From 0bd44afe3417454c5247c10b70897331e586536b Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Fri, 03 Jul 2026 11:25:45 +0800
Subject: [PATCH] feat(payment): 集成BSPAY巴西PIX支付功能
---
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 118 insertions(+), 0 deletions(-)
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java
index 2cf681e..130e66d 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java
@@ -14,8 +14,10 @@
import cc.mrbird.febs.mall.vo.ApiOrderPayVo;
import cc.mrbird.febs.mall.vo.OrderDetailVo;
import cc.mrbird.febs.mall.vo.OrderListVo;
+import cc.mrbird.febs.pay.service.BsPayService;
import cc.mrbird.febs.pay.service.IXcxPayService;
import cc.mrbird.febs.pay.service.LwPayService;
+import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -46,6 +48,7 @@
private final IApiMallOrderInfoService mallOrderInfoService;
private final IXcxPayService iXcxPayService;
private final LwPayService lwPayService;
+ private final BsPayService bsPayService;
private final IMallCountryDeliveryService countryDeliveryService;
@ApiOperation(value = "创建订单--验证是否允许创建", notes = "创建订单--验证是否允许创建")
@@ -353,6 +356,121 @@
return "redirect:/pages/payResult?orderNo=" + orderId + "&code=" + returncode;
}
+ // ==================== BSPAY (巴西PIX) 支付 ====================
+
+ /**
+ * 创建订单并通过 BSPAY (巴西PIX) 支付
+ * <p>
+ * 流程:创建订单 → 调 BSPAY 下单接口 → 返回支付 URL (mweb_url)
+ * <p>
+ * 交易类型:trade_type=201 (巴西PIX),货币:BRL
+ */
+ @ApiOperation(value = "创建订单-BSPAY巴西PIX支付", notes = "创建订单并返回BSPAY PIX支付URL")
+ @PostMapping(value = "/createOrderByBsPay")
+ @Limit(key = "createOrderByBsPay", period = 1, count = 1, name = "BSPAY下单", prefix = "limit", limitType = LimitType.IP)
+ public FebsResponse createOrderByBsPay(@RequestBody @Validated BsPayCreateOrderDto bsPayDto) {
+ // 1. 创建订单
+ AddOrderDto addOrderDto = bsPayDto.getOrder();
+ Long orderId = mallOrderInfoService.createOrder(addOrderDto);
+
+ // 2. 获取订单详情
+ MallOrderInfo order = mallOrderInfoService.getById(orderId);
+ if (order != null
+ && OrderStatusEnum.WAIT_PAY.getValue() == order.getStatus()
+ ) {
+ // 3. 调用 BSPAY 下单接口
+ try {
+ String payUrl = bsPayService.createPayment(order);
+
+ Map<String, Object> result = new HashMap<>();
+ result.put("orderNo", order.getOrderNo());
+ result.put("amount", order.getAmount());
+ result.put("payUrl", payUrl);
+ return new FebsResponse().success().data(result);
+ } catch (Exception e) {
+ log.error("BSPAY 下单失败: orderId={}", orderId, e);
+ return new FebsResponse().fail().message("Payment channel exception: " + e.getMessage());
+ }
+ }
+ return new FebsResponse().fail().message("Payment channel exception");
+ }
+
+ @ApiOperation(value = "BSPAY巴西PIX支付", notes = "BSPAY巴西PIX支付")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "success", response = ApiOrderPayVo.class)
+ })
+ @PostMapping(value = "/payOrderByBsPay", produces = "application/json")
+ public FebsResponse payOrderByBsPay(@RequestBody @Validated ApiOrderPayDto payDto) {
+
+ Long orderId = payDto.getOrderId();
+ Integer payType = payDto.getPayType();
+ // 2. 获取订单详情
+ MallOrderInfo order = mallOrderInfoService.getById(orderId);
+ if (order != null
+ && OrderConstants.PAY_TYPE_BS == payType
+ && OrderStatusEnum.WAIT_PAY.getValue() == order.getStatus()
+ ) {
+ // 3. 调用 BSPAY 下单接口
+ try {
+ String payUrl = bsPayService.createPayment(order);
+
+ Map<String, Object> result = new HashMap<>();
+ result.put("orderNo", order.getOrderNo());
+ result.put("amount", order.getAmount());
+ result.put("payUrl", payUrl);
+ return new FebsResponse().success().data(result).message("success");
+ } catch (Exception e) {
+ log.error("BSPAY 下单失败: orderId={}", orderId, e);
+ return new FebsResponse().fail().message("Payment channel exception: " + e.getMessage());
+ }
+ }
+ return new FebsResponse().fail().message("Payment channel exception");
+ }
+
+ /**
+ * BSPAY 支付结果通知(服务端回调)
+ * <p>
+ * BSPAY 在支付完成后会 POST JSON 通知到此地址。
+ * 需在 BSPAY 下单时传 notify_url 为此地址。
+ * <p>
+ * 注意:收到后必须响应 "SUCCESS",BSPAY 才会停止重试(最多5次)。
+ */
+ @ApiOperation(value = "BSPAY 支付回调", notes = "接收BSPAY异步通知")
+ @PostMapping(value = "/bsPayNotify")
+ public String bsPayNotify(HttpServletRequest request) {
+ // 读取 JSON body
+ try {
+ java.io.BufferedReader reader = request.getReader();
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ }
+ String body = sb.toString();
+ log.info("BSPAY 回调原始数据: {}", body);
+
+ if (StrUtil.isBlank(body)) {
+ log.error("BSPAY 回调数据为空");
+ return "FAIL";
+ }
+
+ // 解析 JSON 为 Map
+ Map<String, String> params = new HashMap<>();
+ com.alibaba.fastjson.JSONObject json = com.alibaba.fastjson.JSONObject.parseObject(body);
+ for (String key : json.keySet()) {
+ params.put(key, json.getString(key));
+ }
+
+ log.info("BSPAY 回调参数: {}", params);
+
+ boolean success = bsPayService.handleCallback(params);
+ return success ? "SUCCESS" : "FAIL";
+ } catch (Exception e) {
+ log.error("BSPAY 回调处理异常", e);
+ return "FAIL";
+ }
+ }
+
@ApiOperation(value = "根据国家编码查询运费", notes = "根据国家编码查询对应运费")
@ApiResponses({
@ApiResponse(code = 200, message = "success")
--
Gitblit v1.9.1