微信小程序支付 官方文档 如下
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3
支付的过程如下
1、小程序内调用登录接口,获取到用户的openid,api参见公共api【小程序登录API】
2、商户server调用支付统一下单,api参见公共api【统一下单API】
3、商户server调用再次签名,api参见公共api【再次签名】
4、商户server接收支付通知,api参见公共api【支付结果通知API】
5、商户server查询支付结果,api参见公共api【查询订单API】
准备工作:
1、在pom.xml 添加 微信支付的sdk
com.github.wxpay
wxpay-sdk
0.0.3
wxpay-sdk 里面已经封装好了 生成签名,map转xml,xml转map 等 工具方法了真的超方便
2.接着创建一个 名为 IWXPayDomain 的接口 主要是 主备域名切换使用 没有必要 可以不改直接用下面的类就行了
public abstract interface IWXPayDomain {
/**
* 上报域名网络状况
* @param domain 域名。 比如:api.mch.weixin.qq.com
* @param elapsedTimeMillis 耗时
* @param ex 网络请求中出现的异常。
* null表示没有异常
* ConnectTimeoutException,表示建立网络连接异常
* UnknownHostException, 表示dns解析异常
*/
abstract void report(final String domain, long elapsedTimeMillis, final Exception ex);
/**
* 获取域名
* @param config 配置
* @return 域名
*/
abstract DomainInfo getDomain(final WXPayConfig config);
static class DomainInfo{
public String domain; //域名
public boolean primaryDomain; //该域名是否为主域名。例如:api.mch.weixin.qq.com为主域名
public DomainInfo(String domain, boolean primaryDomain) {
this.domain = domain;
this.primaryDomain = primaryDomain;
}
@Override
public String toString() {
return "DomainInfo{" +
"domain='" + domain + '\'' +
", primaryDomain=" + primaryDomain +
'}';
}
}
}
3.实现 WXPayConfig 这个类
public class WXMCHPayConfig implements WXPayConfig {
IWXPayDomain getWXPayDomain() {
return new IWXPayDomain(){
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
public DomainInfo getDomain(WXPayConfig config) {
return new IWXPayDomain.DomainInfo("api.mch.weixin.qq.com",true);
}
};
}
public String getAppID() {
return "AppID";//填上微信小程序的appid
}
public String getMchID() {
return "MchID";//商户的MchID
}
public String getKey() {
return "";//商户的key 这个是用来做签名的
}
public InputStream getCertStream() {
return null;
}
@Override
public int getHttpConnectTimeoutMs() {
return 19000;
}
@Override
public int getHttpReadTimeoutMs() {
return 19000;
}
}
进入主题:
微信支付的第一步:
1、小程序内调用登录接口,获取到用户的openid,api参见公共api【小程序登录API】
关于这步就不细说了 小程序 登录完 返回 的openid 可以直接用了 相信很多人都懂了
第二步,
2、商户server调用支付统一下单,api参见公共api【统一下单API】
/*
在这里生成工单 取出工单号
*/
//微信统一下单
Map data = new HashMap<>();
data.put("openid", openid);//用户ID--微信登录返回的openid
data.put("body", body);//商品描述
data.put("out_trade_no", "29a5a115be54410a89c3394aa6b83f55");//32位数的工单号
data.put("total_fee", "1"); //金额 单位: 分--这里一定要用字符串
data.put("spbill_create_ip", Ip);// 用户IP地址
data.put("trade_type", "JSAPI");//支付类型
//异步接收微信支付结果通知的回调地址--不能携带参数 而且外网必须能正常访问
data.put("notify_url", "http://locahost:8888/monitorNotif");
WXPay wxPay = new WXPay( new WXMCHPayConfig() , MD5 ,false );//创建 WXPay 实例调用 统一下单接口
Map resultMap = wxPay.unifiedOrder(data);//统一下单接口
看着上面的代码你可能会很疑惑 为什么 官网文档中统一下单接口 必须传的 "appid 、mchid 、nonce_str "还有最重要的签名sign 都没有传就可以下单了。
其实 你跟着 unifiedOrder 这个方法debug 下去 你会发现 WXPay 这个工具类 都自动帮你添加了appid 、mchid、 nonce_str 还有生成sign签名。
一路debug下去 找到 一下方法 fillRequestData 这里可以看到 剩下的信息都添加了
签名 sign 如WXPayUtil.generateSignature(reqData, this.config.getKey(), this.signType) 生成
public Map fillRequestData(Map reqData) throws Exception {
reqData.put("appid", this.config.getAppID());
reqData.put("mch_id", this.config.getMchID());
reqData.put("nonce_str", WXPayUtil.generateNonceStr());
if (SignType.MD5.equals(this.signType)) {
reqData.put("sign_type", "MD5");
} else if (SignType.HMACSHA256.equals(this.signType)) {
reqData.put("sign_type", "HMAC-SHA256");
}
reqData.put("sign", WXPayUtil.generateSignature(reqData, this.config.getKey(), this.signType));
return reqData;
}
第三步
统一下单成功后将会返回一个 prepay_id 用于 再次签名
Map res = new HashMap<>();
res.put("package", "prepay_id="+prepay_id);
res.put("signType", "MD5");
res.put("nonceStr", WXPayUtil.generateNonceStr());//获取32位随机字符串
res.put("appId", AppID);//小程序的appid
res.put("timeStamp", System.currentTimeMillis() / 1000 +"");//到秒的时间戳
String paySign = WXPayUtil.generateSignature(res,WXMCHPayConfig.Key);//获取签名
res.put("paySign", paySign); //签名
将 集合 res 回传到小程序端
小程序调用 以下接口 调起支付页面
wx.requestPayment(
{
'appId':'',
'timeStamp': '',
'nonceStr': '',
'package': '',
'signType': 'MD5',
'paySign': '',
'success':function(res){},
'fail':function(res){},
'complete':function(res){}
})
开发工具 看到的是二维码支付页面
当支付成功时 微信就会根据 统一下单时传的 通知接口("notify_url", "http://locahost:8888/monitorNotif")返回 支付账单相关信息。
第四步,微信支付通知处理
4、商户server接收支付通知,api参见公共api【支付结果通知API】
//监听 微信支付通知
@RequestMapping("/monitorNotify")
@ResponseBody
public String monitorNotify(HttpServletRequest request, HttpServletResponse response ) throws Exception{
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
String result = new String(outSteam.toByteArray(), "utf-8");
System.out.println(result);//获取到支付结果 xml
/* 以下来自官网的例子
1
*/
// 将 xml转换为map 方便操作数据
Map resultMap = WXPayUtil.xmlToMap(result);
//通过支付结果里面的数据生成签名 对比 支付结果中的签名是否一致
String signTest = WXPayUtil.generateSignature(resultMap, WXMCHPayConfig.Key);//生成签名
// 签名验证
if(!signTest.equals(resultMap.get("sign"))){
Map res = new HashMap<>();
res.put("return_code","FAIL"); //SUCCESS/FAIL SUCCESS表示商户接收通知成功并校验成功
res.put("return_msg","签名失败");
String resStr = WXPayUtil.mapToXml(res); //以xml 的形式告知微信
return resStr;
}
/*假如签名验证成功 最好对比一下 金额是否一样 是否重复 接收通知 验证*/
验证成功 ,就修改工单状态 生成支付流水 等等操作
// 以上验证用过后 应答微信通知
Map resXml = new HashMap<>();
resXml.put("return_code","SUCCESS"); //SUCCESS/FAIL SUCCESS表示商户接收通知成功并校验成功
resXml.put("return_msg","OK");
String resStr = WXPayUtil.mapToXml(resXml);
return resStr;
}
第五步:查询订单
5、商户server查询支付结果,api参见公共api【查询订单API】
/*
* 查询订单
* */
@GetMapping("/selectOrder")
@ResponseBody
public Result selectOrder(String orderid) throws Exception {
WXPay wxPay = new WXPay( new WXMCHPayConfig() , MD5 ,false );
Map data = new HashMap<>();
data.put("out_trade_no", orderid);//生成的工单ID
Map res = wxPay.orderQuery(data);//微信查询订单接口
return res ;
}
其实在第四步 处理的通知的时候可以 通过查询订单的状态 验证该工单是否支付成功
好了 到这里结束。。。。