微信支付|实现微信支付(Native支付),使用WebSocket进行推送——3.创建支付订单,接收付款结果

实现微信支付(Native支付),使用WebSocket进行推送——3.创建支付订单,接收付款结果 注:本实验使用springboot框架
一、创建订单 1.流程
微信支付|实现微信支付(Native支付),使用WebSocket进行推送——3.创建支付订单,接收付款结果
文章图片

2.创建支付订单所需参数
2. API接口
大家可以用浏览器访问微信支付的API手册( https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1), 查阅具体的调用规范。下面是本次项目所使用到的参数

参数 含义 类型 例子
appid 公众号ID String wxd678efh567hg6787
mch_id 商户号ID String 1230000109
nonce_str 随机字符串 String 5K8264ILTKCH16CQ2502SI8ZNMTM67VS
sign 签名 String C380BEC2BFD727A4B6845133519F3AD6
body 商品订单描述 String 腾讯充值中心-QQ会员充值
out_trade_no 商品订单号 String 20150806125346
total_fee 订单金额,单位为分 int 150
spbill_create_ip 终端IP(用户的客户端IP ) String 123.12.12.123
notify_url 通知地址 String http://140.143.132.225:8000/project-1/doc-110/
trade_type 交易类型 String NATIVE
微信支付平台给我们返回的响应中包含了下面这些参数。
参数 含义 类型 例子
return_code 通信状态码 String SUCCESS
result_code 业务状态码 String SUCCESS
app_id 微信公众账号APPID String wxd678efh567hg6787
mch_id 商户ID String 1230000109
nonce_str 随机字符串 String 5K8264ILTKCH16CQ2502SI8ZNMTM67VS
sign 数字签名 String C380BEC2BFD727A4B6845133519F3AD6
trade_type 交易类型 String NATIVE
prepay_id 支付订单ID String wx201410272009395522657a690389285100
2.1 本实验使用值注入的方式,在application.yaml中配置相关参数
wx: app-id: 企业身份APPID app-secret: 企业身份密钥 mch-id: 企业身份商户号ID key: 企业身份微信支付的密钥 cert-path: 企业身份微信支付的数字证书路径

3、持久层
3.1 sql语句 1.searchAmectByCondition:根据id,用户id,(或状态码)查询罚款单的uuid(做商品编号),amount(罚款金额),prepay_id(支付订单),status(是否支付成功)
2.updatePrepayId:更新支付订单(用于向微信服务器请求生成订单成功后保存订单编号,方便之后主动查询支付状态)
id="searchAmectByCondition" parameterType="HashMap" resultType="HashMap"> SELECT uuid, amount, prepay_id AS prepayId, `status` FROM tb_amect WHERE id = #{amectId} AND user_id = #{userId} AND `status` = #{status} UPDATE tb_amect SET prepay_id = #{prepayId} WHERE id = #{amectId} AND status = 1

3.2 DAO层
public interface TbAmectDao { …… public HashMap searchAmectByCondition(HashMap param); public int updatePrepayId(HashMap param); }

4、业务层代码
public interface AmectService { …… public String createNativeAmectPayOrder(HashMap param); }

public class AmectServiceImpl implements AmectService { @Autowired private MyWXPayConfig myWXPayConfig; …… @Override public String createNativeAmectPayOrder(HashMap param) { int userId = MapUtil.getInt(param, "userId"); int amectId = MapUtil.getInt(param, "amectId"); //根据罚款单ID和用户ID查询罚款单记录 HashMap map = amectDao.searchAmectByCondition(param); if (map != null && map.size() > 0) { //将元转为分 String amount = new BigDecimal(MapUtil.getStr(map, "amount")).multiply(new BigDecimal("100")).intValue() + ""; try { WXPay wxPay = new WXPay(myWXPayConfig); param.clear(); param.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串 param.put("body", "缴纳罚款"); param.put("out_trade_no", MapUtil.getStr(map, "uuid")); param.put("total_fee", amount); param.put("spbill_create_ip", "127.0.0.1"); param.put("notify_url", "http://s1.nsloop.com:35750/emos-api/amect/recieveMessage"); param.put("trade_type", "NATIVE"); String sign = WXPayUtil.generateSignature(param, myWXPayConfig.getKey()); param.put("sign", sign); Map result = wxPay.unifiedOrder(param); //创建支付订单 String prepayId = result.get("prepay_id"); //微信订单ID String codeUrl = result.get("code_url"); //支付连接,需要生成二维码让手机扫码 if (prepayId != null) { param.clear(); param.put("prepayId", prepayId); param.put("amectId", amectId); int rows = amectDao.updatePrepayId(param); if (rows != 1) { throw new EmosException("更新罚款单的支付订单ID失败"); }//把支付订单的URL生成二维码 QrConfig qrConfig = new QrConfig(); qrConfig.setWidth(255); qrConfig.setHeight(255); qrConfig.setMargin(2); String qrCodeBase64 = QrCodeUtil.generateAsBase64(codeUrl, qrConfig, "jpg"); return qrCodeBase64; } else { log.error("创建支付订单失败", result); throw new EmosException("创建支付订单失败"); } } catch (Exception e) { log.error("创建支付订单失败", e); throw new EmosException("创建支付订单失败"); }} else { throw new EmosException("没有找到罚款单"); } } }

5、WEB层代码
@Data @Schema(description = "创建Native支付罚款订单表单") public class CreateNativeAmectPayOrderForm { @NotNull(message = "amectId不能为空") @Min(value = https://www.it610.com/article/1, message ="amectId不能小于1") @Schema(description = "罚款单ID") private Integer amectId; }public class AmectController { …… @PostMapping("/createNativeAmectPayOrder") @Operation(summary = "创建Native支付罚款订单") @SaCheckLogin public R createNativeAmectPayOrder(@Valid @RequestBody CreateNativeAmectPayOrderForm form) { int userId = StpUtil.getLoginIdAsInt(); int amectId = form.getAmectId(); HashMap param = new HashMap() {{ put("amectId", amectId); put("userId", userId); }}; String qrCodeBase64 = amectService.createNativeAmectPayOrder(param); //R是一个统一的返回类,你也可以用hashmap返回 return R.ok().put("qrCodeBase64", qrCodeBase64); } }

6、前端
会有相应的罚款列表,点击缴纳罚款,发送ajax请求,提交罚款订单id,调用createNativeAmectPayOrder接口,返回的二维码使用img标签显示,记为支付码,使用微信扫码支付即可
二、接收付款结果 1.持久层
修改罚款状态
UPDATE tb_amect SET status = #{status} WHERE uuid = #{uuid}

public interface TbAmectDao { …… public int updateStatus(HashMap param); }

2.业务层代码
public interface AmectService { …… public int updateStatus(HashMap param); }

public class AmectServiceImpl implements AmectService { …… @Override public int updateStatus(HashMap param) { int rows = amectDao.updateStatus(param); return rows; } }

3.编写Web层代码
微信平台发送付款结果通知的Request里面是XML格式的数据,所以我们要从中提取出我们需要的关键数据,这部分的文档规范,大家可以查阅微信支付的官方资料( https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8)。 而且这个请求并不是只发送一次,如果商户系统没有接收到请求,微信平台会每隔一小段时间再发送一次付款结果。
参数 含义 类型 示例
return_code 通信状态码 String SUCCESS
result_code 业务状态吗 String SUCCESS
sign 数字签名 String C380BEC2BFD727A4B6845133519F3AD6
out_trade_no 商品订单ID String 1212321211201407033568112322
transaction_id 支付订单ID String 1217752501201407033233368018
total_fee 订单金额 int 150
time_end 支付的时间 String 20141030133525
> 1

返回给微信平台的响应内容也必须是XML格式的,否则微信平台就会认为你没收到付款结果通知。

修改一处代码
修改 WXPayUtil.isSignatureValid(xml, key) -》改为WXPayUtil.isSignatureValid(data,key, WXPayConstants.SignType.HMACSHA256) 添加Map data = https://www.it610.com/article/WXPayUtil.xmlToMap(xml); 原因是WXPayUtil.isSignatureValid(xml, key) 的签名验证使用MD5加密,源码如下, 而我们真实的微信支付使用的是HMACSHA256,所以签名验证怎么都不会对, 所以要改一下签名验证的方法之前我没考虑到这个,以为是只能使用MD5,所以我直接修改了微信支付工具类WXPay, 直接改为用MD5,后来觉得使用MD5不需要密钥很不安全,重新debug后发现原来 是这个原因。现在改回用HMACSHA256,需要修改三处代码,这是其中一处

另外两处:
实现微信支付(Native支付),使用WebSocket进行推送——2.微信支付工具类:中的二、官网给的工具类 的 2.1 WXPay类 核心类
以及:实现微信支付(Native支付),使用WebSocket进行推送 ——4.配置SpringBoot支持WebSocket,推送结果:中的五、主动查询付款结果 的 5.2 业务层代码
//源码 public static boolean isSignatureValid(String xmlStr, String key) throws Exception { Map data = https://www.it610.com/article/xmlToMap(xmlStr); if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) { return false; } String sign = data.get(WXPayConstants.FIELD_SIGN); return generateSignature(data, key).equals(sign); }public static String generateSignature(final Map data, String key) throws Exception { return generateSignature(data, key, SignType.MD5); }

之前创建订单时提交给微信服务端的回调url,notify_url,支付成功后微信服务器会调用该接口,发送回调通知
public class AmectController { @Value("${wx.key}") private String key; …… @Operation(summary = "接收消息通知") @RequestMapping("/receiveMessage") public void receiveMessage(HttpServletRequest request, HttpServletResponse response) throws Exception { request.setCharacterEncoding("utf-8"); Reader reader = request.getReader(); BufferedReader buffer = new BufferedReader(reader); String line = buffer.readLine(); StringBuffer temp = new StringBuffer(); while (line!= null){ temp.append(line); line = buffer.readLine(); } buffer.close(); reader.close(); String xml = temp.toString(); //利用数字证书验证收到的响应内容,避免有人伪造付款结果发送给Web方法。//修改该处代码 WXPayUtil.isSignatureValid(xml, key) -》改为WXPayUtil.isSignatureValid(data,key, WXPayConstants.SignType.HMACSHA256) //添加Map data = https://www.it610.com/article/WXPayUtil.xmlToMap(xml); Map data = https://www.it610.com/article/WXPayUtil.xmlToMap(xml); if(WXPayUtil.isSignatureValid(data,key, WXPayConstants.SignType.HMACSHA256)){ Map map = WXPayUtil.xmlToMap(xml); String resultCode = map.get("result_code"); String returnCode = map.get("return_code"); if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)) { String outTradeNo = map.get("out_trade_no"); //罚款单UUID //更新订单状态 HashMap param = new HashMap() {{ put("status", 2); put("uuid", outTradeNo); }}; int rows = amectService.updateStatus(param); if (rows == 1) { //向前端页面推送付款结果 //根据罚款单ID查询用户ID int userId = amectService.searchUserIdByUUID(outTradeNo); //向用户推送结果 WebSocketService.sendInfo("收款成功", userId + ""); //给微信平台返回响应 response.setCharacterEncoding("utf-8"); response.setContentType("application/xml"); Writer writer = response.getWriter(); BufferedWriter bufferedWriter = new BufferedWriter(writer); bufferedWriter.write(" "); bufferedWriter.close(); writer.close(); } else { log.error("更新订单状态失败"); response.sendError(500, "更新订单状态失败"); } }}else { log.error("数字签名异常"); response.sendError(500, "数字签名异常"); } } }

【微信支付|实现微信支付(Native支付),使用WebSocket进行推送——3.创建支付订单,接收付款结果】微信支付系列其他部分

    推荐阅读