最近项目开发到微信支付的模块,经过一周的不懈努力,虽然微信支付流程跑通了,但当时的那些坑是真的烦,为了避免自己以后犯同样的错误,当然还有各位新接触微信支付的同仁们闭坑,所以自我觉得还是有必要写个博客记录记录,顺便提高一下自己的总结能力以及表达能力。
为了更加详细的记录微信支付的整个流程,我还是从微信支付的开发文档说起:
微信支付开发文档地址(我这里使用的微信支付V2版本开发文档):https://pay.weixin.qq.com/wiki/doc/api/index.html
打开链接会进入如下图的页面:
可以看到页面的上部分分别有 普通商户版 服务商版 和 银行服务商版 三个版本的文档 分类
还是要说一下区别:(自我理解的)
普通商户版:就是在微信商户平台使用企业公司资质等信息申请开通的微信普通商户,开通完成后会微信会分配一个商户号(mch_id) 和商户密钥(mch_secret),是在调用支付接口时使用的重要参数。
服务商版:大致和普通商户的申请规则一样,只不过可能会多一些申请资料之类的,经过微信审核通过,则成为微信支付服务商,服务商下面可以邀请多个子商户进入,服务商相当于就是一个管理员可以管理这些子商户,我们把这些子商户称为特约商户,同样微信支付也会给服务商商户分配 服务商商户号和服务商商户密钥 用于 提供其相关支付能力。
银行服务商版:暂时没有接触到,但大致原理都一样,需要银行资质申请,但其中支付产品较少。
在这里我先说一说普通商户版文档里面的APP支付这一支付产品
点击进入看到如下页面:侧边栏的文档说明,术语,支付账户,接口规则,运营规范这些等等都可以自己去看,可以仔细研究研究,因为可能很多支付遇到的细节性问题里面都能找到答案。我这里就不具体一一介绍了,直接进入正题,API列表
点击进入API列表:如下图,可以看到许多微信支付提供的对外接口,如统一下单,申请退款等。具体要查看哪一个文档,点击进入就行了。
在这里要先说一下自己理解的APP支付这一支付产品的支付流程:
1、前端选择商品提交订单,跳转到支付页面选择支付方式;
2、选择微信支付后,请求java后端根据微信app支付文档写好的的统一下单接口;
3、这个统一下单的接口在请求微信那边过后会返给前端一些重要的参数,用于唤醒手机app里的微信支付;
4、在统一下单接口中我们会给微信服务器那边一个回调地址路径,用户微信服务回调我们自己系统的通知接口,用于响应微信服务订单是否支付成功;
在写代码之前要做好微信支付的一些准备:
1、java项目中的配置文件配置好微信支付需要的相关参数,如你 应用 appid,应用 appsecret,商户号 mch_id,商户密钥 mch_secret 回调地址接口路径 2、要将应用 appid 和你的商户 产生绑定关系,这个可以在微信公众平台去绑定 3、要有切实可行的运行环境,保证微信回调可以成功。 4、有些微信API接口还需要商户证书文件,需要去商户平台下载并放置到你的项目中的resources文件夹下
准备工作完成之后就可以安心写代码了,不多说了,直接上代码:
@RequestMapping("/wxAppPay") public ResBean<Object> wxAppPay(@RequestBody ReqWxPayBean reqWxPayBean, HttpServletRequest request){ // 响应数据体 ResBean<Object> resBean = new ResBean<>(); //根据订单号去数据库拿出对应订单的价格(如果是前端传值价格的话可能会有风险,所以后端从数据库获取订单价格,单位为分) Long price = orderTooler.fetchPrice(reqWxPayBean.getPayOutTradeNo(), reqWxPayBean.getSessionId()); //添加app唤醒支付所需支付信息的参数 LinkedHashMap<String, String> params = new LinkedHashMap<>(); params.put("appid", appId);//如果是app支付则为应用appid params.put("attach", reqWxPayBean.getSessionId()); params.put("body", reqWxPayBean.getPayBody()); params.put("mch_id", mchId);//商户的商户号 params.put("nonce_str", Long.toString(System.currentTimeMillis() / 1000));// 随机字符串 params.put("notify_url", couponNoticeUrl); // 支付成功回调地址 params.put("out_trade_no", reqWxPayBean.getPayOutTradeNo()); // 自己系统生成的订单号 params.put("spbill_create_ip", PayUtils.getIp(request)); // ip params.put("total_fee", String.valueOf(price)); // 订单总价,单位为分 params.put("trade_type", String.valueOf(WxPayApi.TradeType.APP)); // 指定支付方式,为app支付 params.put("sign", PayUtils.getSign(params, mchSecret)); // 构造签名 //提交到微信服务器 String post = HttpUtils.post("https://api.mch.weixin.qq.com/pay/unifiedorder", PaymentKit.toXml(params)); //4解析xml格式的数据为map集合 Map<String, String> result = PaymentKit.xmlToMap(post); //5、获得返回状态码 String return_code = result.get("return_code"); String result_code = result.get("result_code"); String err_code = result.get("err_code"); String err_code_des = result.get("err_code_des"); String return_msg = result.get("return_msg"); if (PaymentKit.codeIsOK(return_code)) { if (PaymentKit.codeIsOK(result_code)) { // 成功之后将一下参数返回给前端人员用于唤醒app支付 Map<String, String> parameterMap = new LinkedHashMap<>(); //非空参数值的参数按照参数名ASCII码从小到大排序(字典序) parameterMap.put("appid",appId);//服务商申请的公众号或移动应用appid。 parameterMap.put("mch_id",result.get("mch_id")); // 商户号 parameterMap.put("nonce_str",String.valueOf(result.get("nonce_str"))); // 随机字符串 parameterMap.put("packageValue", "Sign=WXPay"); // 默认为Sign=WXPay parameterMap.put("prepay_id", String.valueOf(result.get("prepay_id")));// 调用微信接口之后生成的预支付id最重要的参数 parameterMap.put("timestamp", Long.toString(System.currentTimeMillis() / 1000));//时间戳 Map<String, String> map = new LinkedHashMap<>(); map.put("appid", appId); map.put("noncestr", String.valueOf(result.get("nonce_str"))); map.put("package", "Sign=WXPay"); map.put("partnerid", result.get("sub_mch_id")); map.put("prepayid", String.valueOf(result.get("prepay_id"))); map.put("timestamp", Long.toString(System.currentTimeMillis() / 1000));//时间戳 parameterMap.put("sign",PayUtils.getSign(map,serverMchSecret)); resBean.setRes_code("0000"); resBean.setRes_desc(result_code); resBean.setRes_content(parameterMap); } else { resBean.setRes_code("9999"); resBean.setRes_desc(err_code_des); resBean.setRes_content(err_code); } } else { resBean.setRes_code("9999"); resBean.setRes_desc(return_msg); resBean.setRes_content(err_code); } return resBean; }
这里仅仅只是微信支付里面普通商户模式的app统一下单一个模块的记录,后面还会继续更新其他微信支付相关的采坑记录。