package com.tourguide.modules.order.service; import com.alibaba.fastjson.JSONObject; import com.tourguide.common.exception.BusinessException; import com.tourguide.common.huifu.properties.HuiFuProperties; import com.tourguide.modules.order.payment.HuifuPaymentNotice; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @Service public class HuifuPaymentCallbackService { private static final Logger log = LoggerFactory.getLogger(HuifuPaymentCallbackService.class); private static final DateTimeFormatter PAYMENT_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); private final HuiFuProperties properties; private final TourOrderPaymentService orderPaymentService; public HuifuPaymentCallbackService(HuiFuProperties properties, TourOrderPaymentService orderPaymentService) { this.properties = properties; this.orderPaymentService = orderPaymentService; } public void handle(JSONObject body) { String reqSeqId = first(body, "req_seq_id", "org_req_seq_id"); if (!StringUtils.hasText(reqSeqId)) throw new BusinessException(400, "汇付回调缺少请求流水号"); String callbackHuifuId = first(body, "huifu_id", "out_huifu_id"); validateMerchant(callbackHuifuId); String transactionStatus = body.getString("trans_stat"); if (!"S".equals(transactionStatus)) { log.info("汇付支付回调非成功状态,reqSeqId={},transStat={}", reqSeqId, transactionStatus); return; } BigDecimal amount = amount(body); HuifuPaymentNotice notice = new HuifuPaymentNotice(reqSeqId, callbackHuifuId, transactionStatus, amount, first(body, "hf_seq_id", "out_trans_id", "transaction_id"), paymentTime(body)); boolean confirmed = orderPaymentService.confirm(notice); log.info("汇付支付回调处理完成,reqSeqId={},首次确认={},providerTransactionId={}", reqSeqId, confirmed, notice.getProviderTransactionId()); } private void validateMerchant(String callbackHuifuId) { if (!StringUtils.hasText(callbackHuifuId)) { throw new BusinessException(400, "汇付回调缺少商户号"); } if (!StringUtils.hasText(properties.getHuifuId()) || !properties.getHuifuId().equals(callbackHuifuId)) { throw new BusinessException(400, "汇付回调商户号不匹配"); } } private BigDecimal amount(JSONObject body) { String value = first(body, "trans_amt", "ord_amt", "pay_amt"); if (!StringUtils.hasText(value)) throw new BusinessException(400, "汇付回调缺少支付金额"); try { return new BigDecimal(value); } catch (NumberFormatException exception) { throw new BusinessException(400, "汇付回调支付金额格式错误"); } } private LocalDateTime paymentTime(JSONObject body) { String value = first(body, "end_time", "trans_time", "pay_time"); if (!StringUtils.hasText(value)) return LocalDateTime.now(); String normalized = value.replace("-", "").replace(":", "").replace(" ", ""); try { return LocalDateTime.parse(normalized, PAYMENT_TIME_FORMAT); } catch (DateTimeParseException exception) { return LocalDateTime.now(); } } private String first(JSONObject body, String... keys) { for (String key : keys) { String value = body.getString(key); if (StringUtils.hasText(value)) return value; } return null; } }