本文简单总结一下 Java 中 SpringBoot 对接微信支付需要做的一些工作。
准备工作
在对接微信支付时,可以使用微信提供的 SDK 进行对接:使用 Java SDK 快速开始
其中需要进行一些必要的配置,如下:
  
具体要做的工作有如下3个:
- 生成商户 API 证书。生成方法:商户API证书获取方法及功能介绍
 
- 商户 API 证书生成之后,拿到证书序列号。获取方法:证书相关问题
 
- 配置 APIv3 密钥。配置方法:API v3密钥
 
服务端获取微信支付请求串
App 端在调起微信支付前,需要先请求服务端拿到交易ID信息,之后还需要组装一个请求串,组装请求串时涉及加签操作,需要用到微信支付私钥,出于安全考虑,这个组装请求的操作最好在服务端处理。可以直接将这两个步骤合并,App 端请求服务端获取支付请求串,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
   | 
 
 
 
 
  @Component public class WeChatPayUtil {     @Autowired     private WeChatPayProperties weChatPayProperties;     private static WeChatPayProperties staticWeChatPayProperties;     private static Config config;
      private static final Logger logger = LoggerFactory.getLogger(WeChatPayUtil.class);
      @PostConstruct     public void init() {         staticWeChatPayProperties = weChatPayProperties;         config = new RSAAutoCertificateConfig.Builder()                         .merchantId(staticWeChatPayProperties.getMerchantId())                         .privateKeyFromPath(staticWeChatPayProperties.getPrivateKeyPath())                         .merchantSerialNumber(staticWeChatPayProperties.getMerchantSerialNumber())                         .apiV3Key(staticWeChatPayProperties.getApiV3key())                         .build();     }
      
 
 
 
 
 
 
      public static String requestAppApy(String payWater, BigDecimal money, String subject) {         AppService appService = new AppService.Builder().config(config).build();         PrepayRequest request = new PrepayRequest();         Amount amount = new Amount();                  amount.setTotal(money.multiply(new BigDecimal(100)).intValue());         amount.setCurrency("CNY");         request.setAmount(amount);         request.setAppid(staticWeChatPayProperties.getAppID());         request.setMchid(staticWeChatPayProperties.getMerchantId());         request.setDescription(subject);         request.setNotifyUrl(staticWeChatPayProperties.getNotifyUrl());         request.setOutTradeNo(payWater);         PrepayResponse response = appService.prepay(request);         logger.info("发起微信支付返回结果:{}", response.getPrepayId());         return getAppPayInfo(response.getPrepayId());     }
      
 
 
 
 
      private static String getAppPayInfo(String prepayid) {         JSONObject jsonObject = new JSONObject();         jsonObject.put("appid", staticWeChatPayProperties.getAppID());         jsonObject.put("prepayid", prepayid);         jsonObject.put("noncestr", UuidUtil.getUid());         jsonObject.put("timestamp", System.currentTimeMillis() / 1000);         jsonObject.put("partnerid", staticWeChatPayProperties.getMerchantId());         jsonObject.put("package", "Sign=WXPay");         jsonObject.put("sign", generateSignature(jsonObject));         return jsonObject.toJSONString();     }
      
 
 
 
 
      private static String generateSignature(JSONObject jsonObject) {         String str = jsonObject.getString("appid") + "\n" + jsonObject.getString("timestamp") + "\n" + jsonObject.getString("noncestr") + "\n" + jsonObject.getString("prepayid");         try {             Signature sign = Signature.getInstance("SHA256withRSA");             sign.initSign(getPrivateKey());             sign.update(str.getBytes(StandardCharsets.UTF_8));             return Base64.getEncoder().encodeToString(sign.sign());         } catch (Exception e) {             throw new RuntimeException("签名生成失败!", e);         }     }
      
 
 
 
      private static PrivateKey getPrivateKey() throws Exception {         String content = new String(Files.readAllBytes(Paths.get(staticWeChatPayProperties.getPrivateKeyPath())), StandardCharsets.UTF_8);         String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")                 .replace("-----END PRIVATE KEY-----", "")                 .replaceAll("\\s+", "");         KeyFactory kf = KeyFactory.getInstance("RSA");         return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));     } }
 
  | 
 
关于签名的生成方法参考微信官方文档中的这两个指引:
- 如何生成请求签名 - 微信支付文档
 
- 如何在程序中加载私钥 - 微信支付文档
 
处理微信支付回调
微信支付文档只给了处理回调的步骤,并没有给出具体的代码案例。在 SpringBoot+微信支付-JSAPI{微信支付回调}  这篇文档中给出了一个案例,在这个案例的基础上进行了处理,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | @RequestMapping("/api/aliReturnPay") @RestController public class PayReturnController {   @Autowired   private WeChatPayReturnManager weChatPayReturnManager;      
 
 
    @PostMapping("/returnPayAsynchronousFromWeChatPay")   public Map<String, Object> returnPayAsynchronousFromWeChatPay(HttpServletResponse response, HttpServletRequest request) throws IOException  {       boolean dealResult = weChatPayReturnManager.dealAsyncResultFromWeChatPay(request);              Map<String, Object> map = new HashMap<>();       map.put("code", dealResult ? "SUCCESS" : "FAIL");       return map;   } }
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
   | 
 
 
 
  @Component public class WeChatPayReturnManager {     @Autowired     private IPayReturnService payReturnService;
      private final Logger logger = LoggerFactory.getLogger(WeChatPayReturnManager.class);
      
 
 
 
 
      public boolean dealAsyncResultFromWeChatPay(HttpServletRequest request) {         String notifyBody = getWechatPayNotifyBody(request);         logger.info("收到来自微信支付的回调请求: {}", notifyBody);         if (StringUtils.isBlank(notifyBody)) {             logger.error("非法请求!");             return false;         }         RequestParam notifyHeader = getWechatPayNotifyHeader(request, notifyBody);                  NotificationParser notificationParser = new NotificationParser(WeChatPayUtil.getConfigInst());         Transaction transaction = null;         try {             transaction = notificationParser.parse(notifyHeader, Transaction.class);         } catch (ValidationException e) {             logger.error("验签失败!", e);             return false;         }         logger.info("验签成功!订单状态:{},订单完整信息:{}", transaction.getTradeState(), JSON.toJSONString(transaction));                  if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {             return payReturnService.updateForSpecificLogic(transaction.getOutTradeNo(), null);         }         return true;     }
      
 
 
 
      private String getWechatPayNotifyBody(HttpServletRequest request) {         StringBuilder sb = new StringBuilder();         try (BufferedReader reader = request.getReader()) {             String line;             while ((line = reader.readLine()) != null) {                 sb.append(line);             }         } catch (IOException e) {             logger.error("微信支付回调结果获取失败!", e);             return null;         }         return sb.toString();     }
      
 
 
 
 
      private RequestParam getWechatPayNotifyHeader(HttpServletRequest request, String notifyBody) {                  String timestamp = request.getHeader(Constant.WECHAT_PAY_TIMESTAMP);                  String nonce = request.getHeader(Constant.WECHAT_PAY_NONCE);                  String signType = request.getHeader("Wechatpay-Signature-Type");                  String serialNo = request.getHeader(Constant.WECHAT_PAY_SERIAL);                  String signature = request.getHeader(Constant.WECHAT_PAY_SIGNATURE);                  return new RequestParam.Builder()                 .serialNumber(serialNo)                 .nonce(nonce)                 .signature(signature)                 .timestamp(timestamp)                 .signType(signType)                 .body(notifyBody)                 .build();     } }
 
  | 
 
总结
微信支付提供的文档并不是很完善,并没有提供比较完善的 Demo 供开发者对接,对比支付宝的对接文档差了很多。
参考文档
APP下单 - 微信支付文档
APP调起支付 - 微信支付文档
如何生成请求签名 - 微信支付文档
如何在程序中加载私钥 - 微信支付文档
APP调起支付签名 - 微信支付文档
支付回调和查单实现指引 - 微信支付文档
SpringBoot+微信支付-JSAPI{微信支付回调}