集成JPay的微信支付

时间 2021/3/19 14:02:13 加载中...

前言

项目用到微信支付,参照了 JPay 的 Spring Boot Demo 程序。
项目地址:https://gitee.com/javen205/IJPay/tree/master/IJPay-Demo-SpringBoot
但最好还是理解下微信支付的流程。
可以看一下官方的扫码支付:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5&index=3
然后在看 Demo 程序中是如何实现扫码程序的。

我们将 Demo 程序中的部分代码移植到自己的程序中。

自己项目为非前后台,技术栈 Spring Boot + thymeleaf。

移植步骤

pom.xml 文件添加依赖

添加了 微信支付 和 支付宝支付 两种。

  1. <properties>
  2. <ijapy.version>2.7.3</ijapy.version>
  3. </properties>
  4. <dependencies>
  5. <dependency>
  6. <groupId>com.github.javen205</groupId>
  7. <artifactId>IJPay-WxPay</artifactId>
  8. <version>${ijapy.version}</version>
  9. </dependency>
  10. <dependency>
  11. <groupId>com.github.javen205</groupId>
  12. <artifactId>IJPay-AliPay</artifactId>
  13. <version>${ijapy.version}</version>
  14. </dependency>
  15. </dependencies>

添加配置

在 application.yml 中添加支付相关的配置

  1. pay:
  2. weixin:
  3. appId: 应用编号
  4. appSecret: appSecret appId 对应的接口密码,微信公众号授权获取用户 openId 时使用
  5. mchId: 微信支付商户号
  6. partnerKey: API 密钥
  7. certPath: apiclient_cert.p1 证书路径,在微信商户后台下载
  8. domain: 外网访问项目的域名,支付通知中会使用
  9. ali:
  10. appId: 应用编号
  11. privateKey: 应用私钥
  12. publicKey: 支付宝公钥,通过应用公钥上传到支付宝开放平台换取支付宝公钥(如果是证书模式,公钥与私钥在CSR目录)。
  13. appCertPath: 应用公钥证书
  14. aliPayCertPath: 支付宝公钥证书
  15. aliPayRootCertPath: 支付宝根证书
  16. serverUrl: 支付宝支付网关,沙箱环境时设置为 http://openapi.ev.com/gateway.do 使用正式环境时设置为 http://openapi.com/gateway.do
  17. domain: 外网访问项目的域名,支付通知中会使用

在配置包下(我的是 config)新增一个包专门放置和支付相关的配置,比如包名为 pay
新增微信配置类和支付宝配置类。

  1. @Configuration
  2. @ConfigurationProperties(prefix = "pay.ali")
  3. public class AliConfig {
  4. private String appId;
  5. private String privateKey;
  6. private String publicKey;
  7. private String appCertPath;
  8. private String aliPayCertPath;
  9. private String aliPayRootCertPath;
  10. private String serverUrl;
  11. private String domain;
  12. //getter setter 略
  13. }
  14. @Configuration
  15. @ConfigurationProperties(prefix = "pay.weixin")
  16. public class WxConfig {
  17. private String appId;
  18. private String appSecret;
  19. private String mchId;
  20. private String partnerKey;
  21. private String certPath;
  22. private String domain;
  23. //getter setter 略
  24. }

添加接口

编写微信的控制器

此控制器包含一个【花钱购买角色】的业务逻辑。
前端调用 /wxPay/setRole 的接口,后台会创建一个订单记录(自己系统记录的订单),同时根据角色价格生成一个二维码,二维码会以 BASE64 的形式返回给前端。用户扫码支付后,微信后台就会调用回调接口(生成二维码的时候指定的),我们在回调接口中补充上自己的业务逻辑也就是赋值角色即可。

如果有新的逻辑,修改 setRole 方法,和 UpdateOrder 方法即可。

  1. package com.sqber.personMgr.ui.controller.pay;
  2. import com.google.zxing.BarcodeFormat;
  3. import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
  4. import com.ijpay.core.enums.SignType;
  5. import com.ijpay.core.enums.TradeType;
  6. import com.ijpay.core.kit.HttpKit;
  7. import com.ijpay.core.kit.IpKit;
  8. import com.ijpay.core.kit.QrCodeKit;
  9. import com.ijpay.core.kit.WxPayKit;
  10. import com.ijpay.wxpay.WxPayApi;
  11. import com.ijpay.wxpay.WxPayApiConfigKit;
  12. import com.ijpay.wxpay.model.OrderQueryModel;
  13. import com.ijpay.wxpay.model.UnifiedOrderModel;
  14. import com.sqber.personMgr.base.BaseResponse;
  15. import com.sqber.personMgr.base.SessionHelper;
  16. import com.sqber.personMgr.bll.IPayOrderService;
  17. import com.sqber.personMgr.entity.PayOrder;
  18. import com.sqber.personMgr.ui.config.pay.WxConfig;
  19. import org.slf4j.Logger;
  20. import org.slf4j.LoggerFactory;
  21. import org.springframework.beans.factory.annotation.Autowired;
  22. import org.springframework.util.StringUtils;
  23. import org.springframework.web.bind.annotation.*;
  24. import com.ijpay.wxpay.WxPayApiConfig;
  25. import javax.servlet.http.HttpServletRequest;
  26. import javax.servlet.http.HttpServletResponse;
  27. import java.io.ByteArrayOutputStream;
  28. import java.io.IOException;
  29. import java.util.Base64;
  30. import java.util.HashMap;
  31. import java.util.Map;
  32. @RestController
  33. @RequestMapping("/wxPay")
  34. public class WxController {
  35. private final Logger log = LoggerFactory.getLogger(this.getClass());
  36. @Autowired
  37. WxConfig wxConfig;
  38. @Autowired
  39. IPayOrderService payOrderService;
  40. public void getApiConfig() {
  41. WxPayApiConfig apiConfig;
  42. try {
  43. apiConfig = WxPayApiConfigKit.getApiConfig(wxConfig.getAppId());
  44. } catch (Exception e) {
  45. apiConfig = WxPayApiConfig.builder()
  46. .appId(wxConfig.getAppId())
  47. .mchId(wxConfig.getMchId())
  48. .partnerKey(wxConfig.getPartnerKey())
  49. .certPath(wxConfig.getCertPath())
  50. .domain(wxConfig.getDomain())
  51. .build();
  52. WxPayApiConfigKit.setThreadLocalWxPayApiConfig(apiConfig);
  53. }
  54. }
  55. @GetMapping("/test")
  56. public String test(){
  57. return "test";
  58. }
  59. /**
  60. * 扫码支付模式二
  61. */
  62. @PostMapping("/scanCode2")
  63. public BaseResponse scanCode2(HttpServletRequest request, HttpServletResponse response, String totalFee) {
  64. if (StringUtils.isEmpty(totalFee)) {
  65. return BaseResponse.fail("支付金额不能为空");
  66. }
  67. String ip = IpKit.getRealIp(request);
  68. if (StringUtils.isEmpty(ip)) {
  69. ip = "127.0.0.1";
  70. }
  71. return createCode(WxPayKit.generateStr(), ip,"","","", 1);
  72. }
  73. private BaseResponse createCode(String orderNo, String ip, String body, String detail, String attach, int totalFee){
  74. getApiConfig();
  75. WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
  76. String notifyUrl = wxPayApiConfig.getDomain().concat("/wxPay/payNotify");
  77. Map<String, String> params = UnifiedOrderModel
  78. .builder()
  79. .appid(wxPayApiConfig.getAppId())
  80. .mch_id(wxPayApiConfig.getMchId())
  81. .nonce_str(WxPayKit.generateStr())
  82. .body(body)
  83. .attach(attach)
  84. .detail(detail)
  85. .out_trade_no(orderNo)
  86. .total_fee(String.valueOf(totalFee))
  87. .spbill_create_ip(ip)
  88. .notify_url(notifyUrl)
  89. .trade_type(TradeType.NATIVE.getTradeType())
  90. .build()
  91. .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
  92. log.info("parmas:" + params);
  93. log.info("notifyUrl:" + notifyUrl);
  94. String xmlResult = WxPayApi.pushOrder(false, params);
  95. log.info("统一下单:" + xmlResult);
  96. Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
  97. String returnCode = result.get("return_code");
  98. String returnMsg = result.get("return_msg");
  99. System.out.println(returnMsg);
  100. if (!WxPayKit.codeIsOk(returnCode)) {
  101. return BaseResponse.fail("error:" + returnMsg);
  102. }
  103. String resultCode = result.get("result_code");
  104. if (!WxPayKit.codeIsOk(resultCode)) {
  105. return BaseResponse.fail("error:" + returnMsg);
  106. }
  107. //生成预付订单success
  108. String qrCodeUrl = result.get("code_url");
  109. // 生成二维码,并返回 BASE64 格式的图片
  110. try {
  111. ByteArrayOutputStream output = new ByteArrayOutputStream();
  112. QrCodeKit.encodeOutPutSteam(output, qrCodeUrl, BarcodeFormat.QR_CODE, 3, ErrorCorrectionLevel.H, "png", 200, 200);
  113. byte[] byteArr = output.toByteArray();
  114. String str = Base64.getEncoder().encodeToString(byteArr);
  115. String imgStr = new StringBuilder().append("data:image/png;base64,").append(str).toString();
  116. BaseResponse baseResponse = new BaseResponse();
  117. baseResponse.setData(imgStr);
  118. return baseResponse;
  119. } catch (IOException e) {
  120. log.error("error:{}", e);
  121. e.printStackTrace();
  122. return BaseResponse.fail("服务器错误");
  123. }
  124. }
  125. @GetMapping("/setRole")
  126. // 创建业务订单
  127. private BaseResponse createOrder(HttpServletRequest request, HttpServletResponse response){
  128. // 从数据库中获取角色的价格
  129. PayOrder payOrder = new PayOrder();
  130. payOrder.setOrderNo(WxPayKit.generateStr());
  131. payOrder.setAmount(1); // 1分 1元 = 10角 = 100分
  132. payOrder.setUserId(SessionHelper.GetLoginUserID());
  133. payOrder.setOrderState(PayOrder.NOT_PAYED);
  134. payOrder.setProduct("获取管理员角色");
  135. payOrder.setContent("获取管理员角色");
  136. payOrderService.insert(payOrder);
  137. String ip = IpKit.getRealIp(request);
  138. if (StringUtils.isEmpty(ip)) {
  139. ip = "127.0.0.1";
  140. }
  141. return createCode(payOrder.getOrderNo(),ip, payOrder.getContent(), "详情", "附加", payOrder.getAmount());
  142. }
  143. /**
  144. * 异步通知
  145. */
  146. @RequestMapping(value = "/payNotify", method = {RequestMethod.POST, RequestMethod.GET})
  147. @ResponseBody
  148. public String payNotify(HttpServletRequest request) {
  149. log.info("异步通知来了");
  150. String xmlMsg = HttpKit.readData(request);
  151. log.info("支付通知=" + xmlMsg);
  152. Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
  153. String returnCode = params.get("return_code");
  154. String resultCode = params.get("result_code");
  155. String transactionId = params.get("transaction_id"); // 微信订单号
  156. String out_trade_no = params.get("out_trade_no"); // 商户订单号
  157. String attach = params.get("attach"); // 附加数据
  158. log.info("微信订单号(交易单号):" + transactionId);
  159. log.info("订单号(商户单号):" + out_trade_no);
  160. // 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
  161. // 注意此处签名方式需与统一下单的签名类型一致
  162. getApiConfig();
  163. if (WxPayKit.verifyNotify(params, WxPayApiConfigKit.getWxPayApiConfig().getPartnerKey(), SignType.HMACSHA256)) {
  164. if (WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(resultCode)) {
  165. // 更新订单信息
  166. // 发送通知等
  167. updateOrder(out_trade_no);
  168. Map<String, String> xml = new HashMap<String, String>(2);
  169. xml.put("return_code", "SUCCESS");
  170. xml.put("return_msg", "OK");
  171. return WxPayKit.toXml(xml);
  172. }
  173. }
  174. return null;
  175. }
  176. // 更新订单 && 发送商品
  177. private void updateOrder(String orderNo){
  178. payOrderService.completeOrder(orderNo);
  179. PayOrder payOrder = payOrderService.getByOrderNo(orderNo);
  180. log.info("设置指定角色");
  181. // 赋予用户特定角色
  182. // userRole.updateRole(userId,payOrder.getProduct());
  183. }
  184. /**
  185. * 查询订单
  186. *
  187. * @param transactionId
  188. * @param outTradeNo
  189. * @return
  190. */
  191. @RequestMapping(value = "/queryOrder", method = {RequestMethod.POST, RequestMethod.GET})
  192. @ResponseBody
  193. public String queryOrder(@RequestParam(value = "transactionId", required = false) String transactionId, @RequestParam(value = "outTradeNo", required = false) String outTradeNo) {
  194. try {
  195. getApiConfig();
  196. WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
  197. Map<String, String> params = OrderQueryModel.builder()
  198. .appid(wxPayApiConfig.getAppId())
  199. .mch_id(wxPayApiConfig.getMchId())
  200. .transaction_id(transactionId)
  201. .out_trade_no(outTradeNo)
  202. .nonce_str(WxPayKit.generateStr())
  203. .build()
  204. .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
  205. log.info("请求参数:{}", WxPayKit.toXml(params));
  206. String query = WxPayApi.orderQuery(params);
  207. log.info("查询结果: {}", query);
  208. // if(成功了){
  209. // updateOrder();
  210. // }
  211. return query;
  212. } catch (Exception e) {
  213. e.printStackTrace();
  214. return "系统错误";
  215. }
  216. }
  217. }

支付宝接口控制器暂无。

业务订单表 SQL 语句

  1. CREATE TABLE `payOrder` (
  2. `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  3. `orderNo` varchar(100) NOT NULL DEFAULT '' COMMENT '订单号',
  4. `amount` int NOT NULL DEFAULT 0 COMMENT '金额(单位:分)',
  5. `userId` int NOT NULL DEFAULT 0 COMMENT '用户id',
  6. `orderState` int NOT NULL DEFAULT 0 COMMENT '订单状态:0-待支付 1-已支付 2-支付失败',
  7. `product` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '商品(比如按角色付费,则可以存角色id)',
  8. `content` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '商品描述',
  9. `createUser` varchar(64) NULL DEFAULT '' COMMENT '创建人',
  10. `createTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  11. `modifyUser` varchar(64) NULL DEFAULT '' COMMENT '更新人',
  12. `modifyTime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  13. `status` tinyint DEFAULT '1' COMMENT '记录状态:0-删除 1-正常',
  14. PRIMARY KEY (`id`)
  15. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='支付订单表';

Demo地址

https://github.com/shenqiangbin/personMgr

扫码分享
版权说明
作者:SQBER
文章来源:http://www.sqber.com/articles/the-pay-wx.html
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。