开发前准备
- 注册微信商户号:https://pay.weixin.qq.com/index.php
- 注册微信小程序:https://mp.weixin.qq.com/
- 在小程序中配置商户号,并且开通微信支付功能,如下图:
首先请仔细阅读 微信支付官方文档
根据文档简述其开发流程主要为:
1、小程序内调用登录接口,获取到用户的openid,api参见公共api【小程序登录API】 2、商户server调用支付统一下单,api参见公共api【统一下单API】
3、商户server调用再次签名,api参见公共api【再次签名】
4、商户server接收支付通知,api参见公共api【支付结果通知API】
5、商户server查询支付结果,api参见公共api【查询订单API】
获取微信用户的openid
小程序端:
wx.login({
success: function (r) {
var params = {
userId: userid, //微信用户id,用于绑定生成的openid, 可根据业务逻辑灵活处理
code: r.code
}
api.getWxOpenid(params) //封装等微信小程序api,用于请求后台接口
.then((res) => {
if (res.code == 101) {
//获取微信用户的 openid 并存入缓存 --可根据业务逻辑灵活处理
wx.setStorageSync('WXOPENID', res.data.WXOPENID);
} else {
wx.showToast({
title: res.msg,
icon: 'none',
duration: 1000
})
}
})
}
})
java后台:
APPID: 小程序的appid SECRET: 小程序的Secret
String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+APPID+"&secret="+SECRET+"&js_code="+code+"&grant_type=authorization_code";
String result = HttpUtils.sendGet(url);
return JSONObject.parseObject(result, PageData.class);
生成统一订单
/**
* 获取微信小程序订单支付参数
* @param paramsPd 统一下单接口部分参数
* {
* 必需参数 【body: 商品简单描述, openid: 微信用户ID, outTradeNo: 商户自己的订单号码, ip: IP地 址, totalFee: 订单总金额,单位为分】
* 可选参数 【 detail: 商品详情, attach: 附加数据】
* }
* @param partner 商户号
* @param partnerkey 商户号密钥
* @return
*/
public static PageData getWxPayParameters(PageData paramsPd, String partner, String partnerkey) {
PageData result = new PageData();
Map<String, String> params = new HashMap<>();
//必需参数
params.put("appid", APPID); //小程序ID
params.put("mch_id", partner); //商户号 ●注意
params.put("body", paramsPd.getString("body")); //body 商品简单描述
params.put("nonce_str", Sha1Util.getNonceStr()); //随机字符串
params.put("notify_url", UNIFY_NOTIFY_URL); //异步通知的地址,支付回调接口地址
params.put("openid", paramsPd.getString("openid")); //openid wx.login({})返回的
params.put("out_trade_no", paramsPd.getString("outTradeNo")); //商户自己的订单号码
params.put("spbill_create_ip", paramsPd.getString("ip")); //IP地址
params.put("total_fee", paramsPd.getString("totalFee")); //支付多少钱
params.put("trade_type", "JSAPI"); //交易类型 JSAPI
//可选参数
if(StringUtils.isNotBlank(paramsPd.getString("detail"))) {
params.put("detail", paramsPd.getString("detail")); //商品详情
}
if(StringUtils.isNotBlank(paramsPd.getString("attach"))) {
params.put("attach", paramsPd.getString("attach")); //附加数据
}
//获取签名
String sign = generateSign(params, partnerkey);
params.put("sign", sign); //签名
String xml = generateXmlStr(params); //获取接口满足格式要求的接口参数
//调用统一下单接口获取 预支付ID: prepay_id
String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String prepay_id = "";
try {
Map<String, Object> map = wxHttpPost(createOrderURL, xml);
String resultCode = (String) map.get("result_code");
if("SUCCESS".equals(resultCode)) {
prepay_id = (String) map.get("prepay_id");
if(StringUtils.isBlank(prepay_id)) {
result.put("code", false);
result.put("msg", "预支付交易会话标识获取异常");
return result;
}
}else {
String errCode = (String) map.get("err_code");
String msg = buildUPIErrMsg(errCode); //构建统一支付接口异常消息描述
result.put("code", false);
result.put("msg", msg);
return result;
}
} catch (Exception e) {
e.printStackTrace();
result.put("code", false);
result.put("msg", "微信支付出错啦!!!");
return result;
}
Map<String, String> signParams = new HashMap<>();
String timeStamp = Sha1Util.getTimeStamp(); //时间戳
String nonceStr = Sha1Util.getNonceStr(); //随机串
signParams.put("appId", APPID);
signParams.put("nonceStr", nonceStr);
signParams.put("package", "prepay_id="+prepay_id);
signParams.put("timeStamp", timeStamp);
signParams.put("signType", "MD5");
String paySign = generateSign(signParams, partnerkey); //获取支付数据签名
//构建需要返回的支付参数
PageData data = new PageData();
data.put("timeStamp", timeStamp);
data.put("nonceStr", nonceStr);
data.put("package", "prepay_id="+prepay_id);
data.put("signType", "MD5");
data.put("paySign", paySign);
result.put("code", true);
result.put("data", data);
result.put("msg", "支付参数获取成功");
return result;
}
/**
* 生成签名
* @param params 参数
* @param partnerkey 商户号密钥
* @return
*/
public static String generateSign(Map<String, String> params, String partnerkey) {
String result = "";
try {
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(params.entrySet());
// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(o2.getKey());
}
});
// 构造签名键值对的格式
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, String> item : infoIds) {
String key = item.getKey();
if (!(null == key || "".equals(key) || "sign".equals(key) || "key".equals(key))) {
String val = item.getValue();
if (!(null == val || "".equals(val))) {
sb.append(key + "=" + val + "&");
}
}
}
sb.append("key=" + partnerkey);
//进行MD5加密
result = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
} catch (Exception e) {
return null;
}
return result;
}
微信小程序调起支付: 微信API描述
wx.requestPayment({
'nonceStr': params.nonceStr,
'package': params.package,
'paySign': params.paySign,
'signType': params.signType,
'timeStamp': params.timeStamp,
'success': function (res) {
支付成功后处理逻辑
},
'fail': function (res) { },
'complete': function (res) { }
})
支付成功后的回调
在统一下单输入了回调的URL,在成功后,微信会回调你多次,一直到成功为止。请参考自身业务进行编写代码。【回调地址在同一支付接口中设置】
/**
* 微信支付结果通知回调接口
*
* @return
*/
@RequestMapping(value = "/wxPayCallBack")
@ResponseBody
public String eNotifys(HttpServletRequest request, HttpServletResponse response) {
logger.info("==================小程序支付结果通知回调=========================");
BufferedReader br;
try {
br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
Document doc = DocumentHelper.parseText(sb.toString()); // 将字符串转为XML
Element root = doc.getRootElement(); // 获取根节点
String resultCode = root.elementTextTrim("result_code"); //支付状态响应标识 SUCCESS/FAIL
String outTradeNo = root.elementTextTrim("out_trade_no"); //商户订单号
String transactionId = root.elementTextTrim("transaction_id"); //微信支付订单号
String totalFee = root.elementTextTrim("total_fee"); //订单金额
String cashFee = root.elementTextTrim("cash_fee"); //现金支付金额
String attach = root.elementTextTrim("attach"); //商家数据包, 原样返回
//SUCCESS/FAIL
// 根据微信的result_code判断微信端是否支付成功
if ("SUCCESS".equals(resultCode)) {
String xml = WxAppletPayUtil.successXML("SUCCESS", "");
response.getWriter().write(xml);
logger.error("商户订单号: 【" + outTradeNo + "】支付成功");
//业务逻辑处理
}else if ("FAIL".equals(resultCode)) {
String errCode = root.elementTextTrim("err_code");
String errCodeDes = root.elementTextTrim("err_code_des");
logger.error("商户订单号: 【" + outTradeNo + "】支付失败, "+"描述:【errCode:"+errCode+", errCodeDes:"+errCodeDes+"】");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}