| | |
| | | package com.tourguide.common.huifu; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.huifu.bspay.sdk.opps.core.utils.RsaUtils; |
| | | import com.tourguide.common.huifu.properties.HuiFuProperties; |
| | | import com.tourguide.modules.order.service.HuifuPaymentCallbackService; |
| | | import com.tourguide.modules.order.service.HuifuRefundCallbackService; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | |
| | | /** |
| | | * 斗拱回调入口。 |
| | |
| | | public class DouGongNotifyController { |
| | | private static final Logger log = LoggerFactory.getLogger(DouGongNotifyController.class); |
| | | |
| | | @Autowired |
| | | private HuiFuProperties huiFuProperties; |
| | | private final HuifuCallbackVerifier callbackVerifier; |
| | | private final HuifuPaymentCallbackService paymentCallbackService; |
| | | private final HuifuRefundCallbackService refundCallbackService; |
| | | |
| | | public DouGongNotifyController(HuifuCallbackVerifier callbackVerifier, |
| | | HuifuPaymentCallbackService paymentCallbackService, |
| | | HuifuRefundCallbackService refundCallbackService) { |
| | | this.callbackVerifier = callbackVerifier; |
| | | this.paymentCallbackService = paymentCallbackService; |
| | | this.refundCallbackService = refundCallbackService; |
| | | } |
| | | |
| | | @PostMapping("/pay-notify") |
| | | public String payNotify(HttpServletRequest request) { |
| | | return handleNotify("dougong-pay", request); |
| | | public String payNotify(@RequestParam(value = "sign", required = false) String sign, |
| | | @RequestParam(value = "resp_data", required = false) String respData) { |
| | | try { |
| | | JSONObject body = callbackVerifier.verify(sign, respData); |
| | | paymentCallbackService.handle(body); |
| | | return "200"; |
| | | } catch (Exception exception) { |
| | | log.warn("dougong-pay callback failed: {}", exception.getMessage()); |
| | | return "FAIL"; |
| | | } |
| | | } |
| | | |
| | | @PostMapping("/refund-notify") |
| | | public String refundNotify(@RequestParam(value = "sign", required = false) String sign, |
| | | @RequestParam(value = "resp_data", required = false) String respData) { |
| | | try { |
| | | JSONObject body = callbackVerifier.verify(sign, respData); |
| | | refundCallbackService.handle(body); |
| | | return "200"; |
| | | } catch (Exception exception) { |
| | | log.warn("dougong-refund callback failed: {}", exception.getMessage()); |
| | | return "FAIL"; |
| | | } |
| | | } |
| | | |
| | | @PostMapping("/refund-logistics") |
| | | public String refundLogistics(HttpServletRequest request) { |
| | | return handleNotify("dougong-refund-logistics", request); |
| | | public String refundLogistics(@RequestParam(value = "sign", required = false) String sign, |
| | | @RequestParam(value = "resp_data", required = false) String respData) { |
| | | return handleNotify("dougong-refund-logistics", sign, respData); |
| | | } |
| | | |
| | | @PostMapping("/refund-crosscity") |
| | | public String refundCrossCity(HttpServletRequest request) { |
| | | return handleNotify("dougong-refund-crosscity", request); |
| | | public String refundCrossCity(@RequestParam(value = "sign", required = false) String sign, |
| | | @RequestParam(value = "resp_data", required = false) String respData) { |
| | | return handleNotify("dougong-refund-crosscity", sign, respData); |
| | | } |
| | | |
| | | @PostMapping("/encashment") |
| | | public String encashment(HttpServletRequest request) { |
| | | return handleNotify("dougong-encashment", request); |
| | | public String encashment(@RequestParam(value = "sign", required = false) String sign, |
| | | @RequestParam(value = "resp_data", required = false) String respData) { |
| | | return handleNotify("dougong-encashment", sign, respData); |
| | | } |
| | | |
| | | private String handleNotify(String bizName, HttpServletRequest request) { |
| | | String sign = request.getParameter("sign"); |
| | | String respData = request.getParameter("resp_data"); |
| | | if (!StringUtils.hasText(sign) || !StringUtils.hasText(respData)) { |
| | | log.warn("{} missing params, sign={}, resp_data={}", bizName, sign, respData); |
| | | private String handleNotify(String bizName, String sign, String respData) { |
| | | try { |
| | | JSONObject body = callbackVerifier.verify(sign, respData); |
| | | log.info("{} verified, req_seq_id={}, org_req_seq_id={}, trans_stat={}", bizName, |
| | | body.getString("req_seq_id"), body.getString("org_req_seq_id"), body.getString("trans_stat")); |
| | | return "200"; |
| | | } catch (Exception exception) { |
| | | log.warn("{} callback failed: {}", bizName, exception.getMessage()); |
| | | return "FAIL"; |
| | | } |
| | | |
| | | // 备注:斗拱回调可能把括号转义为带空格的 HTML 实体,验签前必须恢复原文,否则签名会不一致。 |
| | | String normalizedRespData = respData.replaceAll("& #40;", "(").replaceAll("& #41;", ")"); |
| | | boolean verified = RsaUtils.verify(normalizedRespData, huiFuProperties.getRsaPublicKey(), sign); |
| | | if (!verified) { |
| | | log.warn("{} signature verify failed, resp_data={}", bizName, normalizedRespData); |
| | | return "FAIL"; |
| | | } |
| | | |
| | | JSONObject body = JSONObject.parseObject(normalizedRespData); |
| | | // 备注:当前阶段先记录回调关键流水,后续订单支付确认、退款确认、提现确认都应从这里分发到幂等服务。 |
| | | log.info("{} verified, req_seq_id={}, org_req_seq_id={}, trans_stat={}, body={}", |
| | | bizName, |
| | | body.getString("req_seq_id"), |
| | | body.getString("org_req_seq_id"), |
| | | body.getString("trans_stat"), |
| | | body); |
| | | return "200"; |
| | | } |
| | | } |