微信公众号微信支付
步骤一.首先 你要有一个微信公众号 和公众号对应的微信商户号
1) 先申请一个微信公众号
https://kf.qq.com/faq/120911VrYVrA151013MfYvYV.html
官网教程 做微信支付 我们要的是服务号。 订阅号不能做微信支付
2) 申请微信商户号
https://jingyan.baidu.com/article/15622f24d389b3fdfcbea5d1.html
步骤二.配置信息
1)配置信息 设置网页授权域名 和
微信公众平台-->公众号设置-->功能设置
先把这个文件下载过来 放到 你要设置的域名 根目录下 这样才会设置成功 不要带http
2)配置支付目录
商户平台-->产品中心-->开发配置(传送门:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3),且最多可以同时设置5个目录
好 到这一步 配置信息就完成了
步骤三.开发要提供的参数
公众APPID:wx15*********a8 (开发需要的)
开发者密码APPSECEPT : c210***************892d7 (开发需要的)
商户ID:14******42 (开发需要的)
API密钥:5d5************b35b (开发需要的)
微信公众平台-->基本设置
公众APPID 和 APPSECEPT 我们就有了
商户平台-->账号中心-->商户信息
商户ID 也有了 我们还差一个 API密钥
商户平台-->账号中心-->API安全
好了 现在 配置 和参数 我们已经全部都有了 想在开始代码了
步骤四:开发流程:
微信支付 (说白了就是调用官方文档的“统一下单”接口,之后将微信服务器返回的参数做个加工后,返回到前台(JSP页面)) 文档地址
https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1
文档主要讲 请求地址是
https://api.mch.weixin.qq.com/pay/unifiedorder
入参有很多 其中的必填参数有:
1. appid APPID (已有)
2. mch_id 商户ID (已有)
3. nonce_str 随机字符串(可以用UUID生成)
4. sign 签名
5. body 所支付的名称
6. out_trade_no 咱们自己所提供的订单号,需要唯一(可以是时间戳)
7. total_fee 支付金额
8. spbill_create_ip IP地址(获取ip地址)
9. notify_url 回调地址(支付成功后的通知地址)
10. trade_type 支付类型(默认写 咱们是公众号支付此处给“JSAPI”)
11. openid 支付人的微信公众号对应的唯一标识
好 我们先获取 openid
1.获取OpenID 时 要先获取 code 也是去请求微信的地址
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
入参
APPID: 公众号 APPID
redirect_uri: 回调地址 就是访问这个链接后跳转到哪个地址去
response_type:就直接写 code
scope:应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
state:重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
#wechat_redirect:必填不用管
回参
code:返回的 code参数
state:上面传的 state参数
2.获取OpenID 也是请求地址
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
入参
APPID: 公众号 APPID
secret:开发者密码 APPSECEPT
code:上面获取的code 参数
grant_type:直接写 authorization_code
回参
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
OpenID 就是我们要
1. appid APPID (已有)
2. mch_id 商户ID (已有)
3. nonce_str 随机字符串(可以用UUID生成)
4. sign 签名
5. body 所支付的名称
6. out_trade_no 咱们自己所提供的订单号,需要唯一(可以是时间戳)
7. total_fee 支付金额 1 为1分钱
8. spbill_create_ip IP地址(获取ip地址)
9. notify_url 回调地址(支付成功后的通知地址)
10. trade_type 支付类型 咱们是公众号支付此处给“JSAPI”
11. openid 支付人的微信公众号对应的唯一标识 (openid 我们也有了)
现在 我们只差 sing了
用WXPayUtil中的
generateSignature(Map<String, String> data, String key, SignType signType)
方法,data是将除了sign外,其他10个参数放到map中,key是四大配置参数中的API秘钥(paternerKey)
sign_type 也可以通过 WXPayUtil 中的方法获取 主要是表明 加密方式
WXPayConstants.SignType sign_type = WXPayConstants.SignType.MD5;
这些方法都在 wxpay-sdk-0.0.3.jar 这个jar包里
String sign = WXPayUtil.generateSignature(map, AppMchKey, sign_type);
这就生成了 sing 了 把 sing 也 加到 data 里去
调用 mapToXml(Map<String, String> data)
会把 map 转换成 xml
String reqestHtml =WXPayUtil.mapToXml(map);
post请求 https://api.mch.weixin.qq.com/pay/unifiedorder
String xmlStr = sendPost(url,reqestHtml);
成功放回的数据
<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[wx2421b1c4370ec43b]]></appid> <mch_id><![CDATA[10000100]]></mch_id> <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str> <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid> <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id> <trade_type><![CDATA[JSAPI]]></trade_type> </xml>
调用 WXPayUtil.xmlToMap(respondResult) 把 xml 转换成 map
Map<String, String> map = WXPayUtil.xmlToMap(respondResult);
其中 只有 prepay_id 是我们要的
我们要放回个前端的参数是
1. appId:四大参数之一的APPID;
2. timestamp:时间戳(newDate()即可)
3. nonceStr:随机字符串,再次用WXPayUtil中的generateNonceStr()即可;
4. package:就tm是它用到了prepay_id,但是还不是直接取值,还非要固定格式的,值的格式例如:”prepay_id= wx2018…250…9981…666”
5. signType:写MD5就好
6. paySign:签名
if (xmlStr.indexOf("SUCCESS") != -1) { Map<String, String> map = WXPayUtil.xmlToMap(xmlStr); prepay_id = (String) map.get("prepay_id"); } Map<String, String> payMap = new HashMap<String, String>(); payMap.put("appId", appID); payMap.put("timeStamp", getCurrentTimestamp()+""); payMap.put("nonceStr", WXPayUtil.generateNonceStr()); payMap.put("signType", "MD5"); payMap.put("package", "prepay_id=" + prepay_id); String paySign = WXPayUtil.generateSignature(payMap, AppMchKey);// AppMchKey 为api秘钥
payMap.put("paySign", paySign);
return payMap;
这就完成了 代码
后端代码 有一些方法 可以自己写
package com.wizrole.hospital.wechat.controller; import com.github.wxpay.sdk.WXPayUtil; import com.wizrole.hospital.healthCard.util.HttpRequest; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import static com.wizrole.hospital.wechat.service.WechatService.*; import static com.wizrole.hospital.wechat.util.XmlUtil.getCurrentTimestamp; import static com.wizrole.hospital.wechat.util.XmlUtil.getIpAddr; public class Test { @RequestMapping(value = "testPay" , method = {RequestMethod.POST, RequestMethod.GET}) @ResponseBody public Map orders(HttpServletRequest request) {
String code= request.getParameter("code");
//页面获取openId接口 String getopenid_url = https://api.weixin.qq.com/sns/oauth2/access_token; String param="appid="+你appid+"&secret="+你secret+"&code="+code+"&grant_type=authorization_code"; //向微信服务器发送get请求获取openIdStr String openIdStr = HttpRequest.sendGet(getopenid_url, param); JSONObject json = JSONObject.parseObject(openIdStr);//转成Json格式 String openId = json.getString("openid");//获取openId
try { Map<String, String> paraMap = new HashMap<String, String>(); //获取请求ip地址 String ip = getIpAddr(request); String body = request.getParameter("body"); String total_fee = request.getParameter("total_fee");//支付金额 String out_trade_no = String.valueOf(getCurrentTimestamp()); //订单号 paraMap.put("appid",appID);//公众号ID paraMap.put("body", "尧舜商城-订单结算"); paraMap.put("mch_id",AppMchId);//商户号 ID paraMap.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串 paraMap.put("openid", openid);//OpenID paraMap.put("out_trade_no", out_trade_no);//订单号 paraMap.put("spbill_create_ip", ip); paraMap.put("total_fee","1"); paraMap.put("trade_type", "JSAPI"); paraMap.put("notify_url","http://wizrole.natapp1.cc/Wechat/notifyUrl.do");// 此路径是微信服务器调用支付结果通知路径随意写 String sign = WXPayUtil.generateSignature(paraMap, AppMchKey); paraMap.put("sign", sign); String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式 // 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; String xmlStr = HttpRequest.sendPost(unifiedorder_url, xml);//发送post请求"统一下单接口"返回预支付id:prepay_id //以下内容是返回前端页面的json数据 String prepay_id = "";//预支付id if (xmlStr.indexOf("SUCCESS") != -1) { Map<String, String> map = WXPayUtil.xmlToMap(xmlStr); prepay_id = (String) map.get("prepay_id"); } Map<String, String> payMap = new HashMap<String, String>(); payMap.put("appId", appID); payMap.put("timeStamp", getCurrentTimestamp()+""); payMap.put("nonceStr", WXPayUtil.generateNonceStr()); payMap.put("signType", "MD5"); payMap.put("package", "prepay_id=" + prepay_id); String paySign = WXPayUtil.generateSignature(payMap, AppMchKey); payMap.put("paySign", paySign); return payMap; } catch (Exception e) { e.printStackTrace(); } return null; } }
/** * 获取客户端IP地址 * @param request * @return */ public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip; }
package com.wizrole.hospital.healthCard.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; public class HttpRequest { private static final Logger log = LoggerFactory.getLogger(HttpRequest.class); /** * 发送GET请求 * @param url * @param param * @return */ public static String sendGet(String url , String param){ String result = ""; BufferedReader in = null; HttpURLConnection connection = null; try { log.info("发起请求 URL为[" + url + "], 请求方式 [GET], 参数为: " + param); String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString.replaceAll(" ","")); connection = (HttpURLConnection)realUrl.openConnection(); connection.setRequestProperty("accept" , "*/*"); connection.setRequestProperty("connection" , "Keep-Alive"); connection.setRequestProperty("user-agent" , "Mozilla/4.0(compatible; MSIE 6.0; Windows NT5.1:SV1"); connection.connect(); Map<String , List<String>> map = connection.getHeaderFields(); for (String key : map.keySet()){ System.out.println("----------------------------| " + key + "--->" + map.get(key)); } in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = in.readLine()) != null){ result += line; } }catch (Exception e){ log.error("发送GET请求出现异常 : " + e); e.printStackTrace(); }finally { try{ connection.disconnect(); if(in != null){ in.close(); } }catch (Exception e1){ e1.printStackTrace(); } } return result; } /** * 发送POST请求 * @param url * @param param * @return * @throws UnsupportedEncodingException */ public static String sendPost(String url , String param){ BufferedWriter out = null; BufferedReader in = null; HttpURLConnection conn = null; StringBuffer result = new StringBuffer(); try { //param = new String(param.getBytes() , "utf-8"); log.info("发起请求 URL为[" + url + "], 请求方式 [POST], 参数为: " + param); URL realUrl = new URL(url); conn = (HttpURLConnection)realUrl.openConnection(); conn.setRequestProperty("Content-Type","application/json; charset=utf-8"); conn.setRequestProperty("accept" , "*/*"); conn.setRequestProperty("connection" , "Keep-Alive"); conn.setRequestProperty("user-agent" , "Mozilla/4.0(compatible; MSIE 6.0; Windows NT5.1:SV1"); conn.setDoInput(true); conn.setDoOutput(true); out = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(),"UTF-8")); out.write(param); out.flush(); in = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8")); // Map<String , List<String>> map1 = conn.getRequestProperties(); conn.connect(); // for (String key : map1.keySet()){ // System.out.println("----------------------------> " + key + "--->" + map1.get(key)); // } Map<String , List<String>> map = conn.getHeaderFields(); for (String key : map.keySet()){ System.out.println("----------------------------| " + key + "--->" + map.get(key)); } String line = null; while ((line = in.readLine())!=null){ result.append(line); } log.info("出参 : " + result.toString()); }catch (Exception e){ log.error("发送POST请求出现异常 : " + e); e.printStackTrace(); }finally { try{ conn.disconnect(); if(out != null){ out.close(); } if(in != null){ in.close(); } }catch (Exception e1){ e1.printStackTrace(); } } return result.toString(); } }
前端代码
var a = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=appid&redirect_uri=http://wizrole.natapp1.cc/index.html&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect"; location.href = a;
在index.html 页面写 下面这段js
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
var code = getQueryString("code");
//调取微信支付
function pay(){
openid = $("#openId").text();
$.ajax({
url:"/testPay",
type:"POST",
dataType: 'json',
data:{
code:code,
body:"测试",
total_fee:"1"
},
success: function (data) {
if (data.return_msg == "OK") {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": data.appid, //公众号名称,由商户传入
"timeStamp": data.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": data.nonce_str, //随机串
"package": "prepay_id=" + data.prepay_id,
"signType": "MD5", //微信签名方式:
"paySign": data.sign //微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
alert("支付成功,订单受理中");
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
//取消订单
self.cancelOrder(orderdata.order_no, function (status) {
if (status == 1) {
alert("支付已取消");
} else if (status == 2) {
alert("订单取消失败,请联系管理员")
}
});
} else if (res.err_msg == "get_brand_wcpay_request:fail") {
//取消订单
self.cancelOrder(orderdata.order_no, function (status) {
if (status == 1) {
alert("支付失败");
} else if (status == 2) {
alert("订单取消失败,请联系管理员")
}
});
}
}
);
}
}
})
}