Compare commits

...

2 Commits

Author SHA1 Message Date
huk
83b4744fbd refactor(transaction): 重构分账逻辑
- 移除了不必要的账户信息获取方法
- 优化了分账规则的获取和校验逻辑
- 重构了分账计算逻辑,支持按不同手续费承担方式进行分账
- 新增了分账结果异步回调通知地址配置
2025-09-02 16:27:18 +08:00
huk
dd588457e0 feat(payment): 更新易支付回调URL地址为远程服务器地址 2025-09-02 14:31:40 +08:00
27 changed files with 815 additions and 360 deletions

View File

@ -262,4 +262,5 @@ easypay:
wechat-mchid: 804474446 wechat-mchid: 804474446
easypay-public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArgVerkGaSEQvZIOpLjeUoVpL0lSYLc04+txtPFtfm5r5XFbaNaf5Ahu0lziGEwWzrGONThSsnb3U9pqoY6BpqviN4h+Guw5oEdHr1T/eDkQD5urgQUaZA6lDoU9XC662r+0kpbKidvXIsK2CrShN+BF8HEJmRZuhglxh25OHWIWqQiUDjLZC+QJRZqUu9Uzy9RBBu7qa0f0xbqYl3hnYi+vH++SsyOavO2gUVQyKU5Kkt5ZJVpZFQvD3BXePgwJSpsvrjhj0hiYp2v6PScN9XHP1vXB4wtIYSFYwmVus1KkV/LfDzUm6zHjliHYTVl6lPMhveIVJlRIqInRZRHxg5QIDAQAB easypay-public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArgVerkGaSEQvZIOpLjeUoVpL0lSYLc04+txtPFtfm5r5XFbaNaf5Ahu0lziGEwWzrGONThSsnb3U9pqoY6BpqviN4h+Guw5oEdHr1T/eDkQD5urgQUaZA6lDoU9XC662r+0kpbKidvXIsK2CrShN+BF8HEJmRZuhglxh25OHWIWqQiUDjLZC+QJRZqUu9Uzy9RBBu7qa0f0xbqYl3hnYi+vH++SsyOavO2gUVQyKU5Kkt5ZJVpZFQvD3BXePgwJSpsvrjhj0hiYp2v6PScN9XHP1vXB4wtIYSFYwmVus1KkV/LfDzUm6zHjliHYTVl6lPMhveIVJlRIqInRZRHxg5QIDAQAB
merRsaPrivateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCc1mku1mtfTc1vZM9z3TkRMZ9SaKP+6MdoQhjDHB9vJUOuG2Jnegej4gtzcm8MVYomV1azJMtCbPq4PN9aHegH13JthvrUPR3nAKp8AUp9Fh47ded//snNyAf7C7o8xKN5e7n89ROwacCAL2QDno76ngXzQQVj1TxY9pBdekZ03ezuDk1sv/u4FBlW8kRyaVdqyl4FdvP+EEljDe8gGxRikLyb3cK1B6G7w2BXgM/svft5SOoiqStx2XvgHRrFRB5w96TdoKNH9yxHU2clMJiz+5cej1VL6OHZspuY3cnPyJtyS+weYjDT+COLLBUe9UwbOV15DXDNpBLd34W4GiqtAgMBAAECggEAYaVwmVOwSAblp7wJGScb16OggStbJ2MAe93jEt7Yh1eZGrY7/xbP2O3smTUfBHvhZlusRB7dWf8F3l0v5iiGhRNTT/PhCPEARAl7G3emS9jQe869kkgslq06ose7bQg0i3dH5cEkQAqnameXClXWyRxHshrCY1SonO7uFPUDFtD2Z9GHu6dOWkOHfX7UETHV5/NTSqvner5M2YNsV/5To87zrmaeeNyvu91JgJSYofTD8IcMyhxcswxB9F3ECP7nulLDdCV+9mvE9zO0i4mPBxYb++87J0pu35TN9OY+gpiCK3Ed0gDqvvkTiCQEFu6y7OiqsQwy03CE5hAwWZR2YQKBgQDdb6e7+FWJSr9fIknfvZPPtKmbEkMHP1RYnVo3pmuVm14Z3UmostiSBRPMfVjeOX0N82SONM9P+1v9SDgSK3pL8rlk9Hx6D5KI7uj2IMtTVt00pO31KtxDLmXfgapDbWHK1RGEn1PDrnKYy5yOKJ8n0Wgp4lVEPIBN6uIud0A9iQKBgQC1UXdhEXFETyqEYWb9OnWbBjiVIw3D2/qW5c+WQGsHYdMEImC/0oTWIH9fKd4Azf26JjZ9nQvs5VcijJP6BXbGPqir5NgGVkwCj33PoNg4dDgIVE4BYaSp//7B6jCi5QiRTzCSOmUSkZ5L1Kz4SNEDe9r0MpLhxrsAzmR9aEJ/BQKBgHI71Ks63F2cSwd39+ZNtYA0cj7Gd/+4IvooCs+kseGXKj9rkkFOKj2CEwmuLHdP7vyQcHKQOdbIFFegtxRgi5G8oPm8yq5pdC3iGhpHJr1SlYFACGYu+zxJJlLcYIqyVf2+V3A0hZDwYLwEZjpMKHbxJ6xbz6MJFyObJZ3U9TYJAoGANXoGjJF5Z501u/+CQZN5VjSagZnqGGcL3G+BLx5msrGua9y7zjeHyCOjjWyqtnAKsllM3vVvq/nkHiN6DVaJNmUKmFARSqUvG944TAFzZAsa75H1w8CJsT34ZDbvC0wjn7/MYoRohPZ/ynu6XCwVwUJJTJaR7ZcQVmeJCdezLQUCgYEAszix6V9oL80Clb1nMwIly6I78+pcFKyk82yXxvySb6XPJvgoasbz9xYIeKlPyy6r8Aj/ujz5GVtDtkE6n6bsjekCdnUKUY3uTvFAX+YGQkuZnaPoisELJFI2Hal22tNnJyCOYh//AGAiHYpHNUKD4hsKBb45MhK1xwvTHpuLkdc= merRsaPrivateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCc1mku1mtfTc1vZM9z3TkRMZ9SaKP+6MdoQhjDHB9vJUOuG2Jnegej4gtzcm8MVYomV1azJMtCbPq4PN9aHegH13JthvrUPR3nAKp8AUp9Fh47ded//snNyAf7C7o8xKN5e7n89ROwacCAL2QDno76ngXzQQVj1TxY9pBdekZ03ezuDk1sv/u4FBlW8kRyaVdqyl4FdvP+EEljDe8gGxRikLyb3cK1B6G7w2BXgM/svft5SOoiqStx2XvgHRrFRB5w96TdoKNH9yxHU2clMJiz+5cej1VL6OHZspuY3cnPyJtyS+weYjDT+COLLBUe9UwbOV15DXDNpBLd34W4GiqtAgMBAAECggEAYaVwmVOwSAblp7wJGScb16OggStbJ2MAe93jEt7Yh1eZGrY7/xbP2O3smTUfBHvhZlusRB7dWf8F3l0v5iiGhRNTT/PhCPEARAl7G3emS9jQe869kkgslq06ose7bQg0i3dH5cEkQAqnameXClXWyRxHshrCY1SonO7uFPUDFtD2Z9GHu6dOWkOHfX7UETHV5/NTSqvner5M2YNsV/5To87zrmaeeNyvu91JgJSYofTD8IcMyhxcswxB9F3ECP7nulLDdCV+9mvE9zO0i4mPBxYb++87J0pu35TN9OY+gpiCK3Ed0gDqvvkTiCQEFu6y7OiqsQwy03CE5hAwWZR2YQKBgQDdb6e7+FWJSr9fIknfvZPPtKmbEkMHP1RYnVo3pmuVm14Z3UmostiSBRPMfVjeOX0N82SONM9P+1v9SDgSK3pL8rlk9Hx6D5KI7uj2IMtTVt00pO31KtxDLmXfgapDbWHK1RGEn1PDrnKYy5yOKJ8n0Wgp4lVEPIBN6uIud0A9iQKBgQC1UXdhEXFETyqEYWb9OnWbBjiVIw3D2/qW5c+WQGsHYdMEImC/0oTWIH9fKd4Azf26JjZ9nQvs5VcijJP6BXbGPqir5NgGVkwCj33PoNg4dDgIVE4BYaSp//7B6jCi5QiRTzCSOmUSkZ5L1Kz4SNEDe9r0MpLhxrsAzmR9aEJ/BQKBgHI71Ks63F2cSwd39+ZNtYA0cj7Gd/+4IvooCs+kseGXKj9rkkFOKj2CEwmuLHdP7vyQcHKQOdbIFFegtxRgi5G8oPm8yq5pdC3iGhpHJr1SlYFACGYu+zxJJlLcYIqyVf2+V3A0hZDwYLwEZjpMKHbxJ6xbz6MJFyObJZ3U9TYJAoGANXoGjJF5Z501u/+CQZN5VjSagZnqGGcL3G+BLx5msrGua9y7zjeHyCOjjWyqtnAKsllM3vVvq/nkHiN6DVaJNmUKmFARSqUvG944TAFzZAsa75H1w8CJsT34ZDbvC0wjn7/MYoRohPZ/ynu6XCwVwUJJTJaR7ZcQVmeJCdezLQUCgYEAszix6V9oL80Clb1nMwIly6I78+pcFKyk82yXxvySb6XPJvgoasbz9xYIeKlPyy6r8Aj/ujz5GVtDtkE6n6bsjekCdnUKUY3uTvFAX+YGQkuZnaPoisELJFI2Hal22tNnJyCOYh//AGAiHYpHNUKD4hsKBb45MhK1xwvTHpuLkdc=
backUrl: http://192.168.1.71:8080/easypay/trade/callback trade-backUrl: http://82.156.121.2:8880/trans/easypay/trade/callback
separate-backUrl: http://82.156.121.2:8880/trans/easypay/separate/callback

View File

@ -31,7 +31,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author zcc * @author zcc
* @date 2022-12-01 * @date 2022-12-01
*/ */
//@Tag(name ="订单接口列表")
@RestController @RestController
@RequestMapping("/oms/order") @RequestMapping("/oms/order")
@Slf4j @Slf4j
@ -69,7 +68,11 @@ public class OrderController extends BaseController {
return R.ok(util.writeExcel(convert.toVO(list), "订单列表数据")); return R.ok(util.writeExcel(convert.toVO(list), "订单列表数据"));
} }
@Tag(name ="获取订单表详细信息") /**
* 获取订单详细信息
* @param id 订单id
* @return
*/
@GetMapping(value = "/{id}") @GetMapping(value = "/{id}")
public R<ManagerOrderDetailVO> getInfo(@PathVariable("id") Long id) { public R<ManagerOrderDetailVO> getInfo(@PathVariable("id") Long id) {
return R.ok(orderService.selectById(id)); return R.ok(orderService.selectById(id));

View File

@ -40,8 +40,13 @@ public class EasypayConfig {
private String merRsaPrivateKey; private String merRsaPrivateKey;
/** /**
* 易生异步回调通知地址 * 支付结果异步回调通知地址
*/ */
private String backUrl; private String tradeBackUrl;
/**
* 分账结果异步回调通知地址
*/
private String separateBackUrl;
} }

View File

@ -2,23 +2,16 @@ package com.wzj.soopin.transaction.controller;
import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.annotation.SaIgnore;
import com.wzj.soopin.transaction.domain.bo.PaymentBO; import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO;
import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest; import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse; import com.wzj.soopin.transaction.domain.vo.EasypayTransResultVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO; import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO;
import com.wzj.soopin.transaction.service.IEasypayService; import com.wzj.soopin.transaction.service.IEasypayService;
import com.wzj.soopin.transaction.service.impl.WxAuthService;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.rmi.ServerException; import java.rmi.ServerException;
@ -61,6 +54,7 @@ public class TransEasypayController {
*/ */
@Log(title = "易生支付-发起支付", businessType = BusinessType.OTHER) @Log(title = "易生支付-发起支付", businessType = BusinessType.OTHER)
@PostMapping("/trade") @PostMapping("/trade")
@SaIgnore
public R<EasypayPrePayVO> trade(@RequestBody PaymentBO paymentBO) throws ServerException { public R<EasypayPrePayVO> trade(@RequestBody PaymentBO paymentBO) throws ServerException {
EasypayPrePayVO easypayPrePayVO = easypayService.payment(paymentBO); EasypayPrePayVO easypayPrePayVO = easypayService.payment(paymentBO);
return R.ok(easypayPrePayVO); return R.ok(easypayPrePayVO);
@ -73,9 +67,10 @@ public class TransEasypayController {
* @return * @return
*/ */
@GetMapping("/paymentQuery/{orderId}") @GetMapping("/paymentQuery/{orderId}")
public R<EasypayPaymentResultVO> paymentQuery(@PathVariable("orderId") Long orderId) throws ServerException { @SaIgnore
EasypayPaymentResultVO easypayPaymentResultVO = easypayService.paymentQuery(orderId); public R<EasypayTransResultVO> paymentQuery(@PathVariable("orderId") Long orderId) throws ServerException {
return R.ok(easypayPaymentResultVO); EasypayTransResultVO easypayTransResultVO = easypayService.paymentQuery(orderId);
return R.ok(easypayTransResultVO);
} }
/** /**
@ -86,8 +81,43 @@ public class TransEasypayController {
*/ */
@Log(title = "易生支付-实时退款", businessType = BusinessType.OTHER) @Log(title = "易生支付-实时退款", businessType = BusinessType.OTHER)
@PostMapping("/refund/{orderId}") @PostMapping("/refund/{orderId}")
public R<EasypayPrePayVO> refund(@PathVariable("orderId") Long orderId) throws ServerException { @SaIgnore
public R refund(@PathVariable("orderId") Long orderId) throws ServerException {
easypayService.refund(orderId); easypayService.refund(orderId);
return R.ok(); return R.ok();
} }
/**
* 查询退款结果
*
* @param orderId 订单id
* @return
*/
@GetMapping("/refundQuery/{orderId}")
@SaIgnore
public R<EasypayTransResultVO> refundQuery(@PathVariable("orderId") Long orderId) throws ServerException {
EasypayTransResultVO easypayTransResultVO = easypayService.refundQuery(orderId);
return R.ok(easypayTransResultVO);
}
/**
* 处理易生分账结果通知回调 处理
*
* @param easyPayRequest
* @return
*/
@SaIgnore
@Log(title = "易生支付-分账结果通知回调", businessType = BusinessType.UPDATE)
@PostMapping("/separate/callback")
public Map separateCallback(@RequestBody EasyPayRequest easyPayRequest) {
easypayService.handleSeparateCallback(easyPayRequest);
HashMap<Object, Object> map = new HashMap<>();
map.put("code", "000000");
map.put("msg", "Success");
return map;
}
} }

View File

@ -0,0 +1,56 @@
package com.wzj.soopin.transaction.domain.bo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.List;
/**
* 分账请求参数
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SeparateApplyBO {
/**
* 订单id
*/
private Long orderId;
/**
* 订单id
*/
private Long payId;
/**
* 支付完成时间
*/
private Date transDate;
/**
* 分账订单总金额.单位分
*/
private Long transSumAmt;
/**
* 分账订单总笔数
*/
private Long transSumCount;
/**
* 分账总单流水号
*/
private String separateBatchTrace;
/**
* 分账项
*/
private List<SeparateItemBO> separateItemBOList;
}

View File

@ -0,0 +1,64 @@
package com.wzj.soopin.transaction.domain.bo;
import com.wzj.soopin.transaction.enums.easypay.SepaStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 分账项
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SeparateItemBO {
/**
* 分账子单流水号
*/
private String separateTrade;
/**
* 分账收款方商户号
*/
private String receiveMchtCode;
/**
* 分账金额,单位分
*/
private Integer sepaTransAmount;
/**
* 手续费对应本金(原交易订单)金额
* 当前分账接收方拟承担的手续费本金金额金额和比例2选1必填
* 当前收款方不承担则送0
* 当前收款方全额承担则送原订单金额
* 当前收款方仅承担自己接收金额的手续费则送分账金额
*/
private Integer sepaFeeAmount;
// =============== 分账结果参数 ==============
/**
* 分账单状态
*/
private SepaStatus sepaStatus;
/**
* 分账平台应结金额单位分
*/
private Long sepaPlatStlmAmount;
/**
* 分账实际结算金额单位分
*/
private Long sepaStlmAmount;
}

View File

@ -75,10 +75,6 @@ public class SeparateInfo {
* 分账比例分账金额和分账比例2选一必填30代表30%仅可上送整数 * 分账比例分账金额和分账比例2选一必填30代表30%仅可上送整数
*/ */
private Long sepaRatio; private Long sepaRatio;
/**
* 分账金额分账金额和分账比例2选一必填单位分
*/
private Long sepaTransAmount;
/** /**
* 分账订单描述 * 分账订单描述
*/ */

View File

@ -1,8 +1,12 @@
package com.wzj.soopin.transaction.domain.entity; package com.wzj.soopin.transaction.domain.entity;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor
@NoArgsConstructor
public class WxAuthResponse { public class WxAuthResponse {
private String openid; private String openid;
private String session_key; private String session_key;

View File

@ -85,9 +85,9 @@ public class DivideDetail extends BaseEntity {
@ExcelProperty(value = "状态", order = 5) @ExcelProperty(value = "状态", order = 5)
private Integer status; private Integer status;
@Schema(description = "账户id") @Schema(description = "账户code")
@ExcelProperty(value = "账户id", order = 6) @ExcelProperty(value = "账户code", order = 6)
private Long accountId; private String accountCode;
/** /**
* 账户名称 * 账户名称
* */ * */

View File

@ -4,6 +4,7 @@ package com.wzj.soopin.transaction.domain.po;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.wzj.soopin.transaction.enums.DivideRuleFeeType;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.common.mybatis.core.domain.BaseEntity;
@ -46,6 +47,13 @@ public class DivideRule extends BaseEntity {
private Long regionId; private Long regionId;
/**
* 手续费承担方式类型见 {@link DivideRuleFeeType}
*/
@Schema(description = "手续费承担方式")
private Integer feeType;
/** /**
* 状态 * 状态
*/ */

View File

@ -83,11 +83,11 @@ public class PayOrder extends BaseAudit {
private String unTrace; private String unTrace;
/** /**
* 是否支付完成 * 交易状态 0->待支付,1->支付中,2->已支付,3->退款中,4->已退款
*/ */
@TableField(value = "trans_over") @TableField(value = "trans_state")
@Schema(description = "是否支付完成") @Schema(description = "交易状态 0->待支付,1->支付中,2->已支付,3->退款中,4->已退款")
private Boolean transOver; private Integer transState;
@TableField(value = "del_flag") @TableField(value = "del_flag")
private String delFlag; private String delFlag;

View File

@ -74,9 +74,9 @@ public class DivideDetailVO extends BaseEntity {
private Integer type; private Integer type;
@Schema(description = "账户id") @Schema(description = "账户code")
@ExcelProperty(value = "账户id", order = 6) @ExcelProperty(value = "账户code", order = 6)
private Long accountId; private String accountCode;
/** /**
* 账户名称 * 账户名称
* */ * */

View File

@ -2,6 +2,7 @@ package com.wzj.soopin.transaction.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.wzj.soopin.transaction.enums.DivideRuleFeeType;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.common.mybatis.core.domain.BaseEntity;
@ -56,6 +57,12 @@ public class DivideRuleVO extends BaseEntity {
@Schema(description = "区域id") @Schema(description = "区域id")
private Long regionId; private Long regionId;
/**
* 手续费承担方式类型见 {@link DivideRuleFeeType}
*/
@Schema(description = "手续费承担方式")
private Integer feeType;
/** /**
* 类型 * 类型
*/ */

View File

@ -14,7 +14,7 @@ import java.math.BigDecimal;
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class EasypayPaymentResultVO { public class EasypayTransResultVO {
/** /**
* 订单id * 订单id
@ -27,9 +27,9 @@ public class EasypayPaymentResultVO {
public Long payId; public Long payId;
/** /**
* 支付是否完成 * 交易状态 0->待支付,1->已支付,2->退款中,3->已退款
*/ */
public boolean paymentComplete; public int transState;
/** /**
* 订单总金额 * 订单总金额

View File

@ -1,5 +1,13 @@
package com.wzj.soopin.transaction.enums; package com.wzj.soopin.transaction.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 分账规则的商户类型
*/
@Getter
@AllArgsConstructor
public enum DivideRuleDetailType { public enum DivideRuleDetailType {
@ -15,25 +23,14 @@ public enum DivideRuleDetailType {
* 代理 * 代理
*/ */
PROXY(3, "代理"), PROXY(3, "代理"),
/**
* 达人
*/
REFERENCE(4, "达人"),;
REFERENCE(4, "推广"),; private final int value;
private int value; private final String desc;
private String desc;
DivideRuleDetailType(int value, String desc) {
this.value = value;
this.desc = desc;
}
public int getValue() {
return value;
}
public String getDesc() {
return desc;
}
public static DivideRuleDetailType getEnum(int value) { public static DivideRuleDetailType getEnum(int value) {
for (DivideRuleDetailType type : DivideRuleDetailType.values()) { for (DivideRuleDetailType type : DivideRuleDetailType.values()) {

View File

@ -1,33 +1,44 @@
package com.wzj.soopin.transaction.enums; package com.wzj.soopin.transaction.enums;
import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import org.dromara.common.core.exception.ServiceException;
import java.util.Arrays;
@Getter @Getter
@AllArgsConstructor
public enum DivideRuleFeeType { public enum DivideRuleFeeType {
/** /**
* 按比例分配 * 按比例分配
*/ */
RATE(1, "按比例分配"), RATE(0, "按比例分配"),
/** /**
* 商家承担 * 商家承担
*/ */
SELLER(2, "商家承担"), SELLER(1, "商家承担"),
/** /**
* 平台承担 * 平台承担
*/ */
PLATFORM(3, "平台承担"), PLATFORM(2, "平台承担"),
/**
* 平台代理承担
*/
PROXY(3, "代理承担"),
/**
PROXY(4, "平台代理承担"); * 平台代理承担
*/
REFERENCE(4, "达人承担");
private String desc;
private int value; private int value;
DivideRuleFeeType(int value, String desc) { private String desc;
this.value = value;
this.desc = desc; public static DivideRuleFeeType getByValue(int value) {
return Arrays.stream(values()).filter(e -> e.value == value).findFirst().orElseThrow(() -> new ServiceException("手续费分配方式" + value + "不存在"));
} }
} }

View File

@ -0,0 +1,29 @@
package com.wzj.soopin.transaction.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 支付单交易状态
*/
@Getter
@AllArgsConstructor
public enum TransState {
PENDING(0, "待支付"),
Payment(1, "支付中"),
PAID(2, "已支付"),
REFUND_PENDING(3, "退款中"),
REFUNDED(4, "已退款");
private final int code;
private final String desc;
}

View File

@ -1,27 +1,42 @@
package com.wzj.soopin.transaction.enums.easypay; package com.wzj.soopin.transaction.enums.easypay;
import java.io.IOException; import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.exception.ServiceException;
import java.util.Arrays;
/** /**
* 分账单状态处理中成功失败已退款 * 分账单状态处理中成功失败已退款
*/ */
@Getter
@AllArgsConstructor
public enum SepaStatus { public enum SepaStatus {
PENDING, REFUNDED, SUCCESS;
public String toValue() { SUCCESS("SUCCESS", "成功"),
return switch (this) { FAIL("FAIL", "失败"),
case PENDING -> "PENDING"; PENDING("PENDING", "处理中"),
case REFUNDED -> "REFUNDED"; PROCESSING("PROCESSING", "处理中"),
case SUCCESS -> "SUCCESS"; REFUNDED("REFUNDED", "已退款");
};
}
/**
* 状态code
*/
private final String value;
/**
* 状态描述
*/
private final String info;
public static SepaStatus getByValue(String value) {
return Arrays.stream(SepaStatus.values())
.filter(e -> StrUtil.equals(e.getValue(), value))
.findFirst()
.orElseThrow(() -> new ServiceException("分账状态不存在"));
public static SepaStatus forValue(String value) throws IOException {
return switch (value) {
case "PENDING" -> PENDING;
case "REFUNDED" -> REFUNDED;
case "SUCCESS" -> SUCCESS;
default -> throw new IOException("Cannot deserialize SepaStatus");
};
} }
} }

View File

@ -2,6 +2,7 @@ package com.wzj.soopin.transaction.mapper;
import com.wzj.soopin.transaction.domain.po.DivideDetail; import com.wzj.soopin.transaction.domain.po.DivideDetail;
import com.wzj.soopin.transaction.domain.vo.DivideDetailVO; import com.wzj.soopin.transaction.domain.vo.DivideDetailVO;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/** /**
@ -9,6 +10,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
* *
* @author zcc * @author zcc
*/ */
@Mapper
public interface DivideDetailMapper extends BaseMapperPlus<DivideDetail, DivideDetailVO> { public interface DivideDetailMapper extends BaseMapperPlus<DivideDetail, DivideDetailVO> {
} }

View File

@ -3,13 +3,20 @@ package com.wzj.soopin.transaction.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wzj.soopin.transaction.domain.po.Divide; import com.wzj.soopin.transaction.domain.po.Divide;
import com.wzj.soopin.transaction.domain.vo.DivideVO; import com.wzj.soopin.transaction.domain.vo.DivideVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/** /**
* 意见反馈Mapper接口 * 意见反馈Mapper接口
* *
* @author zcc * @author zcc
*/ */
public interface DivideMapper extends BaseMapper <Divide> { @Mapper
public interface DivideMapper extends BaseMapper<Divide> {
DivideVO getVOById( Long divideId); DivideVO getVOById(Long divideId);
@Select("select * from trans_divide where order_id = #{orderId}")
Divide selectByOrderId(@Param("orderId") Long orderId);
} }

View File

@ -3,8 +3,6 @@ package com.wzj.soopin.transaction.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wzj.soopin.transaction.domain.po.PayOrder; import com.wzj.soopin.transaction.domain.po.PayOrder;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@Mapper @Mapper
public interface PayOrderMapper extends BaseMapper<PayOrder> { public interface PayOrderMapper extends BaseMapper<PayOrder> {

View File

@ -2,9 +2,10 @@ package com.wzj.soopin.transaction.service;
import com.wzj.soopin.transaction.domain.bo.PaymentBO; import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO;
import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest; import com.wzj.soopin.transaction.domain.bo.easypay.EasyPayRequest;
import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO; import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO; import com.wzj.soopin.transaction.domain.vo.EasypayTransResultVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO; import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -24,7 +25,7 @@ public interface IEasypayService {
* @param orderId * @param orderId
* @return * @return
*/ */
EasypayPaymentResultVO paymentQuery(Long orderId) throws ServerException; EasypayTransResultVO paymentQuery(Long orderId) throws ServerException;
/** /**
* 处理易生支付结果通知回调 * 处理易生支付结果通知回调
@ -38,6 +39,27 @@ public interface IEasypayService {
*/ */
void refund(Long orderId); void refund(Long orderId);
/**
* 退款查询
* @param orderId
* @return
*/
EasypayTransResultVO refundQuery(Long orderId);
/**
* 发起分账
* @param separateApplyBO
* @return
*/
SeparateApplyBO separateApply(SeparateApplyBO separateApplyBO);
/**
* 处理易生分账结果通知回调
* @param easyPayRequest
*/
void handleSeparateCallback(EasyPayRequest easyPayRequest);
/** /**
* 获取易生账户 * 获取易生账户
* @param memberId * @param memberId

View File

@ -1,5 +1,6 @@
package com.wzj.soopin.transaction.service.impl; package com.wzj.soopin.transaction.service.impl;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.transaction.convert.DivideRuleConvert; import com.wzj.soopin.transaction.convert.DivideRuleConvert;
@ -104,15 +105,9 @@ public class DivideRuleServiceImpl extends ServiceImpl<DivideRuleMapper, DivideR
List<DivideRule> divideRuleList = baseMapper.selectList(new QueryWrapper<DivideRule>().lambda() List<DivideRule> divideRuleList = baseMapper.selectList(new QueryWrapper<DivideRule>().lambda()
.eq(DivideRule::getType,orderType) .eq(DivideRule::getType,orderType)
.eq(DivideRule::getStatus,DivideRuleStatus.ON.getCode())); .eq(DivideRule::getStatus,DivideRuleStatus.ON.getCode()));
if (CollectionUtils.isEmpty(divideRuleList)) { Assert.notEmpty(divideRuleList, () -> new ServiceException("未找到品类的分账规则"));
return null; Assert.isTrue(divideRuleList.size() == 1, () -> new ServiceException("品类分账规则不唯一"));
}
if(divideRuleList.size()>1){
throw new ServiceException("分账规则不唯一");
}
DivideRule divideRule = divideRuleList.get(0); DivideRule divideRule = divideRuleList.get(0);
List<DivideRuleDetail> detailList = detailMapper.selectList(new QueryWrapper<DivideRuleDetail>().lambda() List<DivideRuleDetail> detailList = detailMapper.selectList(new QueryWrapper<DivideRuleDetail>().lambda()
.eq(DivideRuleDetail::getRuleId, divideRule.getId())); .eq(DivideRuleDetail::getRuleId, divideRule.getId()));
DivideRuleVO vo = convert.toVO(divideRule); DivideRuleVO vo = convert.toVO(divideRule);

View File

@ -1,30 +1,35 @@
package com.wzj.soopin.transaction.service.impl; package com.wzj.soopin.transaction.service.impl;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.member.domain.po.Member;
import com.wzj.soopin.member.domain.po.MemberAccount;
import com.wzj.soopin.member.service.IMemberAccountService; import com.wzj.soopin.member.service.IMemberAccountService;
import com.wzj.soopin.member.service.IMemberService; import com.wzj.soopin.member.service.IMemberService;
import com.wzj.soopin.order.domain.entity.Order; import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.service.OrderItemService; import com.wzj.soopin.order.service.OrderItemService;
import com.wzj.soopin.order.service.OrderService; import com.wzj.soopin.order.service.OrderService;
import com.wzj.soopin.order.utils.StringUtils;
import com.wzj.soopin.transaction.convert.DivideConvert; import com.wzj.soopin.transaction.convert.DivideConvert;
import com.wzj.soopin.transaction.convert.DivideDetailConvert; import com.wzj.soopin.transaction.convert.DivideDetailConvert;
import com.wzj.soopin.transaction.domain.bo.DivideBO; import com.wzj.soopin.transaction.domain.bo.DivideBO;
import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO;
import com.wzj.soopin.transaction.domain.bo.SeparateItemBO;
import com.wzj.soopin.transaction.domain.po.Divide; import com.wzj.soopin.transaction.domain.po.Divide;
import com.wzj.soopin.transaction.domain.po.DivideDetail; import com.wzj.soopin.transaction.domain.po.DivideDetail;
import com.wzj.soopin.transaction.domain.po.PayOrder;
import com.wzj.soopin.transaction.domain.vo.DivideRuleDetailVO; import com.wzj.soopin.transaction.domain.vo.DivideRuleDetailVO;
import com.wzj.soopin.transaction.domain.vo.DivideRuleVO; import com.wzj.soopin.transaction.domain.vo.DivideRuleVO;
import com.wzj.soopin.transaction.domain.vo.DivideVO; import com.wzj.soopin.transaction.domain.vo.DivideVO;
import com.wzj.soopin.transaction.enums.DivideRuleDetailType; import com.wzj.soopin.transaction.enums.DivideRuleDetailType;
import com.wzj.soopin.transaction.enums.DivideRuleFeeType;
import com.wzj.soopin.transaction.enums.DivideStatus; import com.wzj.soopin.transaction.enums.DivideStatus;
import com.wzj.soopin.transaction.enums.TransState;
import com.wzj.soopin.transaction.mapper.DivideDetailMapper; import com.wzj.soopin.transaction.mapper.DivideDetailMapper;
import com.wzj.soopin.transaction.mapper.DivideMapper; import com.wzj.soopin.transaction.mapper.DivideMapper;
import com.wzj.soopin.transaction.service.IDivideRuleService; import com.wzj.soopin.transaction.service.IDivideRuleService;
import com.wzj.soopin.transaction.service.IDivideService; import com.wzj.soopin.transaction.service.IDivideService;
import com.wzj.soopin.transaction.service.IEasypayService; import com.wzj.soopin.transaction.service.IEasypayService;
import com.wzj.soopin.transaction.service.PayOrderService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
@ -36,9 +41,8 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.HashMap; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 分账服务实现类 * 分账服务实现类
@ -56,23 +60,17 @@ public class DivideServiceImpl extends ServiceImpl<DivideMapper, Divide> impleme
private final DivideConvert divideConvert; private final DivideConvert divideConvert;
private final OrderService orderService; private final OrderService orderService;
private final PayOrderService payOrderService;
private final IDivideRuleService ruleService; private final IDivideRuleService ruleService;
private final OrderItemService orderItemService;
private final ConfigService configService;
private final IEasypayService easypayService; private final IEasypayService easypayService;
private final IMemberAccountService accountService;
private final ISysTenantService sysTenantService; private final ISysTenantService sysTenantService;
private final IMemberService memberService;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -138,177 +136,270 @@ public class DivideServiceImpl extends ServiceImpl<DivideMapper, Divide> impleme
return baseMapper.selectOne(new QueryWrapper<Divide>().lambda().eq(Divide::getOrderSn, orderNo)); return baseMapper.selectOne(new QueryWrapper<Divide>().lambda().eq(Divide::getOrderSn, orderNo));
} }
/**
* 订单金额分账
* 商家先从订单总金额中分走租户中配置的比例后剩余的按分账规则分给平台和推广等账户
*
* @param orderId
* @return
*/
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean divide(Long orderId) { public boolean divide(Long orderId) {
//获取订单信息 //获取订单信息
Order order = orderService.getById(orderId); Order order = orderService.getById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在"));
if (order == null) { Assert.isTrue(order.getStatus() == 3, () -> new ServiceException("订单未支付"));
throw new ServiceException("订单不存在"); PayOrder payOrder = payOrderService.getById(order.getPayId());
} Assert.notNull(payOrder, () -> new ServiceException("支付单不存在"));
if (order.getStatus() != 3) { //订单状态为已完成 Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode(), () -> new ServiceException("订单未支付"));
throw new ServiceException("订单状态不正确"); Assert.isFalse(this.exists(Wrappers.lambdaQuery(Divide.class).eq(Divide::getOrderId, orderId)), () -> new ServiceException("已存在分账记录,无需再次分账"));
}
Divide divide = this.getById(orderId);
if (divide != null) {
throw new ServiceException("已存在分账记录,无需再次分账");
}
//查找分账规则 //查找分账规则
DivideRuleVO rule = ruleService.getVOByOrderType(order.getType()); DivideRuleVO rule = ruleService.getVOByOrderType(order.getType());
if (rule == null) { Integer feeType = rule.getFeeType();
throw new ServiceException("未找到分账规则"); DivideRuleFeeType divideRuleFeeType = DivideRuleFeeType.getByValue(feeType);
} List<DivideRuleDetailVO> details = rule.getDetails();
//开始分账 checkRule(details, divideRuleFeeType);
//根据易生支付的合同查看手续费金额 //根据易生支付的合同查看手续费金额
BigDecimal totalAmount = order.getTotalAmount(); BigDecimal totalAmount = order.getTotalAmount();
BigDecimal totalFee = new BigDecimal(0);
String feeRate = configService.getConfigValue("transaction.divide.feeRate");
if (StringUtils.isNotBlank(feeRate)) {
totalFee = totalAmount.multiply(new BigDecimal(feeRate)).divide(new BigDecimal(1000), 2, BigDecimal.ROUND_HALF_UP);
}
BigDecimal actualMoney = totalAmount.subtract(totalFee);
//计算订单可分配金额,订单只考虑整单退不考虑单独退所以直接计算订单金额 //计算订单可分配金额,订单只考虑整单退不考虑单独退所以直接计算订单金额
//先生成主表信息 //先生成主表信息
divide = Divide.builder() Divide divide = Divide.builder()
.ruleId(rule.getId()) .ruleId(rule.getId())
.orderMoney(totalAmount) .orderMoney(totalAmount)
.fee(totalFee) // .fee(totalFee)
.actualMoney(actualMoney) // .actualMoney(actualMoney)
.orderSn(order.getOrderSn()) .orderSn(order.getOrderSn())
.status(DivideStatus.PENDING.getCode()) .status(DivideStatus.PENDING.getCode())
.build(); .build();
super.save(divide); super.save(divide);
List<DivideDetail> detailList = new ArrayList<>();
//计算商户的手续费 List<SeparateItemBO> separateItemBOList = new ArrayList<>();
// 从租户信息得到商户的分账比例,先分给商户
SysTenantVo tenant = sysTenantService.queryById(order.getTenantId()); SysTenantVo tenant = sysTenantService.queryById(order.getTenantId());
if (tenant == null) { DivideDetail sellerDivideDetail = divideSeller(totalAmount, divide.getId(), tenant, divideRuleFeeType, detailList, separateItemBOList);
throw new ServiceException("租户信息不存在"); detailList.add(sellerDivideDetail);
} // 商户分完后分给代理和达人最后剩余都给平台
BigDecimal sellerFee = new BigDecimal(0); rule.getDetails().stream()
BigDecimal platformFee = new BigDecimal(0); .filter(item -> item.getType() == DivideRuleDetailType.PROXY.getValue())
if (tenant.getBearFeeFlag() == 1) { .findFirst()
//如果承担手续费计算手续费 .ifPresent(ruleDetailVO -> {
sellerFee = totalFee; DivideDetail proxyDivideDetail = divideProxy(totalAmount, ruleDetailVO, divide.getId(), order.getTenantId(),divideRuleFeeType, detailList, separateItemBOList);
} else { });
platformFee = totalFee; rule.getDetails().stream()
} .filter(item -> item.getType() == DivideRuleDetailType.REFERENCE.getValue())
.findFirst()
.ifPresent(ruleDetailVO -> {
DivideDetail divideReference = divideReference(totalAmount, ruleDetailVO, divide.getId(), order.getMemberId(),divideRuleFeeType, detailList, separateItemBOList);
detailList.add(divideReference);
});
//生成商户的分账明细 rule.getDetails().stream()
BigDecimal sellerAmount = divideSeller(actualMoney, sellerFee, divide.getId(), tenant); .filter(item -> item.getType() == DivideRuleDetailType.PLATFORM.getValue())
.findFirst()
.ifPresent(ruleDetailVO -> {
BigDecimal platformAmount = actualMoney.subtract(sellerAmount); DivideDetail dividePlatform = dividePlatform(totalAmount, ruleDetailVO, divide.getId(),divideRuleFeeType, detailList, separateItemBOList);
BigDecimal finalAmount = new BigDecimal(0); detailList.add(dividePlatform);
//按规则进行分账 });
//先分代理 然后达人 最后的剩余都给平台
DivideRuleDetailVO divideRuleDetail = rule.getDetails().stream().filter(item -> item.getType() == DivideRuleDetailType.PROXY.getValue()).findFirst().orElse(null);
if ((divideRuleDetail != null)) {
finalAmount = platformAmount.subtract(divideproxy(platformAmount, divideRuleDetail, divide.getId(), order.getTenantId()));
}
divideRuleDetail = rule.getDetails().stream().filter(item -> item.getType() == DivideRuleDetailType.REFERENCE.getValue()).findFirst().orElse(null);
if ((divideRuleDetail != null)) {
finalAmount = finalAmount.subtract(divideDarren(platformAmount, divideRuleDetail, divide.getId(), order.getMemberId()));
}
dividePlatform(finalAmount, platformFee, divideRuleDetail, divide.getId(), tenant.getBearFeeFlag() == 1);
//同步订单状态 //同步订单状态
syncOrderStatus(order); syncOrderStatus(order);
//同步易生支付 // 调用易生支付实时分账
syncYisheng(null); SeparateApplyBO separateApplyBO = SeparateApplyBO.builder()
.orderId(order.getId())
.payId(order.getPayId())
.transDate(payOrder.getEndTransDate())
.transSumAmt(payOrder.getTransAmount())
.transSumCount((long) separateItemBOList.size())
.separateBatchTrace(String.valueOf(divide.getId()))
.separateItemBOList(separateItemBOList)
.build();
easypayService.separateApply(separateApplyBO);
return true; return true;
} }
private BigDecimal divideSeller(BigDecimal actualMoney, BigDecimal sellerFee, Long divideId, SysTenantVo tenant) { /**
//获取商户的账户信息 * 校验分账规则详情和规则中的手续费承担类型是否一致
Map<String, String> account = getSellerAccount(tenant.getId()); *
if (account == null) { * @param details
log.error("商户信息不存在,不参与分账"); * @param divideRuleFeeType
*/
private void checkRule(List<DivideRuleDetailVO> details, DivideRuleFeeType divideRuleFeeType) {
if (DivideRuleFeeType.SELLER == divideRuleFeeType) {
long count = details.stream().filter(item -> item.getType() == DivideRuleDetailType.SELLER.getValue()).count();
Assert.isTrue(count == 1, () -> new ServiceException("分账总规则中已配置商户承担手续费,但未配置商户分账规则详情"));
} }
if (DivideRuleFeeType.PLATFORM == divideRuleFeeType) {
long count = details.stream().filter(item -> item.getType() == DivideRuleDetailType.PLATFORM.getValue()).count();
Assert.isTrue(count == 1, () -> new ServiceException("分账总规则中已配置平台承担手续费,但未配置平台分账规则详情"));
}
if (DivideRuleFeeType.PROXY == divideRuleFeeType) {
long count = details.stream().filter(item -> item.getType() == DivideRuleDetailType.PROXY.getValue()).count();
Assert.isTrue(count == 1, () -> new ServiceException("分账总规则中已配置代理承担手续费,但未配置代理分账规则详情"));
}
if (DivideRuleFeeType.REFERENCE == divideRuleFeeType) {
long count = details.stream().filter(item -> item.getType() == DivideRuleDetailType.REFERENCE.getValue()).count();
Assert.isTrue(count == 1, () -> new ServiceException("分账总规则中已配置达人承担手续费,但未配置达人分账规则详情"));
}
}
/**
* 商户分账
*
* @param totalAmount 总金额
* @param divideId 分账总表id
* @param divideRuleFeeType
* @param tenant 商户所属租户
* @param detailList
* @param separateItemBOList
* @return
*/
private DivideDetail divideSeller(BigDecimal totalAmount, Long divideId, SysTenantVo tenant,
DivideRuleFeeType divideRuleFeeType, List<DivideDetail> detailList,
List<SeparateItemBO> separateItemBOList) {
//获取商户的账户信息
String mchtCode = getMchtCode(tenant.getId());
//计算商户的分账金额 //计算商户的分账金额
BigDecimal divideRate = tenant.getDivideRate(); BigDecimal divideRate = tenant.getDivideRate();
Integer bearFeeFlag = tenant.getBearFeeFlag();
//计算商户的分账金额 //计算商户的分账金额
BigDecimal sellerAmount = actualMoney.multiply(divideRate).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); BigDecimal sellerAmount = totalAmount.multiply(divideRate).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
//生成商户的分账明细 //生成商户的分账明细
DivideDetail sellerDetail = DivideDetail.builder() DivideDetail sellerDetail = DivideDetail.builder()
.divideId(divideId) .divideId(divideId)
.accountCode(mchtCode)
.money(sellerAmount) .money(sellerAmount)
.fee(sellerFee)
.feePercent(bearFeeFlag == 1 ? new BigDecimal(100) : new BigDecimal(0))
.moneyPercent(divideRate) .moneyPercent(divideRate)
.type(DivideRuleDetailType.SELLER.getValue()).build(); .type(DivideRuleDetailType.SELLER.getValue()).build();
detailMapper.insert(sellerDetail); detailMapper.insert(sellerDetail);
detailList.add(sellerDetail);
return sellerAmount; Integer sepaFeeAmount = 0;
if (divideRuleFeeType == DivideRuleFeeType.SELLER) {
sepaFeeAmount = totalAmount.multiply(BigDecimal.valueOf(100)).intValue();
} else if (divideRuleFeeType == DivideRuleFeeType.RATE) {
sepaFeeAmount = sellerAmount.multiply(BigDecimal.valueOf(100)).intValue();
}
separateItemBOList.add(SeparateItemBO.builder()
.separateTrade(String.valueOf(sellerDetail.getId()))
.receiveMchtCode(mchtCode)
.sepaTransAmount(sellerAmount.multiply(BigDecimal.valueOf(100)).intValue())
.sepaFeeAmount(sepaFeeAmount)
.build());
return sellerDetail;
} }
private BigDecimal divideproxy(BigDecimal platformMoney, DivideRuleDetailVO rule, Long divideId, Long tenantId) { /**
* 代理商分账
*
* @param totalAmount 总金额
* @param rule 代理商的分账规则详情
* @param divideId 分账总表id
* @param tenantId 代理商所属租户id
* @param divideRuleFeeType
* @param detailList
* @param separateItemBOList
* @return
*/
private DivideDetail divideProxy(BigDecimal totalAmount, DivideRuleDetailVO rule, Long divideId, Long tenantId, DivideRuleFeeType divideRuleFeeType, List<DivideDetail> detailList, List<SeparateItemBO> separateItemBOList) {
//获取代理人的账户信息 //获取代理人的账户信息
Map<String, String> account = getProxyAccount(tenantId); String mchtCode = getMchtCode(tenantId);
if (account == null) {
log.error("代理人信息不存在,不参与分账");
return new BigDecimal(0);
}
//计算代理人的分账金额 //计算代理人的分账金额
BigDecimal proxyAmount = platformMoney.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); BigDecimal proxyAmount = totalAmount.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
DivideDetail proxyDetail = DivideDetail.builder() DivideDetail proxyDetail = DivideDetail.builder()
.divideId(divideId) .divideId(divideId)
.accountCode(mchtCode)
.money(proxyAmount) .money(proxyAmount)
.fee(new BigDecimal(0))
.feePercent(new BigDecimal(0))
.moneyPercent(rule.getMoneyPercent()) .moneyPercent(rule.getMoneyPercent())
.type(DivideRuleDetailType.PROXY.getValue()) .type(DivideRuleDetailType.PROXY.getValue())
.build(); .build();
detailMapper.insert(proxyDetail); detailMapper.insert(proxyDetail);
return proxyAmount; detailList.add(proxyDetail);
Integer sepaFeeAmount = 0;
if (divideRuleFeeType == DivideRuleFeeType.PROXY) {
sepaFeeAmount = totalAmount.multiply(BigDecimal.valueOf(100)).intValue();
} else if (divideRuleFeeType == DivideRuleFeeType.RATE) {
sepaFeeAmount = proxyAmount.multiply(BigDecimal.valueOf(100)).intValue();
}
separateItemBOList.add(SeparateItemBO.builder()
.separateTrade(String.valueOf(proxyDetail.getId()))
.receiveMchtCode(mchtCode)
.sepaTransAmount(proxyAmount.multiply(BigDecimal.valueOf(100)).intValue())
.sepaFeeAmount(sepaFeeAmount)
.build());
return proxyDetail;
} }
private BigDecimal divideDarren(BigDecimal platformMoney, DivideRuleDetailVO rule, Long divideId, Long tenantId) { /**
* 达人分账
*
* @param totalAmount
* @param rule
* @param divideId
* @param tenantId
* @param divideRuleFeeType
* @param detailList
* @param separateItemBOList
* @return
*/
private DivideDetail divideReference(BigDecimal totalAmount, DivideRuleDetailVO rule, Long divideId, Long tenantId, DivideRuleFeeType divideRuleFeeType, List<DivideDetail> detailList, List<SeparateItemBO> separateItemBOList) {
//获取达人的账户信息 //获取达人的账户信息
Map<String, String> account = getReferenceAccount(tenantId); String mchtCode = getMchtCode(tenantId);
if (account == null) {
log.error("达人信息不存在,不参与分账");
return new BigDecimal(0);
}
//计算达人的分账金额 //计算达人的分账金额
BigDecimal proxyAmount = platformMoney.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); BigDecimal proxyAmount = totalAmount.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
DivideDetail proxyDetail = DivideDetail.builder() DivideDetail referenceDetail = DivideDetail.builder()
.divideId(divideId) .divideId(divideId)
.accountCode(mchtCode)
.money(proxyAmount) .money(proxyAmount)
.fee(new BigDecimal(0))
.feePercent(new BigDecimal(0))
.moneyPercent(rule.getMoneyPercent()) .moneyPercent(rule.getMoneyPercent())
.type(DivideRuleDetailType.REFERENCE.getValue()) .type(DivideRuleDetailType.REFERENCE.getValue())
.build(); .build();
detailMapper.insert(proxyDetail); detailMapper.insert(referenceDetail);
return proxyAmount; detailList.add(referenceDetail);
Integer sepaFeeAmount = 0;
if (divideRuleFeeType == DivideRuleFeeType.REFERENCE) {
sepaFeeAmount = totalAmount.multiply(BigDecimal.valueOf(100)).intValue();
} else if (divideRuleFeeType == DivideRuleFeeType.RATE) {
sepaFeeAmount = proxyAmount.multiply(BigDecimal.valueOf(100)).intValue();
}
separateItemBOList.add(SeparateItemBO.builder()
.separateTrade(String.valueOf(referenceDetail.getId()))
.receiveMchtCode(mchtCode)
.sepaTransAmount(proxyAmount.multiply(BigDecimal.valueOf(100)).intValue())
.sepaFeeAmount(sepaFeeAmount)
.build());
return referenceDetail;
} }
private void dividePlatform(BigDecimal platformMoney, BigDecimal fee, DivideRuleDetailVO rule, Long divideId, boolean feeFlag) { private DivideDetail dividePlatform(BigDecimal totalAmount, DivideRuleDetailVO rule, Long divideId, DivideRuleFeeType divideRuleFeeType, List<DivideDetail> detailList, List<SeparateItemBO> separateItemBOList) {
//获取平台的账户信息 //获取平台的账户信息
Map<String, String> account = getPlatformAccount(); String mchtCode = getMchtCode(1L);
if (account == null) {
log.error("平台信息不存在,不参与分账");
}
//计算平台的分账金额 //计算平台的分账金额
BigDecimal proxyAmount = platformMoney.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP); BigDecimal platformAmount = totalAmount.multiply(rule.getMoneyPercent()).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
DivideDetail proxyDetail = DivideDetail.builder() DivideDetail platformDetail = DivideDetail.builder()
.divideId(divideId) .divideId(divideId)
.money(proxyAmount) .accountCode(mchtCode)
.fee(fee) .money(platformAmount)
.feePercent(feeFlag ? new BigDecimal(100) : new BigDecimal(0))
.moneyPercent(rule.getMoneyPercent()) .moneyPercent(rule.getMoneyPercent())
.type(DivideRuleDetailType.REFERENCE.getValue()) .type(DivideRuleDetailType.PLATFORM.getValue())
.build(); .build();
detailMapper.insert(proxyDetail); detailMapper.insert(platformDetail);
detailList.add(platformDetail);
Integer sepaFeeAmount = 0;
if (divideRuleFeeType == DivideRuleFeeType.PLATFORM) {
sepaFeeAmount = totalAmount.multiply(BigDecimal.valueOf(100)).intValue();
} else if (divideRuleFeeType == DivideRuleFeeType.RATE) {
sepaFeeAmount = platformAmount.multiply(BigDecimal.valueOf(100)).intValue();
}
separateItemBOList.add(SeparateItemBO.builder()
.separateTrade(String.valueOf(platformDetail.getId()))
.receiveMchtCode(mchtCode)
.sepaTransAmount(platformAmount.multiply(BigDecimal.valueOf(100)).intValue())
.sepaFeeAmount(sepaFeeAmount)
.build());
return platformDetail;
} }
@Override @Override
@ -327,84 +418,22 @@ public class DivideServiceImpl extends ServiceImpl<DivideMapper, Divide> impleme
} }
private Map<String, String> getPlatformAccount() {
return new HashMap<>() {{
put("account", "123456");
put("name", "平台");
}};
}
/** /**
* 获取达人的账户信息 * 获取平台或商家或推广或达人在易生侧的商户号
* *
* @param memberId * @param tenantId
* @return * @return
*/ */
private Map<String, String> getReferenceAccount(Long memberId) { private String getMchtCode(Long tenantId) {
Member member = memberService.getById(memberId); SysTenantVo tenant = sysTenantService.queryById(tenantId);
if (member == null) { Assert.notNull(tenant, () -> new ServiceException("商户信息异常"));
return null; // todo 需要从租户信息中获得商家在易生侧的商户号
} String mchtCode = "";
//推广人 Assert.notBlank(mchtCode, () -> new ServiceException("未维护商户在易生侧的商户号信息"));
Long inviteUserId = member.getSpreadUid(); return mchtCode;
if (inviteUserId == null) {
return null;
}
MemberAccount account = accountService.getMemberAccount(inviteUserId);
if (account == null) {
return null;
}
return new HashMap<>() {{
put("accountId", account.getId() + "");
}};
}
/**
* 获取代理的账户信息
*
* @param
* @return
*/
private Map<String, String> getProxyAccount(Long memberId) {
if (memberId != null) {
return null;
}
MemberAccount account = accountService.getMemberAccount(memberId);
if (account == null) {
return null;
}
return new HashMap<>() {{
put("accountId", account.getId() + "");
}};
}
/**
* 获取商家的账户信息
*
* @param sellerId
* @return
*/
private Map<String, String> getSellerAccount(Long sellerId) {
SysTenantVo tenant = sysTenantService.queryById(sellerId);
if (tenant != null) {
return null;
}
MemberAccount account = accountService.getMemberAccount(tenant.getId());
// if (account == null)
//
// return null;
// }
return new HashMap<>() {{
put("accountId", account.getId() + "");
}};
} }
private void syncOrderStatus(Order order) { private void syncOrderStatus(Order order) {
} }
private void syncYisheng(List<DivideDetail> divideDetails) {
}
} }

View File

@ -18,17 +18,21 @@ import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tencentcloudapi.dlc.v20210125.models.Asset;
import com.wzj.soopin.order.domain.entity.Order; import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.emum.OrderStatusEnum; import com.wzj.soopin.order.emum.OrderStatusEnum;
import com.wzj.soopin.order.mapper.OrderMapper; import com.wzj.soopin.order.mapper.OrderMapper;
import com.wzj.soopin.transaction.config.EasypayConfig; import com.wzj.soopin.transaction.config.EasypayConfig;
import com.wzj.soopin.transaction.config.WechatMiniProgramConfig; import com.wzj.soopin.transaction.config.WechatMiniProgramConfig;
import com.wzj.soopin.transaction.domain.bo.PaymentBO; import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.transaction.domain.bo.SeparateApplyBO;
import com.wzj.soopin.transaction.domain.bo.easypay.*; import com.wzj.soopin.transaction.domain.bo.easypay.*;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundApplyReqBody; import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundApplyReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundReqOrderInfo; import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.req.RefundReqOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.resp.RefundApplyRespBody; import com.wzj.soopin.transaction.domain.bo.easypay.refund.apply.resp.RefundApplyRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.req.SeparateApplyReqBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.req.SeparateReqOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateApplyRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.separate.apply.resp.SeparateRespOrderInfo;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.req.*; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.req.*;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsApiRespBody; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsApiRespBody;
import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsapiRespOrderInfo; import com.wzj.soopin.transaction.domain.bo.easypay.trade.jsapi.resp.JsapiRespOrderInfo;
@ -39,34 +43,30 @@ import com.wzj.soopin.transaction.domain.bo.easypay.trade.query.resp.TradeQueryR
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse; import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.domain.po.PayOrder; import com.wzj.soopin.transaction.domain.po.PayOrder;
import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO; import com.wzj.soopin.transaction.domain.vo.EasypayAccountVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPaymentResultVO; import com.wzj.soopin.transaction.domain.vo.EasypayTransResultVO;
import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO; import com.wzj.soopin.transaction.domain.vo.EasypayPrePayVO;
import com.wzj.soopin.transaction.enums.TransState;
import com.wzj.soopin.transaction.enums.easypay.DelaySettleFlag; import com.wzj.soopin.transaction.enums.easypay.DelaySettleFlag;
import com.wzj.soopin.transaction.enums.easypay.PatnerSettleFlag; import com.wzj.soopin.transaction.enums.easypay.PatnerSettleFlag;
import com.wzj.soopin.transaction.enums.easypay.PayType; import com.wzj.soopin.transaction.enums.easypay.PayType;
import com.wzj.soopin.transaction.enums.easypay.SplitSettleFlag; import com.wzj.soopin.transaction.enums.easypay.SplitSettleFlag;
import com.wzj.soopin.transaction.mapper.DivideDetailMapper;
import com.wzj.soopin.transaction.mapper.DivideMapper; import com.wzj.soopin.transaction.mapper.DivideMapper;
import com.wzj.soopin.transaction.mapper.PayOrderMapper; import com.wzj.soopin.transaction.mapper.PayOrderMapper;
import com.wzj.soopin.transaction.service.IDivideService;
import com.wzj.soopin.transaction.service.IEasypayService; import com.wzj.soopin.transaction.service.IEasypayService;
import com.wzj.soopin.transaction.service.PayOrderService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.FormatsType; import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.ServletUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.rmi.ServerException; import java.rmi.ServerException;
import java.time.LocalDateTime; import java.util.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import static com.wzj.soopin.transaction.constans.EasypayConstants.*; import static com.wzj.soopin.transaction.constans.EasypayConstants.*;
@ -80,18 +80,17 @@ import static com.wzj.soopin.transaction.constans.EasypayConstants.*;
@Slf4j @Slf4j
public class EasypayServiceImpl implements IEasypayService { public class EasypayServiceImpl implements IEasypayService {
private EasypayConfig easypayConfig; private final EasypayConfig easypayConfig;
private WechatMiniProgramConfig wechatMiniProgramConfig; private final WechatMiniProgramConfig wechatMiniProgramConfig;
private WxAuthService wxAuthService; private final WxAuthService wxAuthService;
private final OrderMapper orderMapper; private final OrderMapper orderMapper;
private final DivideMapper divideMapper;
private final PayOrderMapper payOrderMapper; private final PayOrderMapper payOrderMapper;
/** /**
* 生成易生接口请求头参数 * 生成易生接口请求头参数
* *
@ -165,7 +164,7 @@ public class EasypayServiceImpl implements IEasypayService {
*/ */
private void verify(Object header, Object body, String signStr) { private void verify(Object header, Object body, String signStr) {
TreeMap<String, Object> hearMap = new TreeMap<>(JSONObject.from(header)); TreeMap<String, Object> hearMap = new TreeMap<>(JSONObject.from(header));
TreeMap<String, Object>bodyMap = new TreeMap<>(JSONObject.from(body)); TreeMap<String, Object> bodyMap = new TreeMap<>(JSONObject.from(body));
// 递归排序 // 递归排序
sortMap(hearMap); sortMap(hearMap);
sortMap(bodyMap); sortMap(bodyMap);
@ -211,27 +210,32 @@ public class EasypayServiceImpl implements IEasypayService {
.reqBody(apiReqBody) .reqBody(apiReqBody)
.reqSign(reqSign) .reqSign(reqSign)
.build(); .build();
log.debug("调用易生发起支付接口请求:{}",JSONObject.toJSONString(easyPayRequest)); log.debug("调用易生发起支付接口请求:{}", JSONObject.toJSONString(easyPayRequest));
String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/jsapi").toString(); String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/jsapi").toString();
String body = HttpRequest.post(url) String body = HttpRequest.post(url)
.timeout(3000) .timeout(3000)
.body(JSON.toJSONString(easyPayRequest)) .body(JSON.toJSONString(easyPayRequest))
.execute() .execute()
.body(); .body();
log.debug("调用易生发起支付接口响应:{}",body); log.debug("调用易生发起支付接口响应:{}", body);
EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class); EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) { if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign()); verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
JsApiRespBody jsApiRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), JsApiRespBody.class); JsApiRespBody jsApiRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), JsApiRespBody.class);
RespStateInfo respStateInfo = jsApiRespBody.getRespStateInfo(); RespStateInfo respStateInfo = jsApiRespBody.getRespStateInfo();
if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK,RSP_BODY_TRANS_PARTIALLY_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PROCESSING)) { if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_PARTIALLY_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PROCESSING)) {
//保存支付单信息到数据库 //保存支付单信息到数据库
JsapiRespOrderInfo respOrderInfo = jsApiRespBody.getRespOrderInfo(); JsapiRespOrderInfo respOrderInfo = jsApiRespBody.getRespOrderInfo();
payOrder.setPayType(paymentBO.getPayType().getValue()); payOrder.setPayType(paymentBO.getPayType().getValue());
payOrder.setStartTransDate(new Date()); payOrder.setStartTransDate(new Date());
payOrder.setEasypayTrace(respOrderInfo.getOutTrace()); payOrder.setEasypayTrace(respOrderInfo.getOutTrace());
payOrder.setTransState(TransState.Payment.getCode());
payOrderMapper.updateById(payOrder); payOrderMapper.updateById(payOrder);
//关联订单的最新支付单
orderMapper.updateById(Order.builder().id(payOrder.getOrderId()).payId(payOrder.getId()).build());
// 产生待分账记录
generateDivideRecord(payOrder);
// 生成返回前端的预支付信息 // 生成返回前端的预支付信息
return generatePrePayVO(paymentBO.getPayType(), jsApiRespBody); return generatePrePayVO(paymentBO.getPayType(), jsApiRespBody);
} else { } else {
@ -248,6 +252,14 @@ public class EasypayServiceImpl implements IEasypayService {
} }
} }
/**
* 生成待分账记录
* @param payOrder
*/
private void generateDivideRecord(PayOrder payOrder) {
//TODO 待完善
}
/** /**
* 根据支付类型校验请求参数是否完整 * 根据支付类型校验请求参数是否完整
* *
@ -255,9 +267,12 @@ public class EasypayServiceImpl implements IEasypayService {
*/ */
private void checkPaymentParamByPayType(PaymentBO paymentBO) throws ServerException { private void checkPaymentParamByPayType(PaymentBO paymentBO) throws ServerException {
switch (paymentBO.getPayType()) { switch (paymentBO.getPayType()) {
case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> Assert.notBlank(paymentBO.getBuyerId(), () -> new ServiceException("支付宝支付缺少必要参数:buyerId")); case ALI_PAY_JSAPI, ALI_PAY_MINI_APP ->
case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> Assert.notBlank(paymentBO.getWxLoginCode(), () -> new ServiceException("微信支付缺少必要参数:微信登录授权code")); Assert.notBlank(paymentBO.getBuyerId(), () -> new ServiceException("支付宝支付缺少必要参数:buyerId"));
case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getTransType(), paymentBO.getUserAuthCode(), case WE_CHAT_JSAPI, WE_CHAT_MINI_APP ->
Assert.notBlank(paymentBO.getWxLoginCode(), () -> new ServiceException("微信支付缺少必要参数:微信登录授权code"));
case UNION_PAY_JSAPI, UNION_PAY_Js_MINI ->
Assert.isTrue(StrUtil.isAllNotBlank(paymentBO.getTransType(), paymentBO.getUserAuthCode(),
paymentBO.getUserId(), paymentBO.getAreaInfo(), paymentBO.getPaymentValidTime(), paymentBO.getUserId(), paymentBO.getAreaInfo(), paymentBO.getPaymentValidTime(),
paymentBO.getQrCode(), paymentBO.getQrCodeType()), () -> new ServiceException("银联支付缺少必要参数")); paymentBO.getQrCode(), paymentBO.getQrCodeType()), () -> new ServiceException("银联支付缺少必要参数"));
default -> throw new ServerException("不支持的支付方式"); default -> throw new ServerException("不支持的支付方式");
@ -272,13 +287,14 @@ public class EasypayServiceImpl implements IEasypayService {
*/ */
private void setPayInfo(JsApiReqBody apiReqBody, PaymentBO paymentBO) throws ServerException { private void setPayInfo(JsApiReqBody apiReqBody, PaymentBO paymentBO) throws ServerException {
switch (paymentBO.getPayType()) { switch (paymentBO.getPayType()) {
case ALI_PAY_JSAPI, ALI_PAY_MINI_APP -> apiReqBody.setAliBizParam(AliBizParam.builder().buyerId(paymentBO.getBuyerId()).build()); case ALI_PAY_JSAPI, ALI_PAY_MINI_APP ->
apiReqBody.setAliBizParam(AliBizParam.builder().buyerId(paymentBO.getBuyerId()).build());
case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> { case WE_CHAT_JSAPI, WE_CHAT_MINI_APP -> {
WxAuthResponse wxAuthResponse = wxAuthService.getOpenIdByMiniProgramCode(paymentBO.getWxLoginCode()); WxAuthResponse wxAuthResponse = wxAuthService.getOpenIdByMiniProgramCode(paymentBO.getWxLoginCode());
Assert.isTrue(StrUtil.equals(wxAuthResponse.getErrcode(), "0"), () -> new ServiceException("微信小程序登录异常"));
apiReqBody.setWxBizParam(WxBizParam.builder().subAppid(wechatMiniProgramConfig.getAppId()).subOpenId(wxAuthResponse.getOpenid()).build()); apiReqBody.setWxBizParam(WxBizParam.builder().subAppid(wechatMiniProgramConfig.getAppId()).subOpenId(wxAuthResponse.getOpenid()).build());
} }
case UNION_PAY_JSAPI, UNION_PAY_Js_MINI -> apiReqBody.setQrBizParam(BeanUtil.copyProperties(paymentBO, QrBizParam.class)); case UNION_PAY_JSAPI, UNION_PAY_Js_MINI ->
apiReqBody.setQrBizParam(BeanUtil.copyProperties(paymentBO, QrBizParam.class));
default -> throw new ServerException("不支持的支付方式"); default -> throw new ServerException("不支持的支付方式");
} }
PayInfo payInfo = PayInfo.builder() PayInfo payInfo = PayInfo.builder()
@ -297,27 +313,27 @@ public class EasypayServiceImpl implements IEasypayService {
Order order = orderMapper.selectById(paymentBO.getOrderId()); Order order = orderMapper.selectById(paymentBO.getOrderId());
Assert.notNull(order, () -> new ServiceException("订单不存在")); Assert.notNull(order, () -> new ServiceException("订单不存在"));
Assert.isTrue(Objects.equals(order.getStatus(), OrderStatusEnum.UNPAID.getValue()), () -> new ServiceException("订单已支付")); Assert.isTrue(Objects.equals(order.getStatus(), OrderStatusEnum.UNPAID.getValue()), () -> new ServiceException("订单已支付"));
PayOrder payOrder;
// 订单首次支付创建支付订单记录
if(order.getPayId() == null){
payOrder = PayOrder.builder()
.orderId(order.getId())
.transAmount(apiReqBody.getReqOrderInfo().getTransAmount())
.startTransDate(new Date())
.transOver(false)
.build();
payOrderMapper.insert(payOrder);
orderMapper.updateById(Order.builder().id(order.getId()).payId(payOrder.getId()).build());
}else {
payOrder = payOrderMapper.selectById(order.getPayId());
Assert.notNull(payOrder, () -> new ServiceException("订单异常"));
}
// 订单中的金额单位为元需转换为分 // 订单中的金额单位为元需转换为分
long transAmount = order.getTotalAmount().multiply(BigDecimal.valueOf(100)).longValue(); long transAmount = order.getTotalAmount().multiply(BigDecimal.valueOf(100)).longValue();
PayOrder payOrder;
// 订单已拉起过支付支付中或已支付状态不可再次支付
if (order.getPayId() != null) {
payOrder = payOrderMapper.selectById(order.getPayId());
Assert.notNull(payOrder, () -> new ServiceException("订单异常"));
Assert.isTrue(Objects.equals(payOrder.getTransState(), TransState.PENDING.getCode()), () -> new ServiceException("订单支付中或已支付"));
}
payOrder = PayOrder.builder()
.orderId(order.getId())
.transAmount(transAmount)
.startTransDate(new Date())
.transState(TransState.PENDING.getCode())
.payType(paymentBO.getPayType().getValue())
.build();
payOrderMapper.insert(payOrder);
JsapiReqOrderInfo jsapiReqOrderInfo = JsapiReqOrderInfo.builder() JsapiReqOrderInfo jsapiReqOrderInfo = JsapiReqOrderInfo.builder()
.orgTrace(String.valueOf(payOrder.getId())) .orgTrace(String.valueOf(payOrder.getId()))
.transAmount(transAmount) .transAmount(transAmount)
.backUrl(easypayConfig.getBackUrl()) .backUrl(easypayConfig.getTradeBackUrl())
.timeout(String.valueOf(TRACE_TIMEOUT)) .timeout(String.valueOf(TRACE_TIMEOUT))
.orderSub("无终街支付订单") .orderSub("无终街支付订单")
.orderDes("无终街支付订单:" + order.getMerchantNote()) .orderDes("无终街支付订单:" + order.getMerchantNote())
@ -357,23 +373,22 @@ public class EasypayServiceImpl implements IEasypayService {
/** /**
* 查询支付结果 * 查询支付结果
*
* @param orderId * @param orderId
* @return * @return
* @throws ServerException * @throws ServerException
*/ */
@Override @Override
public EasypayPaymentResultVO paymentQuery(Long orderId) throws ServerException { public EasypayTransResultVO paymentQuery(Long orderId) throws ServerException {
Order order = orderMapper.selectById(orderId); Order order = orderMapper.selectById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在")); Assert.notNull(order, () -> new ServiceException("订单不存在"));
PayOrder payOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery(PayOrder.class) PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
.eq(PayOrder::getOrderId, orderId)
.last("limit 1"));
Assert.notNull(payOrder, () -> new ServiceException("订单不存在")); Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
EasypayPaymentResultVO paymentResultVO = EasypayPaymentResultVO.builder() EasypayTransResultVO paymentResultVO = EasypayTransResultVO.builder()
.orderId(order.getId()) .orderId(order.getId())
.payId(order.getPayId()) .payId(order.getPayId())
.totalAmount(order.getTotalAmount()) .totalAmount(order.getTotalAmount())
.paymentComplete(false) .transState(TransState.PENDING.getCode())
.build(); .build();
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader(); EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
TradeQueryReqBody queryReqBody = TradeQueryReqBody.builder() TradeQueryReqBody queryReqBody = TradeQueryReqBody.builder()
@ -382,7 +397,7 @@ public class EasypayServiceImpl implements IEasypayService {
.reqOrderInfo(TradeQueryReqBody.ReqOrderInfo.builder() .reqOrderInfo(TradeQueryReqBody.ReqOrderInfo.builder()
.orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString()) .orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString())
.oriOrgTrace(String.valueOf(order.getPayId())) .oriOrgTrace(String.valueOf(order.getPayId()))
.oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD,payOrder.getStartTransDate())) .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getStartTransDate()))
.build()) .build())
.build(); .build();
String reqSign = getSignStr(reqHeader, queryReqBody); String reqSign = getSignStr(reqHeader, queryReqBody);
@ -391,34 +406,34 @@ public class EasypayServiceImpl implements IEasypayService {
.reqBody(queryReqBody) .reqBody(queryReqBody)
.reqSign(reqSign) .reqSign(reqSign)
.build(); .build();
log.debug("调用易生查询支付接口请求:{}",JSONObject.toJSONString(easyRequest)); log.debug("调用易生查询支付接口请求:{}", JSONObject.toJSONString(easyRequest));
String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/tradeQuery").toString(); String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/tradeQuery").toString();
String body = HttpRequest.post(url) String body = HttpRequest.post(url)
.timeout(3000) .timeout(3000)
.body(JSON.toJSONString(easyRequest)) .body(JSON.toJSONString(easyRequest))
.execute() .execute()
.body(); .body();
log.debug("调用易生查询支付接口响应:{}",body); log.debug("调用易生查询支付接口响应:{}", body);
EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class); EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) { if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(),easyPayResponse.getRspSign()); verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
TradeQueryRespBody tradeQueryRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), TradeQueryRespBody.class); TradeQueryRespBody tradeQueryRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), TradeQueryRespBody.class);
RespStateInfo respStateInfo = tradeQueryRespBody.getRespStateInfo(); RespStateInfo respStateInfo = tradeQueryRespBody.getRespStateInfo();
if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
// 支付完成 进入终态 // 支付完成 进入终态
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT,RSP_BODY_TRANS_PARTIALLY_OK)) { if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) {
TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo(); TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo();
// 更新支付订单信息到数据库 // 更新支付订单信息到数据库
PayOrder payOrderUpdate = PayOrder.builder() PayOrder payOrderUpdate = PayOrder.builder()
.id(payOrder.getId()) .id(payOrder.getId())
.endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()),toString())) .endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()), toString()))
.easypayTrace(respOrderInfo.getOutTrace()) .easypayTrace(respOrderInfo.getOutTrace())
.pcTrace(respOrderInfo.getPcTrace()) .pcTrace(respOrderInfo.getPcTrace())
.unTrace(respOrderInfo.getUnTrace()) .unTrace(respOrderInfo.getUnTrace())
.transOver(true) .transState(TransState.PAID.getCode())
.build(); .build();
payOrderMapper.updateById(payOrderUpdate); payOrderMapper.updateById(payOrderUpdate);
paymentResultVO.setPaymentComplete(true); paymentResultVO.setTransState(TransState.PAID.getCode());
} }
} else { } else {
log.error("查询支付结果失败:{}", respStateInfo.getRespDesc()); log.error("查询支付结果失败:{}", respStateInfo.getRespDesc());
@ -434,25 +449,26 @@ public class EasypayServiceImpl implements IEasypayService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void handleTradeCallback(EasyPayRequest easyPayRequest) { public void handleTradeCallback(EasyPayRequest easyPayRequest) {
verify(easyPayRequest.getReqHeader(),easyPayRequest.getReqBody(), easyPayRequest.getReqSign()); log.debug("易生支付结果通知回调:{}", JSONObject.toJSONString(easyPayRequest));
verify(easyPayRequest.getReqHeader(), easyPayRequest.getReqBody(), easyPayRequest.getReqSign());
TradeQueryRespBody tradeQueryRespBody = BeanUtil.toBean(easyPayRequest.getReqBody(), TradeQueryRespBody.class); TradeQueryRespBody tradeQueryRespBody = BeanUtil.toBean(easyPayRequest.getReqBody(), TradeQueryRespBody.class);
RespStateInfo respStateInfo = tradeQueryRespBody.getRespStateInfo(); RespStateInfo respStateInfo = tradeQueryRespBody.getRespStateInfo();
if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) { if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
// 支付完成 进入终态 // 支付完成 进入终态
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT,RSP_BODY_TRANS_PARTIALLY_OK)) { if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) {
TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo(); TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo();
PayOrder payOrder = payOrderMapper.selectById(String.valueOf(respOrderInfo.getOrgTrace())); PayOrder payOrder = payOrderMapper.selectById(String.valueOf(respOrderInfo.getOrgTrace()));
if(payOrder != null){ if (payOrder != null) {
Order order = orderMapper.selectById(payOrder.getOrderId()); Order order = orderMapper.selectById(payOrder.getOrderId());
if(order != null){ if (order != null) {
// 更新支付订单信息 // 更新支付订单信息
payOrderMapper.updateById(PayOrder.builder() payOrderMapper.updateById(PayOrder.builder()
.id(payOrder.getId()) .id(payOrder.getId())
.endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()),toString())) .endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()), toString()))
.easypayTrace(respOrderInfo.getOutTrace()) .easypayTrace(respOrderInfo.getOutTrace())
.pcTrace(respOrderInfo.getPcTrace()) .pcTrace(respOrderInfo.getPcTrace())
.unTrace(respOrderInfo.getUnTrace()) .unTrace(respOrderInfo.getUnTrace())
.transOver(true) .transState(TransState.PAID.getCode())
.build()); .build());
// 更新订单信息 // 更新订单信息
orderMapper.updateById(Order.builder() orderMapper.updateById(Order.builder()
@ -461,10 +477,10 @@ public class EasypayServiceImpl implements IEasypayService {
.paymentTime(LocalDateTimeUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString(), "yyyyMMddHHmmss")) .paymentTime(LocalDateTimeUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString(), "yyyyMMddHHmmss"))
.payType(PayType.getByValue(payOrder.getPayType()).getChannel()) .payType(PayType.getByValue(payOrder.getPayType()).getChannel())
.build()); .build());
}else{ } else {
log.warn("回调通知的支付订单不存在:{}", respOrderInfo.getOrgTrace()); log.warn("回调通知的支付订单不存在:{}", respOrderInfo.getOrgTrace());
} }
}else{ } else {
log.warn("回调通知的支付订单不存在:{}", respOrderInfo.getOrgTrace()); log.warn("回调通知的支付订单不存在:{}", respOrderInfo.getOrgTrace());
} }
} }
@ -474,11 +490,18 @@ public class EasypayServiceImpl implements IEasypayService {
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void refund(Long orderId) { public void refund(Long orderId) {
Order order = orderMapper.selectById(orderId); Order order = orderMapper.selectById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在")); Assert.notNull(order, () -> new ServiceException("订单不存在"));
PayOrder payOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery(PayOrder.class).eq(PayOrder::getOrderId, orderId).last("limit 1")); PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
Assert.notNull(payOrder, () -> new ServiceException("订单不存在")); Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode(), () -> new ServiceException("订单未支付,不可发起退款"));
// 更新支付订单状态为退款中
payOrderMapper.updateById(PayOrder.builder()
.id(payOrder.getId())
.transState(TransState.REFUND_PENDING.getCode())
.build());
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader(); EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder() RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder()
.reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build()) .reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
@ -486,7 +509,7 @@ public class EasypayServiceImpl implements IEasypayService {
.reqOrderInfo(RefundReqOrderInfo.builder() .reqOrderInfo(RefundReqOrderInfo.builder()
.orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString()) .orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString())
.oriOrgTrace(String.valueOf(order.getPayId())) .oriOrgTrace(String.valueOf(order.getPayId()))
.oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD,payOrder.getStartTransDate())) .oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getEndTransDate()))
.refundAmount(payOrder.getTransAmount()) .refundAmount(payOrder.getTransAmount())
.build()) .build())
.build(); .build();
@ -496,15 +519,165 @@ public class EasypayServiceImpl implements IEasypayService {
.reqBody(refundApplyReqBody) .reqBody(refundApplyReqBody)
.reqSign(reqSign) .reqSign(reqSign)
.build(); .build();
log.debug("调用易生实时退款接口请求:{}",JSONObject.toJSONString(easyRequest)); log.debug("调用易生实时退款接口请求:{}", JSONObject.toJSONString(easyRequest));
String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/refund/apply").toString(); String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/refund/apply").toString();
String body = HttpRequest.post(url) String body = HttpRequest.post(url)
.timeout(3000) .timeout(3000)
.body(JSON.toJSONString(easyRequest)) .body(JSON.toJSONString(easyRequest))
.execute() .execute()
.body(); .body();
log.debug("调用易实时退款接口响应:{}",body); log.debug("调用易实时退款接口响应:{}", body);
RefundApplyRespBody refundApplyRespBody = JSONObject.parseObject(body, RefundApplyRespBody.class); EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
RefundApplyRespBody refundApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), RefundApplyRespBody.class);
RespStateInfo respStateInfo = refundApplyRespBody.getRespStateInfo();
if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
// 更新支付单退款状态
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUNDED.getCode()).build());
}
} else {
log.error("易生退款失败:{}", respStateInfo.getRespDesc());
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PAID.getCode()).build());
throw new ServiceException("易生退款失败:" + respStateInfo.getRespDesc());
}
} else {
log.error("易生退款通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PAID.getCode()).build());
throw new ServiceException("易生退款通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
}
}
@Override
public EasypayTransResultVO refundQuery(Long orderId) {
Order order = orderMapper.selectById(orderId);
Assert.notNull(order, () -> new ServiceException("订单不存在"));
PayOrder payOrder = payOrderMapper.selectById(order.getPayId());
Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
EasypayTransResultVO easypayTransResultVO = EasypayTransResultVO.builder()
.orderId(order.getId())
.payId(payOrder.getId())
.totalAmount(order.getTotalAmount())
.transState(payOrder.getTransState())
.build();
if (payOrder.getTransState() == TransState.REFUNDED.getCode()) {
return easypayTransResultVO;
}
Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode() || payOrder.getTransState() == TransState.REFUND_PENDING.getCode(), () -> new ServiceException("订单未支付"));
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
RefundApplyReqBody refundApplyReqBody = RefundApplyReqBody.builder()
.reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
.payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build())
.reqOrderInfo(RefundReqOrderInfo.builder()
.orgTrace(StrBuilder.create(TRACE_PREFIX).append(System.currentTimeMillis()).append(RandomUtil.randomString(4)).toString())
.oriOrgTrace(String.valueOf(order.getPayId()))
.oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, payOrder.getEndTransDate()))
.refundAmount(payOrder.getTransAmount())
.build())
.build();
String reqSign = getSignStr(reqHeader, refundApplyReqBody);
EasyPayRequest easyRequest = EasyPayRequest.builder()
.reqHeader(reqHeader)
.reqBody(refundApplyReqBody)
.reqSign(reqSign)
.build();
log.debug("调用易生实时退款接口请求:{}", JSONObject.toJSONString(easyRequest));
String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/refund/query").toString();
String body = HttpRequest.post(url)
.timeout(3000)
.body(JSON.toJSONString(easyRequest))
.execute()
.body();
log.debug("调用易实时退款接口响应:{}", body);
EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
RefundApplyRespBody refundApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), RefundApplyRespBody.class);
RespStateInfo respStateInfo = refundApplyRespBody.getRespStateInfo();
if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
easypayTransResultVO.setTransState(TransState.REFUNDED.getCode());
// 更新支付单退款状态
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUNDED.getCode()).build());
}
} else {
log.error("查询支付结果失败:{}", respStateInfo.getRespDesc());
throw new ServiceException("查询支付结果失败:" + respStateInfo.getRespDesc());
}
} else {
log.error("查询支付结果通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
throw new ServiceException("查询支付结果通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
}
return easypayTransResultVO;
}
@Override
public SeparateApplyBO separateApply(SeparateApplyBO separateApplyBO) {
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
List<SeparateInfo.SeparateOrderDetailList> separateOrderDetailListList = separateApplyBO.getSeparateItemBOList().stream().map(item -> BeanUtil.copyProperties(item, SeparateInfo.SeparateOrderDetailList.class)).toList();
SeparateApplyReqBody separateApplyReqBody = SeparateApplyReqBody.builder()
.reqInfo(ReqInfo.builder().mchtCode(easypayConfig.getMchtCode()).build())
.payInfo(PayInfo.builder().transDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, new Date())).build())
.reqOrderInfo(SeparateReqOrderInfo.builder()
.backUrl(easypayConfig.getSeparateBackUrl())
.oriOrgTrace(String.valueOf(separateApplyBO.getPayId()))
.oriTransDate(DateUtils.parseDateToStr(FormatsType.YYYYMMDD, separateApplyBO.getTransDate()))
.separateBatchTrace(separateApplyBO.getSeparateBatchTrace())
.transSumAmt(separateApplyBO.getTransSumAmt())
.transSumCount(separateApplyBO.getSeparateItemBOList().size())
.separateOrderDetailList(separateOrderDetailListList)
.build())
.build();
String reqSign = getSignStr(reqHeader, separateApplyReqBody);
EasyPayRequest easyRequest = EasyPayRequest.builder()
.reqHeader(reqHeader)
.reqBody(separateApplyReqBody)
.reqSign(reqSign)
.build();
log.debug("调用易生请求分账接口请求:{}", JSONObject.toJSONString(easyRequest));
String url = StrBuilder.create(easypayConfig.getApiPathPrefix()).append("/trade/separate/orderApply").toString();
String body = HttpRequest.post(url)
.timeout(3000)
.body(JSON.toJSONString(easyRequest))
.execute()
.body();
log.debug("调用易生请求分账接口响应:{}", body);
EasyPayResponse easyPayResponse = JSONObject.parseObject(body, EasyPayResponse.class);
if (StrUtil.equals(RSP_HEADER_OK, easyPayResponse.getRspHeader().getRspCode())) {
verify(easyPayResponse.getRspHeader(), easyPayResponse.getRspBody(), easyPayResponse.getRspSign());
SeparateApplyRespBody separateApplyRespBody = JSON.parseObject(JSONObject.toJSONString(easyPayResponse.getRspBody()), SeparateApplyRespBody.class);
RespStateInfo respStateInfo = separateApplyRespBody.getRespStateInfo();
if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK)) {
// 更新分账状态
SeparateRespOrderInfo respOrderInfo = separateApplyRespBody.getRespOrderInfo();
handleSeparateInfo(separateApplyBO, respOrderInfo);
}
} else {
log.error("请求分账失败:{}", respStateInfo.getRespDesc());
throw new ServiceException("请求分账失败:" + respStateInfo.getRespDesc());
}
} else {
log.error("易生请求分账通讯失败:{}", easyPayResponse.getRspHeader().getRspInfo());
throw new ServiceException("易生请求分账通讯失败:" + easyPayResponse.getRspHeader().getRspInfo());
}
return separateApplyBO;
}
@Override
public void handleSeparateCallback(EasyPayRequest easyPayRequest) {
// TODO 处理回调分账结果信息更新分账详情表
}
/**
* 处理分账结果信息更新分账详情表
* @param respOrderInfo
*/
private SeparateApplyBO handleSeparateInfo(SeparateApplyBO separateApplyBO, SeparateRespOrderInfo respOrderInfo) {
//TODO 处理分账结果信息更新分账详情表
return separateApplyBO;
} }

View File

@ -1,8 +1,6 @@
package com.wzj.soopin.transaction.service.impl; package com.wzj.soopin.transaction.service.impl;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.transaction.domain.po.PayOrder; import com.wzj.soopin.transaction.domain.po.PayOrder;
import com.wzj.soopin.transaction.mapper.PayOrderMapper; import com.wzj.soopin.transaction.mapper.PayOrderMapper;

View File

@ -1,10 +1,15 @@
package com.wzj.soopin.transaction.service.impl; package com.wzj.soopin.transaction.service.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrBuilder; import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSONObject;
import com.wzj.soopin.transaction.domain.entity.WxAuthResponse; import com.wzj.soopin.transaction.domain.entity.WxAuthResponse;
import com.wzj.soopin.transaction.config.WechatMiniProgramConfig; import com.wzj.soopin.transaction.config.WechatMiniProgramConfig;
import com.wzj.soopin.transaction.wechat.WechatPayConfig; import com.wzj.soopin.transaction.wechat.WechatPayConfig;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -55,10 +60,10 @@ public class WxAuthService {
.append(code) .append(code)
.append("&grant_type=authorization_code") .append("&grant_type=authorization_code")
.toString(); .toString();
WxAuthResponse response = restTemplate.getForObject(url, WxAuthResponse.class); String body = HttpRequest.get(url).timeout(3000).execute().body();
if (response == null || response.getOpenid() == null) { Assert.notBlank(body, () -> new ServiceException("微信小程序登录异常"));
throw new RuntimeException("Failed to get openid from WeChat"); WxAuthResponse response = JSONObject.parseObject(body, WxAuthResponse.class);
} Assert.notBlank(response.getOpenid(), () -> new ServiceException("微信小程序登录异常:" + response.getErrmsg()));
return response; return response;
} }