| | |
| | | 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; |
| | |
| | | private final IApiMallOrderInfoService mallOrderInfoService; |
| | | private final IXcxPayService iXcxPayService; |
| | | private final LwPayService lwPayService; |
| | | private final BsPayService bsPayService; |
| | | private final IMallCountryDeliveryService countryDeliveryService; |
| | | |
| | | @ApiOperation(value = "创建订单--验证是否允许创建", notes = "创建订单--验证是否允许创建") |
| | |
| | | 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 支付 ==================== |
| | | |
| | | /** |
| | |
| | | result.put("orderNo", order.getOrderNo()); |
| | | result.put("amount", order.getAmount()); |
| | | result.put("payUrl", payUrl); |
| | | return new FebsResponse().success().data(result).message("下单成功"); |
| | | 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 "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") |