微信支付(APP支付)-服务端开发(一) - 大西瓜3721

时间:2024-02-23 08:12:52

微信支付(APP支付)-服务端开发(一)

微信支付,首先需要注册一个商户平台公众账号,(网址:https://pay.weixin.qq.com/index.php/home/d_login)

目前微信支付的接入方式有四种方式:公众号支付,APP支付,扫描支付,刷卡支付。本文中我将详细讲解一下APP支付。

微信支付→APP支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_1

主要流程如下:(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_3)

商户系统和微信支付系统主要交互说明:

步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。

步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。

步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay

步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明

步骤5:商户后台接收支付通知。api参见【支付结果通知API

步骤6:商户后台查询支付结果。,api参见【查询订单API

其中与后台相关的主要为步骤2和步骤6。虽然都有官方文档的说明,但是开发过程中,难免还会遇到不少的坑,比如签名问题,大小写问题等。

我首先讲解一下步骤2(统一下单API:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1):

统一下单接口地址:URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder,请求参数,返回参数等文档中都有详细描述,就不再一一说明,直接上代码:

@RequestMapping(value = "/createOrder", method = {RequestMethod.GET, RequestMethod.POST})
public String createOrder(Map<String, String> model) throws Exception{
log.debug("WeChatPayController.createOrder,parameter[{trade_no,subject,total_fee},{"
+ request.getParameter("trade_no")
+ ","
+ request.getParameter("subject")
+ ","
+ request.getParameter("total_fee") + "}]");

WeChatRsp response = new WeChatRsp();
WeChat weChat = new WeChat();

String orderNo = request.getParameter("trade_no"); //订单号
String money = request.getParameter("total_fee"); //订单金额
String body = request.getParameter("subject"); //商品描述根据情况修改

//金额转化为分为单位
float sessionmoney = Float.parseFloat(money);
String finalmoney = String.format("%.2f", sessionmoney);
finalmoney = finalmoney.replace(".", "");

//商户相关资料 
String appid = CommonUtils.getPropertiesValue("config", "appid");
String appsecret = CommonUtils.getPropertiesValue("config", "appsecret");
String partner = CommonUtils.getPropertiesValue("config", "partnerId");
String partnerkey = CommonUtils.getPropertiesValue("config", "partnerkey");

//商户号
String mch_id = partner;
//随机数 
Random random = new Random();
String nonce_str = cn.emagsoftware.utils.MD5Util.getMD5String(String.valueOf(random.nextInt(10000)));

//商户订单号
String out_trade_no = orderNo;
int intMoney = Integer.parseInt(finalmoney);

//总金额以分为单位,不带小数点
int total_fee = intMoney;
//订单生成的机器 IP
String spbill_create_ip = request.getRemoteAddr();
System.out.println("订单生成的机器IP:"+spbill_create_ip);

//这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。
String notify_url = CommonUtils.getPropertiesValue("config", "weChat_notify_url");
//交易类型
String trade_type = CommonUtils.getPropertiesValue("config", "trade_type");

SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", appid); 
packageParams.put("mch_id", mch_id); 
packageParams.put("nonce_str", nonce_str); 
packageParams.put("body", body); 
packageParams.put("out_trade_no", out_trade_no); 

packageParams.put("total_fee", total_fee+""); 
packageParams.put("spbill_create_ip", spbill_create_ip); 
packageParams.put("notify_url", notify_url); 

packageParams.put("trade_type", trade_type);

HttpServletResponse httpServletResponse = null;
RequestHandler reqHandler = new RequestHandler(request, httpServletResponse);
reqHandler.init(appid, appsecret, partnerkey);

String sign = reqHandler.createSign(packageParams);
String xml="<xml>"+
"<appid>"+appid+"</appid>"+
"<body><![CDATA["+body+"]]></body>"+
"<mch_id>"+mch_id+"</mch_id>"+
"<nonce_str>"+nonce_str+"</nonce_str>"+
"<notify_url>"+notify_url+"</notify_url>"+
"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
"<sign>"+sign+"</sign>"+
"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
"<total_fee>"+total_fee+"</total_fee>"+
"<trade_type>"+trade_type+"</trade_type>"+
"</xml>";
log.debug("xml = "+xml);
String createOrderURL = WECHAT_CREATE_ORDER_URL;
String prepay_id="";
//获取预支付交易号
try {
prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml, "prepay_id");
} catch (Exception e1) {
e1.printStackTrace();
response.setResultCode(Constant.ERROR_CODE_9998);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.ERROR_CODE_9998));
}

if (prepay_id!=null& !prepay_id.equals("")) {
response.setResultCode(Constant.SUCCESS_CODE);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.SUCCESS_CODE));
weChat.setAppid(appid);
weChat.setPrepayid(prepay_id);
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonce_string = cn.emagsoftware.utils.MD5Util.getMD5String(timestamp);
weChat.setTimestamp(timestamp);
weChat.setNoncestr(nonce_string);
weChat.setPackages("Sign=WXPay");
weChat.setPartnerid(partner);

//二次签名
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
finalpackage.put("appid", appid); 
finalpackage.put("timestamp", timestamp); 
finalpackage.put("noncestr", nonce_string); 
finalpackage.put("package", "Sign=WXPay"); 
finalpackage.put("partnerid", mch_id);
finalpackage.put("prepayid", prepay_id);
String finalsign = reqHandler.createSign(finalpackage);
weChat.setSign(finalsign);
response.setData(weChat);
}else {
log.debug("预支付交易号生成失败。。。");
response.setResultCode(Constant.ERROR_CODE_9998);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.ERROR_CODE_9998));
}

try{
model.put(Constant.RETURN_MESSAGE, JsonUtils.getJSONString(response));
log.debug("WeChatPayController.createOrder.response=="+JsonUtils.getJSONString(response));
} catch (Exception ex) {
log.error("VersionController.getVersion", ex);
}
return RET_JSP;
}

APP端发起支付请求之后,会发送订单号给服务端程序,服务端拿到订单号之后,根据统一下单地址,发送xml文件给微信,微信接受处理后,如果返回成功,则同时会返回一个预支付交易会话标识(prepay_id),拿到这个标识之后,服务端进行二次签名,生成sign,签名参数如下:

//二次签名
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
finalpackage.put("appid", appid);   //appid
finalpackage.put("timestamp", timestamp);  //时间戳  十位  
finalpackage.put("noncestr", nonce_string); //随机字符串
finalpackage.put("package", "Sign=WXPay");   //固定值
finalpackage.put("partnerid", mch_id);  //商户id(微信商户平台获取)
finalpackage.put("prepayid", prepay_id);  //第一次请求微信,成功后,返回的参数
String finalsign = reqHandler.createSign(finalpackage);  //生成签名
weChat.setSign(finalsign);  //生成签名后,放入对象中

response.setData(weChat);  //参数返回给前端

第一次发送统一下单的时候,官方文档中都有说明,哪些参数是必须的,哪些参数不是必须的,以及参数类型,此处不再一一解释,不理解的可以参考官方文档。重点说一下二次签名:

二次签名的时候涉及到的参数有appid,timestamp,noncestr,partnerid,prepayid,package,这6个参数全部是小写(大小写不同,MD5加密结果不一致,二次签名官方没有文档,比较坑)

APP端获取到服务端传递的参数后,调起支付接口(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2),这一点是APP端的操作,不在详解。

APP端支付成功后,会再次发送请求到服务器端,确定订单是否付款成功,服务端需要再次向微信发起请求,查询订单,具体操作查看下一章节。

注释:开发中遇到任务问题(服务端),欢迎咨询,我也是初次开发微信支付,希望可以帮到你。