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,24 +81,11 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | ||
| 81 | @Override | 81 | @Override |
| 82 | public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable { | 82 | public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable { |
| 83 | // 1. 校验回调数据 | 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 | // 2. 解析订单的状态 | 86 | // 2. 解析订单的状态 |
| 101 | // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂 | 87 | // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂 |
| 88 | + Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); | ||
| 102 | Integer status = parseStatus(bodyObj.get("trade_status")); | 89 | Integer status = parseStatus(bodyObj.get("trade_status")); |
| 103 | // 特殊逻辑: 支付宝没有退款成功的状态,所以,如果有退款金额,我们认为是退款成功 | 90 | // 特殊逻辑: 支付宝没有退款成功的状态,所以,如果有退款金额,我们认为是退款成功 |
| 104 | if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) { | 91 | if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) { |
| @@ -324,10 +311,55 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | @@ -324,10 +311,55 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | ||
| 324 | response.getOutBizNo(), response); | 311 | response.getOutBizNo(), response); |
| 325 | } | 312 | } |
| 326 | 313 | ||
| 327 | - // TODO @chihuo:这里是不是也要实现,支付宝的。 | 314 | + // TODO @芋艿:由于支付宝一直没触发回调,这个方法暂时没办法测试 |
| 328 | @Override | 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 | // ========== 各种工具方法 ========== |