Commit f81dc105a2a4a438d43f5a6487b8cf25f68ddd93
1 parent
7653be9d
feat:【PAY 支付】AbstractAlipayPayClient,支付宝增加回调解析
Showing
1 changed file
with
50 additions
and
18 deletions
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
| ... | ... | @@ -81,24 +81,11 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa |
| 81 | 81 | @Override |
| 82 | 82 | public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable { |
| 83 | 83 | // 1. 校验回调数据 |
| 84 | - Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); | |
| 85 | - boolean verify; | |
| 86 | - if (Objects.equals(config.getMode(), MODE_PUBLIC_KEY)) { | |
| 87 | - verify = AlipaySignature.rsaCheckV1(params, config.getAlipayPublicKey(), | |
| 88 | - StandardCharsets.UTF_8.name(), config.getSignType()); | |
| 89 | - } else if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) { | |
| 90 | - // 由于 rsaCertCheckV1 的第二个参数是 path,所以不能这么调用!!!通过阅读源码,发现可以采用如下方式! | |
| 91 | - X509Certificate cert = AntCertificationUtil.getCertFromContent(config.getAlipayPublicCertContent()); | |
| 92 | - String publicKey = Base64.encodeBase64String(cert.getEncoded()); | |
| 93 | - verify = AlipaySignature.rsaCheckV1(bodyObj, publicKey, | |
| 94 | - StandardCharsets.UTF_8.name(), config.getSignType()); | |
| 95 | - } else { | |
| 96 | - throw new IllegalArgumentException("未知的公钥类型:" + config.getMode()); | |
| 97 | - } | |
| 98 | - Assert.isTrue(verify, "验签结果不通过"); | |
| 84 | + verifyNotifyData(params); | |
| 99 | 85 | |
| 100 | 86 | // 2. 解析订单的状态 |
| 101 | 87 | // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂 |
| 88 | + Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); | |
| 102 | 89 | Integer status = parseStatus(bodyObj.get("trade_status")); |
| 103 | 90 | // 特殊逻辑: 支付宝没有退款成功的状态,所以,如果有退款金额,我们认为是退款成功 |
| 104 | 91 | if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) { |
| ... | ... | @@ -324,10 +311,55 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa |
| 324 | 311 | response.getOutBizNo(), response); |
| 325 | 312 | } |
| 326 | 313 | |
| 327 | - // TODO @chihuo:这里是不是也要实现,支付宝的。 | |
| 314 | + // TODO @芋艿:由于支付宝一直没触发回调,这个方法暂时没办法测试 | |
| 328 | 315 | @Override |
| 329 | - protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) { | |
| 330 | - throw new UnsupportedOperationException("未实现"); | |
| 316 | + protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) | |
| 317 | + throws Throwable { | |
| 318 | + // 1. 校验回调数据 | |
| 319 | + verifyNotifyData(params); | |
| 320 | + | |
| 321 | + // 2. 解析转账状态 | |
| 322 | + Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); | |
| 323 | + String status = bodyObj.get("status"); | |
| 324 | + String outBizNo = bodyObj.get("out_biz_no"); | |
| 325 | + String orderId = bodyObj.get("order_id"); | |
| 326 | + String payDate = bodyObj.get("pay_date"); | |
| 327 | + | |
| 328 | + // 3. 根据状态返回对应的结果 | |
| 329 | + if (Objects.equals(status, "SUCCESS")) { | |
| 330 | + return PayTransferRespDTO.successOf(orderId, parseTime(payDate), outBizNo, bodyObj); | |
| 331 | + } | |
| 332 | + if (Objects.equals(status, "DEALING")) { | |
| 333 | + return PayTransferRespDTO.processingOf(orderId, outBizNo, bodyObj); | |
| 334 | + } | |
| 335 | + if (ObjectUtils.equalsAny(status, "REFUND", "FAIL")) { | |
| 336 | + return PayTransferRespDTO.closedOf(bodyObj.get("sub_code"), bodyObj.get("sub_msg"), | |
| 337 | + outBizNo, bodyObj); | |
| 338 | + } | |
| 339 | + return PayTransferRespDTO.waitingOf(orderId, outBizNo, bodyObj); | |
| 340 | + } | |
| 341 | + | |
| 342 | + /** | |
| 343 | + * 校验回调数据 | |
| 344 | + * | |
| 345 | + * @param params 回调参数 | |
| 346 | + * @throws Throwable 验签失败时抛出异常 | |
| 347 | + */ | |
| 348 | + protected void verifyNotifyData(Map<String, String> params) throws Throwable { | |
| 349 | + boolean verify; | |
| 350 | + if (Objects.equals(config.getMode(), MODE_PUBLIC_KEY)) { | |
| 351 | + verify = AlipaySignature.rsaCheckV1(params, config.getAlipayPublicKey(), | |
| 352 | + StandardCharsets.UTF_8.name(), config.getSignType()); | |
| 353 | + } else if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) { | |
| 354 | + // 由于 rsaCertCheckV1 的第二个参数是 path,所以不能这么调用!!!通过阅读源码,发现可以采用如下方式! | |
| 355 | + X509Certificate cert = AntCertificationUtil.getCertFromContent(config.getAlipayPublicCertContent()); | |
| 356 | + String publicKey = Base64.encodeBase64String(cert.getEncoded()); | |
| 357 | + verify = AlipaySignature.rsaCheckV1(params, publicKey, | |
| 358 | + StandardCharsets.UTF_8.name(), config.getSignType()); | |
| 359 | + } else { | |
| 360 | + throw new IllegalArgumentException("未知的公钥类型:" + config.getMode()); | |
| 361 | + } | |
| 362 | + Assert.isTrue(verify, "验签结果不通过"); | |
| 331 | 363 | } |
| 332 | 364 | |
| 333 | 365 | // ========== 各种工具方法 ========== | ... | ... |