Administrator
7 hours ago 0bd44afe3417454c5247c10b70897331e586536b
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java
@@ -3,14 +3,22 @@
import cc.mrbird.febs.common.annotation.Limit;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.LimitType;
import cc.mrbird.febs.common.enumerates.OrderStatusEnum;
import cc.mrbird.febs.common.utils.MallUtils;
import cc.mrbird.febs.common.utils.ValidateEntityUtils;
import cc.mrbird.febs.mall.controller.dependentStation.constant.OrderConstants;
import cc.mrbird.febs.mall.dto.*;
import cc.mrbird.febs.mall.entity.MallOrderInfo;
import cc.mrbird.febs.mall.service.IApiMallOrderInfoService;
import cc.mrbird.febs.mall.service.IMallCountryDeliveryService;
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;
import io.swagger.annotations.ApiResponse;
@@ -40,6 +48,8 @@
    private final IApiMallOrderInfoService mallOrderInfoService;
    private final IXcxPayService iXcxPayService;
    private final LwPayService lwPayService;
    private final BsPayService bsPayService;
    private final IMallCountryDeliveryService countryDeliveryService;
    @ApiOperation(value = "创建订单--验证是否允许创建", notes = "创建订单--验证是否允许创建")
    @PostMapping(value = "/createOrderVerify")
@@ -169,6 +179,49 @@
        return new FebsResponse().success().data(iXcxPayService.getTemplateId());
    }
    // ==================== XT 支付 ====================
    /**
     * 创建订单并通过 XT 支付
     * <p>
     * 流程:创建订单 → 通过 XT 支付链接
     * <p>
     */
    @ApiOperation(value = "创建订单-XT支付", notes = "创建订单并返回XT支付")
    @PostMapping(value = "/createOrderByXtPay")
    @Limit(key = "createOrderByXtPay", period = 1, count = 1, name = "XT下单", prefix = "limit", limitType = LimitType.IP)
    public FebsResponse createOrderByXtPay(@RequestBody @Validated XtPayCreateOrderDto lwPayDto) {
        // 1. 创建订单
        AddOrderDto addOrderDto = lwPayDto.getOrder();
        Long orderId = mallOrderInfoService.createOrder(addOrderDto);
        // 2. 获取订单详情
        MallOrderInfo order = mallOrderInfoService.getById(orderId);
        if (order != null
                && OrderStatusEnum.WAIT_PAY.getValue() == order.getStatus()
        ){
            return mallOrderInfoService.createOrderByXtPay();
        }
        return new FebsResponse().fail().message("Payment channel exception");
    }
    @ApiOperation(value = "XT支付", notes = "XT支付")
    @PostMapping(value = "/payOrderByXtPay", produces = "application/json")
    public FebsResponse payOrderByXtPay(@RequestBody @Validated ApiOrderPayDto payDto) {
        Long orderId = payDto.getOrderId();
        Integer payType = payDto.getPayType();
        // 2. 获取订单详情
        MallOrderInfo order = mallOrderInfoService.getById(orderId);
        if (order != null
                && OrderConstants.PAY_TYPE_XT == payType
                && OrderStatusEnum.WAIT_PAY.getValue() == order.getStatus()
        ){
            return mallOrderInfoService.createOrderByXtPay();
        }
        return new FebsResponse().fail().message("Payment channel exception");
    }
    // ==================== LWPAY 支付 ====================
    /**
@@ -190,27 +243,76 @@
        // 2. 获取订单详情
        MallOrderInfo order = mallOrderInfoService.getById(orderId);
        if (order == null) {
            return new FebsResponse().fail().message("订单创建失败");
        }
        if (order != null
                && OrderStatusEnum.WAIT_PAY.getValue() == order.getStatus()
        ){
        // 3. 调用 LWPAY 代收接口
        try {
            String payUrl = lwPayService.createPayment(
                    order,
                    lwPayDto.getBankCode(),
                    lwPayDto.getNetwork()
            // 3. 调用 LWPAY 代收接口
            try {
                String payUrl = lwPayService.createPayment(
                        order,
                        lwPayDto.getBankCode(),
                        lwPayDto.getNetwork()
                );
                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("LWPAY 代收下单失败: orderId={}", orderId, e);
                return new FebsResponse().fail().message("Payment channel exception: " + e.getMessage());
            }
        }
        return new FebsResponse().fail().message("Payment channel exception");
    }
    @ApiOperation(value = "LWPAY支付", notes = "LWPAY支付")
    @ApiResponses({
            @ApiResponse(code = 200, message = "success", response = ApiOrderPayVo.class)
    })
    @PostMapping(value = "/payOrderByLwPay", produces = "application/json")
    public FebsResponse payOrderByLwPay(@RequestBody @Validated ApiOrderPayDto payDto) {
        Long orderId = payDto.getOrderId();
        Integer payType = payDto.getPayType();
        // 2. 获取订单详情
        MallOrderInfo order = mallOrderInfoService.getById(orderId);
        if (order != null
                && OrderConstants.PAY_TYPE_SYSTEM == payType
                && OrderStatusEnum.WAIT_PAY.getValue() == order.getStatus()
        ){
            String orderNo = MallUtils.getOrderNum();
            order.setOrderNo(orderNo);
            mallOrderInfoService.getBaseMapper().update(
                    null,
                    Wrappers.lambdaUpdate(MallOrderInfo.class)
                    .set(MallOrderInfo::getOrderNo, orderNo)
                    .eq(MallOrderInfo::getId, orderId)
            );
            // 3. 调用 LWPAY 代收接口
            try {
                String payUrl = lwPayService.createPayment(
                        order,
                        "967",
                        null
                );
            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("下单成功");
        } catch (Exception e) {
            log.error("LWPAY 代收下单失败: orderId={}", orderId, e);
            return new FebsResponse().fail().message("支付通道异常: " + e.getMessage());
                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("LWPAY 代收下单失败: orderId={}", orderId, e);
                return new FebsResponse().fail().message("Payment channel exception: " + e.getMessage());
            }
        }
        return new FebsResponse().fail().message("Payment channel exception");
    }
    /**
@@ -254,4 +356,128 @@
        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")
    })
    @GetMapping(value = "/getShippingFee")
    public FebsResponse getShippingFee(@RequestParam String countryCode) {
        return countryDeliveryService.getShippingFeeByCountryCode(countryCode);
    }
}