diff --git a/jsowell-admin/src/test/java/PaymentTestController.java b/jsowell-admin/src/test/java/PaymentTestController.java index 1ab1b3fa1..45eb56748 100644 --- a/jsowell-admin/src/test/java/PaymentTestController.java +++ b/jsowell-admin/src/test/java/PaymentTestController.java @@ -8,12 +8,18 @@ import com.huifu.adapay.core.exception.BaseAdaPayException; import com.huifu.adapay.model.Payment; import com.huifu.adapay.model.PaymentReverse; import com.huifu.adapay.model.Refund; +import com.jsowell.adapay.common.AdaPayment; import com.jsowell.JsowellApplication; import com.jsowell.adapay.common.DivMember; import com.jsowell.adapay.common.PaymentConfirmInfo; +import com.jsowell.adapay.common.RefundInfo; +import com.jsowell.adapay.config.AbstractAdapayConfig; import com.jsowell.adapay.dto.PaymentConfirmParam; +import com.jsowell.adapay.dto.QueryConfirmReverseDTO; import com.jsowell.adapay.dto.QueryPaymentConfirmDTO; +import com.jsowell.adapay.factory.AdapayConfigFactory; import com.jsowell.adapay.operation.PaymentReverseOperation; +import com.jsowell.adapay.response.ConfirmReverseResponse; import com.jsowell.adapay.response.PaymentConfirmResponse; import com.jsowell.adapay.response.PaymentReverseResponse; import com.jsowell.adapay.response.QueryPaymentConfirmDetailResponse; @@ -24,10 +30,12 @@ import com.jsowell.api.uniapp.customer.TempController; import com.jsowell.common.constant.CacheConstants; import com.jsowell.common.constant.Constants; import com.jsowell.common.core.redis.RedisCache; +import com.jsowell.common.enums.adapay.AdapayStatusEnum; import com.jsowell.common.enums.ykc.ScenarioEnum; import com.jsowell.common.util.AdapayUtil; import com.jsowell.common.util.DateUtils; import com.jsowell.common.util.PageUtils; +import com.jsowell.common.util.StringUtils; import com.jsowell.pile.domain.AdapayUnsplitRecord; import com.jsowell.pile.domain.OrderBasicInfo; import com.jsowell.pile.domain.OrderDetail; @@ -60,7 +68,7 @@ import java.util.concurrent.TimeUnit; /** * 专用处理汇付支付相关 */ -@ActiveProfiles("dev") +@ActiveProfiles("sit") @SpringBootTest(classes = JsowellApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @RunWith(SpringRunner.class) public class PaymentTestController { @@ -940,4 +948,197 @@ public class PaymentTestController { logger.info("{}", JSON.toJSONString(paymentConfirmInfo)); } + + @Test + public void queryPaymentAllInfoTest() throws BaseAdaPayException { + String paymentId = System.getProperty("paymentId"); + if (StringUtils.isBlank(paymentId)) { + throw new IllegalArgumentException("请通过 -DpaymentId=支付ID 传入 paymentId"); + } + JSONObject result = queryPaymentAllInfo(paymentId); + logger.info("paymentId:{}, result:{}", paymentId, JSON.toJSONString(result)); + } + + public JSONObject queryPaymentAllInfo(String paymentId) throws BaseAdaPayException { + if (StringUtils.isBlank(paymentId)) { + throw new IllegalArgumentException("paymentId不能为空"); + } + + PaymentQueryContext context = queryPaymentContext(paymentId); + if (context == null) { + throw new IllegalArgumentException("未匹配到支付单,paymentId:" + paymentId); + } + + QueryPaymentConfirmDTO queryPaymentConfirmDTO = new QueryPaymentConfirmDTO(); + queryPaymentConfirmDTO.setWechatAppId(context.getWechatAppId()); + queryPaymentConfirmDTO.setPaymentId(paymentId); + QueryPaymentConfirmDetailResponse confirmResponse = adapayService.queryPaymentConfirmList(queryPaymentConfirmDTO); + List paymentConfirms = confirmResponse == null || CollectionUtils.isEmpty(confirmResponse.getPaymentConfirms()) + ? Lists.newArrayList() + : paymentResponseToList(confirmResponse.getPaymentConfirms()); + + List paymentReverses = adapayService.queryPaymentReverse(paymentId, context.getWechatAppId()); + if (paymentReverses == null) { + paymentReverses = Lists.newArrayList(); + } + + List refunds = adapayService.queryPaymentRefund(paymentId, context.getWechatAppId()); + if (refunds == null) { + refunds = Lists.newArrayList(); + } + + AdaPayment payment = context.getPayment(); + BigDecimal payAmount = safeAmount(payment.getPay_amt()); + BigDecimal apiConfirmedAmount = safeAmount(payment.getConfirmed_amt()); + BigDecimal apiRefundedAmount = safeAmount(payment.getRefunded_amt()); + BigDecimal apiReservedAmount = safeAmount(payment.getReserved_amt()); + + BigDecimal successConfirmedAmount = sumConfirmedAmount(paymentConfirms, context.getWechatAppId()); + BigDecimal successRefundAmount = sumRefundAmount(refunds); + BigDecimal successReverseAmount = sumReverseAmount(paymentReverses); + + BigDecimal alreadySplitAmount = apiConfirmedAmount.compareTo(BigDecimal.ZERO) > 0 + ? apiConfirmedAmount + : successConfirmedAmount; + BigDecimal alreadyRefundAmount; + if (StringUtils.equals("delay", payment.getPay_mode())) { + alreadyRefundAmount = successReverseAmount.compareTo(BigDecimal.ZERO) > 0 + ? successReverseAmount + : apiReservedAmount; + } else { + alreadyRefundAmount = successRefundAmount.compareTo(BigDecimal.ZERO) > 0 + ? successRefundAmount + : apiRefundedAmount; + } + BigDecimal unsplitAmount = payAmount.subtract(alreadySplitAmount).subtract(alreadyRefundAmount); + if (unsplitAmount.compareTo(BigDecimal.ZERO) < 0) { + unsplitAmount = BigDecimal.ZERO; + } + + JSONObject summary = new JSONObject(); + summary.put("payMode", payment.getPay_mode()); + summary.put("payAmount", AdapayUtil.formatAmount(payAmount)); + summary.put("apiConfirmedAmount", AdapayUtil.formatAmount(apiConfirmedAmount)); + summary.put("apiRefundedAmount", AdapayUtil.formatAmount(apiRefundedAmount)); + summary.put("apiReservedAmount", AdapayUtil.formatAmount(apiReservedAmount)); + summary.put("successConfirmedAmount", AdapayUtil.formatAmount(successConfirmedAmount)); + summary.put("successRefundAmount", AdapayUtil.formatAmount(successRefundAmount)); + summary.put("successReverseAmount", AdapayUtil.formatAmount(successReverseAmount)); + summary.put("alreadySplitAmount", AdapayUtil.formatAmount(alreadySplitAmount)); + summary.put("alreadyRefundAmount", AdapayUtil.formatAmount(alreadyRefundAmount)); + summary.put("unsplitAmount", AdapayUtil.formatAmount(unsplitAmount)); + + JSONObject result = new JSONObject(); + result.put("paymentId", paymentId); + result.put("wechatAppId", context.getWechatAppId()); + result.put("adapayAppId", context.getAdapayAppId()); + result.put("payment", payment); + result.put("paymentRaw", JSON.parseObject(JSON.toJSONString(payment))); + result.put("paymentConfirms", paymentConfirms); + result.put("paymentReverses", paymentReverses); + result.put("refunds", refunds); + result.put("summary", summary); + return result; + } + + private PaymentQueryContext queryPaymentContext(String paymentId) throws BaseAdaPayException { + List configList = AdapayConfigFactory.getAllConfig(); + if (CollectionUtils.isEmpty(configList)) { + throw new IllegalStateException("未加载到汇付商户配置"); + } + + for (AbstractAdapayConfig config : configList) { + AdaPayment payment = adapayService.queryPaymentDetailWithFallback(paymentId, config.getWechatAppId()); + if (payment != null) { + return new PaymentQueryContext(config.getWechatAppId(), config.getAdapayAppId(), payment); + } + } + return null; + } + + private BigDecimal sumConfirmedAmount(List paymentConfirms, String wechatAppId) throws BaseAdaPayException { + BigDecimal totalAmount = BigDecimal.ZERO; + if (CollectionUtils.isEmpty(paymentConfirms)) { + return totalAmount; + } + for (PaymentConfirmInfo paymentConfirm : paymentConfirms) { + if (isConfirmReversed(paymentConfirm, wechatAppId)) { + continue; + } + totalAmount = totalAmount.add(safeAmount(paymentConfirm.getConfirmedAmt())); + } + return totalAmount; + } + + private boolean isConfirmReversed(PaymentConfirmInfo paymentConfirm, String wechatAppId) throws BaseAdaPayException { + QueryConfirmReverseDTO queryConfirmReverseDTO = QueryConfirmReverseDTO.builder() + .wechatAppId(wechatAppId) + .paymentConfirmId(paymentConfirm.getId()) + .build(); + ConfirmReverseResponse confirmReverseResponse = adapayService.queryConfirmReverse(queryConfirmReverseDTO); + return confirmReverseResponse != null && confirmReverseResponse.isSuccess(); + } + + private BigDecimal sumRefundAmount(List refunds) { + BigDecimal totalAmount = BigDecimal.ZERO; + if (CollectionUtils.isEmpty(refunds)) { + return totalAmount; + } + for (RefundInfo refund : refunds) { + if (!StringUtils.equals("S", refund.getTrans_status())) { + continue; + } + totalAmount = totalAmount.add(safeAmount(refund.getRefund_amt())); + } + return totalAmount; + } + + private BigDecimal sumReverseAmount(List paymentReverses) { + BigDecimal totalAmount = BigDecimal.ZERO; + if (CollectionUtils.isEmpty(paymentReverses)) { + return totalAmount; + } + for (PaymentReverseResponse paymentReverseResponse : paymentReverses) { + if (!StringUtils.equals(AdapayStatusEnum.SUCCEEDED.getValue(), paymentReverseResponse.getStatus())) { + continue; + } + totalAmount = totalAmount.add(safeAmount(paymentReverseResponse.getReverse_amt())); + } + return totalAmount; + } + + private BigDecimal safeAmount(String amount) { + if (StringUtils.isBlank(amount)) { + return BigDecimal.ZERO; + } + return new BigDecimal(amount); + } + + private List paymentResponseToList(List paymentConfirms) { + return Lists.newArrayList(paymentConfirms); + } + + private static class PaymentQueryContext { + private final String wechatAppId; + private final String adapayAppId; + private final AdaPayment payment; + + private PaymentQueryContext(String wechatAppId, String adapayAppId, AdaPayment payment) { + this.wechatAppId = wechatAppId; + this.adapayAppId = adapayAppId; + this.payment = payment; + } + + private String getWechatAppId() { + return wechatAppId; + } + + private String getAdapayAppId() { + return adapayAppId; + } + + private AdaPayment getPayment() { + return payment; + } + } } diff --git a/jsowell-pile/src/main/java/com/jsowell/adapay/service/AdapayService.java b/jsowell-pile/src/main/java/com/jsowell/adapay/service/AdapayService.java index 295b56d1c..5ff62e405 100644 --- a/jsowell-pile/src/main/java/com/jsowell/adapay/service/AdapayService.java +++ b/jsowell-pile/src/main/java/com/jsowell/adapay/service/AdapayService.java @@ -1345,7 +1345,74 @@ public class AdapayService { } /** - * 查询支付退款对象 + * 按汇付文档查询支付对象详情 + * Payment.query(paymentId, merchantKey) + */ + public AdaPayment queryPaymentDetail(String paymentId, String wechatAppId) throws BaseAdaPayException { + AbstractAdapayConfig config = AdapayConfigFactory.getConfig(wechatAppId); + if (config == null) { + throw new BusinessException(ReturnCodeEnum.CODE_ADAPAY_CONFIG_IS_NULL_ERROR); + } + Map response = Payment.query(paymentId, config.getWechatAppId()); + if (response == null || response.isEmpty()) { + return null; + } + String resultPaymentId = (String) response.get("id"); + String errorCode = (String) response.get("error_code"); + if (!StringUtils.equals(paymentId, resultPaymentId) || StringUtils.isNotBlank(errorCode)) { + return null; + } + return JSON.parseObject(JSON.toJSONString(response), AdaPayment.class); + } + + /** + * 按汇付文档通过 payment_id 查询支付对象列表 + * 作为 Payment.query 的补充,用于查询 3 天前的交易 + */ + public List queryPaymentListByPaymentId(String paymentId, String wechatAppId) throws BaseAdaPayException { + List resultList = Lists.newArrayList(); + AbstractAdapayConfig config = AdapayConfigFactory.getConfig(wechatAppId); + if (config == null) { + throw new BusinessException(ReturnCodeEnum.CODE_ADAPAY_CONFIG_IS_NULL_ERROR); + } + Map queryListParam = Maps.newHashMap(); + queryListParam.put("app_id", config.getAdapayAppId()); + queryListParam.put("payment_id", paymentId); + queryListParam.put("page_index", 1); + queryListParam.put("page_size", 1); + Map paymentListResult = Payment.queryList(queryListParam, config.getWechatAppId()); + if (paymentListResult == null || paymentListResult.isEmpty()) { + return resultList; + } + + JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(paymentListResult)); + List payments = jsonObject.getList("payments", AdaPayment.class, JSONReader.Feature.FieldBased); + if (CollectionUtils.isNotEmpty(payments)) { + resultList.addAll(payments); + } + return resultList; + } + + /** + * 查询支付对象详情 + * 先查 Payment.query,未命中时再按 payment_id 查询列表 + */ + public AdaPayment queryPaymentDetailWithFallback(String paymentId, String wechatAppId) throws BaseAdaPayException { + AdaPayment payment = queryPaymentDetail(paymentId, wechatAppId); + if (payment != null) { + return payment; + } + + List payments = queryPaymentListByPaymentId(paymentId, wechatAppId); + if (CollectionUtils.isEmpty(payments)) { + return null; + } + return payments.get(0); + } + + /** + * 按汇付文档通过 payment_id 查询退款对象 + * Refund.query({payment_id}, merchantKey) */ public List queryPaymentRefund(String paymentId, String wechatAppId) throws BaseAdaPayException { AbstractAdapayConfig config = AdapayConfigFactory.getConfig(wechatAppId); @@ -1354,7 +1421,6 @@ public class AdapayService { } Map refundParams = Maps.newHashMap(); refundParams.put("payment_id", paymentId); - refundParams.put("app_id", config.getAdapayAppId()); Map response = Refund.query(refundParams, config.getWechatAppId()); PaymentRefundResponse paymentRefundResponse = JSON.parseObject(JSON.toJSONString(response), PaymentRefundResponse.class); List refunds = paymentRefundResponse.getRefunds();