Przeglądaj źródła

Merge branch 'master' of http://124.220.229.80:9093/root/cif

linxk 1 tydzień temu
rodzic
commit
bb3396a174

+ 22 - 87
cif-api/src/main/java/com/txz/cif/dto/BizLogDTO.java

@@ -1,122 +1,57 @@
 /*
-*
-* BizLogDTO.java
-* Copyright(C) 2017-2020 fendo公司
-* @date 2025-07-29
-*/
+ *
+ * BizLogDTO.java
+ * Copyright(C) 2017-2020 fendo公司
+ * @date 2025-07-29
+ */
 package com.txz.cif.dto;
 
+import lombok.Builder;
+import lombok.Data;
+
 import java.io.Serializable;
 import java.util.Date;
 
+@Data
+@Builder
 public class BizLogDTO implements Serializable {
     /**
-     * 
+     *
      */
     private Long id;
-
+    
     /**
      * 订单号
      */
     private String bizNo;
-
+    
     /**
      * 类型1提交充值订单 2充值调用第三方 3充值回调通知成功 4提交提现申请
      */
     private Integer type;
-
+    
     /**
      * 创建人
      */
     private String createUser;
-
+    
     /**
      * 创建时间
      */
     private Date createTime;
-
+    
     /**
      * c_biz_log
      */
     private static final long serialVersionUID = 1L;
-
-    /**
-     * 
-     * @return id 
-     */
-    public Long getId() {
-        return id;
-    }
-
-    /**
-     * 
-     * @param id 
-     */
-    public void setId(Long id) {
-        this.id = id;
-    }
-
-    /**
-     * 订单号
-     * @return biz_no 订单号
-     */
-    public String getBizNo() {
-        return bizNo;
-    }
-
-    /**
-     * 订单号
-     * @param bizNo 订单号
-     */
-    public void setBizNo(String bizNo) {
-        this.bizNo = bizNo;
-    }
-
-    /**
-     * 类型1提交充值订单 2充值调用第三方 3充值回调通知成功 4提交提现申请
-     * @return type 类型1提交充值订单 2充值调用第三方 3充值回调通知成功 4提交提现申请
-     */
-    public Integer getType() {
-        return type;
-    }
-
-    /**
-     * 类型1提交充值订单 2充值调用第三方 3充值回调通知成功 4提交提现申请
-     * @param type 类型1提交充值订单 2充值调用第三方 3充值回调通知成功 4提交提现申请
-     */
-    public void setType(Integer type) {
-        this.type = type;
-    }
-
-    /**
-     * 创建人
-     * @return create_user 创建人
-     */
-    public String getCreateUser() {
-        return createUser;
-    }
-
-    /**
-     * 创建人
-     * @param createUser 创建人
-     */
-    public void setCreateUser(String createUser) {
-        this.createUser = createUser;
-    }
-
+    
     /**
-     * 创建时间
-     * @return create_time 创建时间
+     * 三方接口请求消息
      */
-    public Date getCreateTime() {
-        return createTime;
-    }
-
+    private String sendMessage;
+    
     /**
-     * 创建时间
-     * @param createTime 创建时间
+     * 三方接口接收消息
      */
-    public void setCreateTime(Date createTime) {
-        this.createTime = createTime;
-    }
+    private String receiveMessage;
 }

+ 49 - 0
cif-api/src/main/java/com/txz/cif/dto/tfpay/CheckSignDTO.java

@@ -0,0 +1,49 @@
+package com.txz.cif.dto.tfpay;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/2
+ */
+@Data
+public class CheckSignDTO {
+    
+     /**
+     * 商户编号 - 平台分配商户号
+     */
+    @JsonProperty("memberid")
+    private String memberid;
+    
+    /**
+     * 商户订单号 - 订单号唯一, 字符长度20
+     */
+    @JsonProperty("orderid")
+    private String orderid;
+    
+    /**
+     * 订单最终金额 - 订单金额 单位:元
+     */
+    @JsonProperty("amount")
+    private String amount;
+    
+    /**
+     * 平台订单号
+     */
+    @JsonProperty("transaction_id")
+    private String transaction_id;
+    
+    /**
+     * 交易成功时间 - 时间格式:Y-m-d H:i:s
+     */
+    @JsonProperty("datetime")
+    private String datetime;
+    
+    /**
+     * 交易状态 - "00" 为成功
+     */
+    @JsonProperty("returncode")
+    private String returncode;
+    
+}

+ 49 - 0
cif-api/src/main/java/com/txz/cif/dto/tfpay/TFCreateOrderDTO.java

@@ -0,0 +1,49 @@
+package com.txz.cif.dto.tfpay;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+@Data
+@Builder
+public class TFCreateOrderDTO {
+    
+    /**
+     * 商户号
+     */
+    private String payMemberid;
+    
+    /**
+     * 商户订单号
+     */
+    private String payOrderid;
+    
+    /**
+     * 提交时间
+     */
+    private String payApplydate;
+    
+    /**
+     * 通道编码
+     */
+    private String payChannelcode;
+    
+    /**
+     * 异步回调
+     */
+    private String payNotifyurl;
+    
+    /**
+     * 订单金额
+     */
+    private String payAmount;
+    
+    /**
+     * 客户端ip
+     */
+    private String ip;
+    
+}

+ 135 - 0
cif-api/src/main/java/com/txz/cif/dto/tfpay/TFPayOrderDTO.java

@@ -0,0 +1,135 @@
+package com.txz.cif.dto.tfpay;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+@Data
+@Builder
+public class TFPayOrderDTO {
+    
+    /**
+     * 商户号
+     */
+    @JsonProperty("pay_memberid")
+    private String payMemberid;
+    
+    /**
+     * 商户订单号
+     */
+    @JsonProperty("pay_orderid")
+    private String payOrderid;
+    
+    /**
+     * 提交时间
+     */
+    @JsonProperty("pay_applydate")
+    private String payApplydate;
+    
+    /**
+     * 通道编码
+     */
+    @JsonProperty("pay_channelcode")
+    private String payChannelcode;
+    
+    /**
+     * 异步回调
+     */
+    @JsonProperty("pay_notifyurl")
+    private String payNotifyurl;
+    
+    /**
+     * 同步回调
+     */
+    @JsonProperty("pay_callbackurl")
+    private String payCallbackurl;
+    
+    /**
+     * 订单金额
+     */
+    @JsonProperty("pay_amount")
+    private String payAmount;
+    
+    /**
+     * 数据签名
+     */
+    @JsonProperty("pay_md5sign")
+    private String payMd5sign;
+    
+    /**
+     * 币种
+     */
+    @JsonProperty("currency_type")
+    private String currencyType;
+    
+    /**
+     * 返回数据格式
+     */
+    @JsonProperty("type")
+    private String type;
+    
+    /**
+     * 用户真实ip
+     */
+    @JsonProperty("ip")
+    private String ip;
+    
+    /**
+     * 用户姓名
+     */
+    @JsonProperty("pay_username")
+    private String payUsername;
+    
+    /**
+     * 用户邮箱
+     */
+    @JsonProperty("pay_email")
+    private String payEmail;
+    
+    /**
+     * 用户电话
+     */
+    @JsonProperty("pay_mobile")
+    private String payMobile;
+    
+    /**
+     * 银行账号
+     */
+    @JsonProperty("pay_banknumber")
+    private String payBanknumber;
+    
+    /**
+     * 银行名称
+     */
+    @JsonProperty("pay_bankname")
+    private String payBankname;
+    
+    /**
+     * 附加字段
+     */
+    @JsonProperty("pay_attach")
+    private String payAttach;
+    
+    /**
+     * 商品名称
+     */
+    @JsonProperty("pay_productname")
+    private String payProductname;
+    
+    /**
+     * 身份证号
+     */
+    @JsonProperty("pay_idno")
+    private String payIdno;
+    
+    /**
+     * 加密方式
+     */
+    @JsonProperty("pay_signtype")
+    private String paySigntype;
+    
+}

+ 25 - 0
cif-api/src/main/java/com/txz/cif/dto/tfpay/TFQueryOrderDTO.java

@@ -0,0 +1,25 @@
+package com.txz.cif.dto.tfpay;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+@Data
+public class TFQueryOrderDTO {
+    
+    /**
+     * 商户号
+     */
+    @JsonProperty("pay_memberid")
+    private String payMemberid;
+    
+    /**
+     * 商户订单号
+     */
+    @JsonProperty("pay_orderid")
+    private String payOrderid;
+    
+}

+ 6 - 0
cif-service/pom.xml

@@ -316,6 +316,12 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
+            <version>2.2.5</version>
+        </dependency>
+
     </dependencies>
 
 

+ 36 - 0
cif-service/src/main/java/com/txz/cif/configurer/TFPayConfig.java

@@ -0,0 +1,36 @@
+package com.txz.cif.configurer;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+
+@Component
+@Data
+@RefreshScope
+public class TFPayConfig {
+    
+    // @Value("${tfpay.memberid}")
+    // private String payMemberid;
+    
+    // @Value("${tfpay.key}")
+    // private String key;
+    
+    // @Value("${tfpay.notifyurl}")
+    // private String payNotifyurl;
+    
+    @Value("${tfpay.callbackurl}")
+    private String payCallbackurl;
+    
+    @Value("${tfpay.paymenturl}")
+    private String paymenturl;
+    
+    @Value("${tfpay.withdrawurl}")
+    private String withdrawurl;
+    
+}

+ 1 - 0
cif-service/src/main/java/com/txz/cif/constants/MyConstants.java

@@ -52,4 +52,5 @@ public class MyConstants {
    */
   public static final String ICON_USER = "/public/user.png";
 
+  public static final String TF_PAY_NOTIFY_LOCK = "TF_PAY_NOTIFY_LOCK:%s";
 }

+ 27 - 15
cif-service/src/main/java/com/txz/cif/model/BizLog.java

@@ -4,53 +4,65 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.*;
 
-import java.util.Date;
 import javax.persistence.*;
+import java.util.Date;
 
 @Getter
 @Setter
 @NoArgsConstructor
 @AllArgsConstructor
 @Builder
-@ApiModel(value="com.txz.cif.model.BizLog")
+@ApiModel(value = "com.txz.cif.model.BizLog")
 @Table(name = "c_biz_log")
 public class BizLog {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
-    @ApiModelProperty(value="id")
+    @ApiModelProperty(value = "id")
     private Long id;
-
+    
     /**
      * 订单号
      */
     @Column(name = "biz_no")
-    @ApiModelProperty(value="bizNo订单号")
+    @ApiModelProperty(value = "bizNo订单号")
     private String bizNo;
-    @ApiModelProperty(value="备注")
+    @ApiModelProperty(value = "备注")
     private String memo;
     @Column(name = "biz_type")
-    @ApiModelProperty(value="类型1充值 2提现")
+    @ApiModelProperty(value = "类型1充值 2提现")
     private Integer bizType;
-
+    
     /**
      * type类型1提交充值订单 2充值调用第三方 3充值回调通知成功 4提交提现申请 5提现审核通过 6提现审核失败 7提现调用第三方 8提现回调通知成功
      */
-    @ApiModelProperty(value="type类型1提交充值订单 2充值调用第三方 3充值回调通知成功 4提交提现申请 5提现审核通过 6提现审核失败 7提现调用第三方 8提现回调通知成功 9提现回调通知失败")
+    @ApiModelProperty(value = "type类型1提交充值订单 2充值调用第三方 3充值回调通知成功 4提交提现申请 5提现审核通过 6提现审核失败 7提现调用第三方 8提现回调通知成功 9提现回调通知失败")
     private Integer type;
-
+    
+    /**
+     * 三方接口请求消息
+     */
+    @Column(name = "send_message")
+    private String sendMessage;
+    
+    /**
+     * 三方接口接收消息
+     */
+    @Column(name = "receive_message")
+    private String receiveMessage;
+    
     /**
      * 创建人
      */
     @Column(name = "create_user")
-    @ApiModelProperty(value="createUser创建人")
+    @ApiModelProperty(value = "createUser创建人")
     private String createUser;
-
+    
     /**
      * 创建时间
      */
     @Column(name = "create_time")
-    @ApiModelProperty(value="createTime创建时间")
+    @ApiModelProperty(value = "createTime创建时间")
     private Date createTime;
-
-
+    
+    
 }

+ 50 - 51
cif-service/src/main/java/com/txz/cif/model/RechargeRecord.java

@@ -7,158 +7,157 @@ import lombok.*;
 import javax.persistence.*;
 import java.math.BigDecimal;
 import java.util.Date;
+
 @Getter
 @Setter
 @NoArgsConstructor
 @AllArgsConstructor
 @Builder
 
-@ApiModel(value="com.txz.cif.model.RechargeRecord")
+@ApiModel(value = "com.txz.cif.model.RechargeRecord")
 @Table(name = "c_recharge_record")
 public class RechargeRecord {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
-    @ApiModelProperty(value="id")
+    @ApiModelProperty(value = "id")
     private Long id;
-
+    
     /**
      * 订单号
      */
     @Column(name = "order_no")
-    @ApiModelProperty(value="orderNo订单号")
+    @ApiModelProperty(value = "orderNo订单号")
     private String orderNo;
-
+    
     /**
      * 用户id
      */
     @Column(name = "user_id")
-    @ApiModelProperty(value="userId用户id")
+    @ApiModelProperty(value = "userId用户id")
     private Long userId;
-
+    
     @Column(name = "method_id")
-    @ApiModelProperty(value="支付方式id")
+    @ApiModelProperty(value = "支付方式id")
     private Long methodId;
-
+    
     @Column(name = "channel_id")
-    @ApiModelProperty(value="支付渠道id")
+    @ApiModelProperty(value = "支付渠道id")
     private Long channelId;
-
+    
     /**
      * 交易金额
      */
-    @ApiModelProperty(value="amount交易金额")
+    @ApiModelProperty(value = "amount交易金额")
     private BigDecimal amount;
-
+    
     @Column(name = "real_amount")
-    @ApiModelProperty(value="真实金额")
+    @ApiModelProperty(value = "真实金额")
     private BigDecimal realAmount;
-
-    @ApiModelProperty(value="折扣金额")
+    
+    @ApiModelProperty(value = "折扣金额")
     private BigDecimal discount;
-
+    
     /**
      * 状态 1处理中 2充值成功 3充值失败 4超时取消
      */
-    @ApiModelProperty(value="status状态 1处理中 2充值成功 3充值失败 4超时取消")
+    @ApiModelProperty(value = "status状态 1处理中 2充值成功 3充值失败 4超时取消")
     private Integer status;
-
+    
     /**
      * 渠道
      */
-    @ApiModelProperty(value="channel渠道")
+    @ApiModelProperty(value = "channel渠道")
     private String channel;
-
+    
     /**
      * 币种
      */
-    @ApiModelProperty(value="currency币种")
+    @ApiModelProperty(value = "currency币种")
     private String currency;
-
+    
     /**
      * 用户名
      */
     @Column(name = "user_name")
-    @ApiModelProperty(value="userName用户名")
+    @ApiModelProperty(value = "userName用户名")
     private String userName;
-
+    
     /**
      * 手机号
      */
     @Column(name = "user_phone")
-    @ApiModelProperty(value="userPhone手机号")
+    @ApiModelProperty(value = "userPhone手机号")
     private String userPhone;
-
+    
     /**
      * 交易时间
      */
     @Column(name = "trans_time")
-    @ApiModelProperty(value="transTime交易时间")
+    @ApiModelProperty(value = "transTime交易时间")
     private Date transTime;
-
+    
     /**
      * 交易成功时间
      */
     @Column(name = "success_time")
-    @ApiModelProperty(value="successTime交易成功时间")
+    @ApiModelProperty(value = "successTime交易成功时间")
     private Date successTime;
-
+    
     /**
      * 商品id
      */
     @Column(name = "goods_id")
-    @ApiModelProperty(value="goodsId商品id")
+    @ApiModelProperty(value = "goodsId商品id")
     private Integer goodsId;
-
+    
     /**
      * 创建人
      */
     @Column(name = "create_user")
-    @ApiModelProperty(value="createUser创建人")
+    @ApiModelProperty(value = "createUser创建人")
     private String createUser;
-
+    
     /**
      * 更新人
      */
     @Column(name = "update_user")
-    @ApiModelProperty(value="updateUser更新人")
+    @ApiModelProperty(value = "updateUser更新人")
     private String updateUser;
-
+    
     /**
      * 更新时间
      */
     @Column(name = "update_time")
-    @ApiModelProperty(value="updateTime更新时间")
+    @ApiModelProperty(value = "updateTime更新时间")
     private Date updateTime;
-
+    
     /**
      * 创建时间
      */
     @Column(name = "create_time")
-    @ApiModelProperty(value="createTime创建时间")
+    @ApiModelProperty(value = "createTime创建时间")
     private Date createTime;
-
+    
     /**
      * 版本号
      */
-    @ApiModelProperty(value="version版本号")
+    @ApiModelProperty(value = "version版本号")
     private String version;
-
-
+    
+    
     @Column(name = "bank")
-    @ApiModelProperty(value="银行")
+    @ApiModelProperty(value = "银行")
     private String bank;
-
+    
     /**
      * 用户名
      */
     @Column(name = "bank_account_name")
-    @ApiModelProperty(value="银行账号姓名")
+    @ApiModelProperty(value = "银行账号姓名")
     private String bankAccountName;
-
+    
     @Column(name = "bank_account")
-    @ApiModelProperty(value="银行")
+    @ApiModelProperty(value = "银行")
     private String bankAccount;
     
-    private String url;
-
 }

+ 24 - 0
cif-service/src/main/java/com/txz/cif/service/TFPayService.java

@@ -0,0 +1,24 @@
+package com.txz.cif.service;
+
+import com.txz.cif.dto.BizLogDTO;
+import com.txz.cif.dto.tfpay.TFCreateOrderDTO;
+import com.txz.cif.dto.tfpay.TFQueryOrderDTO;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+
+public interface TFPayService {
+    
+    /**
+     * 创单
+     */
+    BizLogDTO createTFPayOrder(TFCreateOrderDTO dto, String key);
+    
+    /**
+     * 查单
+     */
+    String queryOrder(TFQueryOrderDTO dto, String key);
+    
+}

+ 1 - 1
cif-service/src/main/java/com/txz/cif/service/impl/RechargeRecordServiceImpl.java

@@ -37,7 +37,7 @@ public class RechargeRecordServiceImpl extends AbstractService<RechargeRecord> i
                 .build());
 //        accountService.
         flowService.recharge(RechargeParam.builder()
-                        .amount(record.getAmount())
+                        .amount(record.getRealAmount())
                         .bizNo(record.getOrderNo())
                         .bizId(record.getId()+"")
                         .userId(record.getUserId())

+ 82 - 0
cif-service/src/main/java/com/txz/cif/service/impl/TFPayServiceImpl.java

@@ -0,0 +1,82 @@
+package com.txz.cif.service.impl;
+
+import cn.hutool.http.HttpRequest;
+import com.txz.cif.configurer.TFPayConfig;
+import com.txz.cif.dto.BizLogDTO;
+import com.txz.cif.dto.tfpay.TFCreateOrderDTO;
+import com.txz.cif.dto.tfpay.TFPayOrderDTO;
+import com.txz.cif.dto.tfpay.TFQueryOrderDTO;
+import com.txz.cif.service.TFPayService;
+import com.txz.cif.util.TFPayUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+@Slf4j
+@Service
+public class TFPayServiceImpl implements TFPayService {
+    
+    @Autowired
+    private TFPayConfig tfPayConfig;
+    
+    @Override
+    public BizLogDTO createTFPayOrder(TFCreateOrderDTO dto, String key) {
+        // 构建签名参数
+        TFPayOrderDTO tyPay = TFPayOrderDTO.builder()
+                .payMemberid(dto.getPayMemberid())
+                .payOrderid(dto.getPayOrderid())
+                .payApplydate(dto.getPayApplydate())
+                .payChannelcode(dto.getPayChannelcode())
+                .payNotifyurl(dto.getPayNotifyurl())
+                .payCallbackurl(tfPayConfig.getPayCallbackurl())
+                .payAmount(dto.getPayAmount())
+                .build();
+        
+        tyPay.setPayMd5sign(TFPayUtil.generateSignature(tyPay, key));
+        tyPay.setCurrencyType("BDT");
+        tyPay.setType("json");
+        tyPay.setIp(dto.getIp());
+        
+        Map<String, Object> reqMap = TFPayUtil.convertObjectToMap(tyPay);
+        String result = HttpRequest.post(tfPayConfig.getPaymenturl())
+                .form(reqMap)
+                .contentType("application/x-www-form-urlencoded")
+                .execute()
+                .body();
+        log.info("TFPay createOrder result: {}", result);
+        return BizLogDTO.builder()
+                .sendMessage(reqMap.toString())
+                .receiveMessage(result)
+                .build();
+    }
+    
+    @Override
+    public String queryOrder(TFQueryOrderDTO dto, String key) {
+        TFPayOrderDTO tfPay = TFPayOrderDTO.builder()
+                .payMemberid(dto.getPayMemberid())
+                .payOrderid(dto.getPayOrderid())
+                .build();
+        tfPay.setPayMd5sign(TFPayUtil.generateSignature(tfPay, key));
+        String result = HttpRequest.post(tfPayConfig.getPaymenturl())
+                .form(TFPayUtil.convertObjectToMap(tfPay))
+                .contentType("application/x-www-form-urlencoded")
+                .execute()
+                .body();
+        log.info("TFPay queryOrder result: {}", result);
+        // JSONObject resultJson = JSONUtil.parseObj(result);
+        // if (resultJson.getStr("status").equals("200")) {
+        //     return resultJson.getStr("data");
+        // } else {
+        //     throw new RuntimeException("查询TFPAY支付单失败--->" + result);
+        // }
+        // todo 暂无功能,后续修改
+        return result;
+    }
+    
+}

+ 112 - 0
cif-service/src/main/java/com/txz/cif/util/TFPayUtil.java

@@ -0,0 +1,112 @@
+package com.txz.cif.util;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/8/29
+ */
+public class TFPayUtil {
+    
+    /**
+     * 签名
+     *
+     * @param dto
+     * @param key 私钥
+     *
+     * @return
+     */
+    public static String generateSignature(Object dto, String key) {
+        Map<String, Object> paramMap = convertObjectToMap(dto);
+        String signString = buildSignString(paramMap);
+        return md5(signString + "&key=" + key).toUpperCase();
+    }
+    
+    /**
+     * 验签
+     *
+     * @param dto
+     * @param key          私钥
+     * @param expectedSign 签名
+     *
+     * @return
+     */
+    public static boolean verifySignature(Object dto, String key, String expectedSign) {
+        String generatedSign = generateSignature(dto, key);
+        return generatedSign.equals(expectedSign);
+    }
+    
+    public static Map<String, Object> convertObjectToMap(Object obj) {
+        Map<String, Object> paramMap = new TreeMap<>();
+        Class<?> clazz = obj.getClass();
+        Field[] fields = clazz.getDeclaredFields();
+        
+        for (Field field : fields) {
+            try {
+                field.setAccessible(true);
+                Object value = field.get(obj);
+                if (value != null && !value.toString().isEmpty()) {
+                    JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
+                    String key = (jsonProperty != null) ? jsonProperty.value() : field.getName();
+                    
+                    if (value instanceof BigDecimal) {
+                        paramMap.put(key, ((BigDecimal) value).stripTrailingZeros().toPlainString());
+                    } else {
+                        paramMap.put(key, value);
+                    }
+                }
+            } catch (IllegalAccessException e) {
+            }
+        }
+        return paramMap;
+    }
+    
+    
+    private static String buildSignString(Map<String, Object> paramMap) {
+        StringBuilder sb = new StringBuilder();
+        Iterator<Map.Entry<String, Object>> iterator = paramMap.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<String, Object> entry = iterator.next();
+            sb.append(entry.getKey()).append("=").append(entry.getValue());
+            if (iterator.hasNext()) {
+                sb.append("&");
+            }
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * MD5加密
+     *
+     * @param input 输入字符串
+     *
+     * @return MD5加密结果
+     */
+    public static String md5(String input) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] messageDigest = md.digest(input.getBytes());
+            StringBuilder hexString = new StringBuilder();
+            
+            for (byte b : messageDigest) {
+                String hex = Integer.toHexString(0xFF & b);
+                if (hex.length() == 1) {
+                    hexString.append('0');
+                }
+                hexString.append(hex);
+            }
+            
+            return hexString.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("MD5 algorithm not found", e);
+        }
+    }
+}

+ 144 - 41
cif-service/src/main/java/com/txz/cif/web/RechargeRecordApiController.java

@@ -1,28 +1,38 @@
 package com.txz.cif.web;
 
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
-import com.txz.cif.core.Result;
-import com.txz.cif.core.ResultGenerator;
-import com.txz.cif.model.*;
-import com.txz.cif.service.*;
-
-import com.txz.cif.core.ResultCode;
-
+import com.baomidou.lock.LockInfo;
+import com.baomidou.lock.LockTemplate;
+import com.baomidou.lock.executor.RedisTemplateLockExecutor;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
-import com.txz.cif.core.AuthService;
+import com.txz.cif.constants.MyConstants;
+import com.txz.cif.core.*;
+import com.txz.cif.dto.BizLogDTO;
+import com.txz.cif.dto.tfpay.CheckSignDTO;
+import com.txz.cif.dto.tfpay.TFCreateOrderDTO;
+import com.txz.cif.model.*;
+import com.txz.cif.service.*;
+import com.txz.cif.util.IpUtils;
+import com.txz.cif.util.TFPayUtil;
 import com.txz.cif.web.para.RecordParam;
+import com.txz.cif.web.ro.TFPayNotifyDTO;
+import com.txz.cif.web.vo.CreatePayVO;
 import com.txz.cif.web.vo.PaymentPriceVO;
-import org.springframework.web.bind.annotation.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.*;
 import tk.mybatis.mapper.entity.Condition;
+import tk.mybatis.mapper.entity.Example;
 import tk.mybatis.mapper.entity.Example.Criteria;
 
 import javax.annotation.Resource;
@@ -64,6 +74,12 @@ public class RechargeRecordApiController {
     @Resource
     private PaymentMethodService paymentMethodService;
     
+    @Resource
+    private TFPayService tfPayService;
+    
+    @Resource
+    private LockTemplate lockTemplate;
+    
     @GetMapping("/goodsList/{methodId:^\\d+$}")
     @ApiOperation(value = "充值商品", httpMethod = "GET")
     public Result<PaymentPriceVO> goodsList(@PathVariable("methodId") Long methodId) {
@@ -85,11 +101,12 @@ public class RechargeRecordApiController {
     
     @GetMapping("/add")
     @ApiOperation(value = "创建充值订单", httpMethod = "GET")
-    public Result add(@RequestParam BigDecimal amount, @RequestParam Long methodId, HttpServletRequest request) {
+    public Result<CreatePayVO> add(@RequestParam BigDecimal amount, @RequestParam Long methodId, HttpServletRequest request) {
         Long userId = authService.getTokenUserId(request);
         // if (goodsId == null) {
         //     return ResultGenerator.genFailResult(ResultCode.OAUTH_INVALID_ACCESS_TOKEN);
         // }
+        DateTime now = DateUtil.date();
         try {
             // Goods goods = goodsService.findById(goodsId);
             // if (goods == null) {
@@ -109,8 +126,10 @@ public class RechargeRecordApiController {
             RechargeRecord rechargeRecord = RechargeRecord.builder()
                     .discount(BigDecimal.ZERO)
                     .amount(amount)
-                    .currency("BDT").userId(userId)
-                    .userName(user.getName()).userPhone(user.getPhoneNo())
+                    .currency("BDT")
+                    .userId(userId)
+                    .userName(user.getName())
+                    .userPhone(user.getPhoneNo())
                     .bank(user.getBank())
                     .methodId(methodId)
                     .channelId(paymentChannel == null ? null : paymentChannel.getId())
@@ -119,21 +138,67 @@ public class RechargeRecordApiController {
                     .orderNo(orderNo)
                     .goodsId(-1)
                     .createUser(user.getName())
-                    .status(1).createTime(DateUtil.date())
+                    .status(1)
+                    .createTime(now)
                     .build();
             rechargeRecordService.save(rechargeRecord);
             // TODO 发起第三方支付
-            
+            // 获取渠道编码
+            String channelCode;
+            if (methodId == 1) {
+                channelCode = paymentChannel.getBkashPayCode();
+            } else if (methodId == 2) {
+                channelCode = paymentChannel.getNagadPayCode();
+            } else {
+                channelCode = paymentChannel.getRocketPayCode();
+            }
+            BizLogDTO bizLog;
+            String payUrl = "";
+            boolean issuccess = false;
+            switch (paymentChannel.getChannelName()) {
+                case "TFPAY":
+                    bizLog = tfPayService.createTFPayOrder(
+                            TFCreateOrderDTO.builder()
+                                    .payMemberid(paymentChannel.getMerchantNum())
+                                    .payOrderid(orderNo)
+                                    .payApplydate(now.toString())
+                                    .payChannelcode(channelCode)
+                                    .payNotifyurl(paymentChannel.getNotifyUrl())
+                                    .payAmount(amount.toString())
+                                    .ip(IpUtils.getIPAddress(request))
+                                    .build()
+                            , paymentChannel.getSecretKey());
+                    
+                    JSONObject resultJson = JSONUtil.parseObj(bizLog.getReceiveMessage());
+                    if (resultJson.getStr("status").equals("200")) {
+                        payUrl = resultJson.getStr("data");
+                        issuccess = Boolean.TRUE;
+                    }
+                    break;
+                default:
+                    throw new ServiceException("暂不支持此渠道");
+            }
             
             try {
                 // 新增日志
-                bizLogService.save(BizLog.builder().bizType(1).bizNo(orderNo)
-                        .type(1).createTime(DateUtil.date()).createUser(user.getName())
+                bizLogService.save(BizLog.builder()
+                        .bizType(1)
+                        .bizNo(orderNo)
+                        .type(1)
+                        .receiveMessage(bizLog.getReceiveMessage())
+                        .sendMessage(bizLog.getSendMessage())
+                        .createTime(now)
+                        .createUser(user.getName())
                         .memo("提交充值订单").build());
             } catch (Exception e) {
                 log.error("新增充值提交订单日志失败", e);
             }
-            return ResultGenerator.genSuccessResult(rechargeRecord);
+            
+            if (!issuccess) {
+                throw new ServiceException("创建支付单失败");
+            }
+            
+            return Result.success(CreatePayVO.builder().orderNo("orderNo").payUrl(payUrl).build());
         } catch (Exception e) {
             log.error("新增对象操作异常e:{}", e);
             return ResultGenerator.genFailResult(ResultCode.INTERNAL_SERVER_ERROR);
@@ -141,30 +206,68 @@ public class RechargeRecordApiController {
         
     }
     
-    @GetMapping("/callback")
-    @ApiOperation(value = "三方回调", httpMethod = "GET")
-    public Result<RechargeRecord> detail(@RequestParam String data) {
-        // TODO 回调成功
-        JSONObject json = JSONUtil.parseObj(data);
-        String orderNo = json.getStr("orderNo");
-        RechargeRecord record = rechargeRecordService.findBy("orderNo", orderNo);
-        if (record == null) {
-            return ResultGenerator.genFailResult("订单未找到");
-        }
-        if (StrUtil.equals("1", json.getStr("status"))) {
-            rechargeRecordService.success(record);
-        } else {
-            rechargeRecordService.fail(record);
+    /**
+     * 回调通知
+     */
+    @PostMapping("/callback/tfpay")
+    public Object callback(@ModelAttribute TFPayNotifyDTO dto) {
+        LockInfo lockInfo = lockTemplate.lock(String.format(MyConstants.TF_PAY_NOTIFY_LOCK, dto.getOrderid()), Long.valueOf(1000 * 60 * 2), Long.valueOf(1000 * 1), RedisTemplateLockExecutor.class);
+        if (null == lockInfo) {
+            return "WAIT";
         }
         try {
-            // 新增充值回调日志
-            bizLogService.save(BizLog.builder().bizType(1).bizNo(orderNo)
-                    .type(3).createTime(DateUtil.date()).createUser("第三方")
-                    .memo("充值回调").build());
-        } catch (Exception e) {
-            log.error("新增充值回调日志失败", e);
+            log.info("支付渠道--->tfpay,回调通知--->" + dto);
+            Condition condition = new Condition(PaymentChannel.class);
+            Example.Criteria criteria = condition.createCriteria();
+            criteria.andEqualTo("channelName", "TFPAY");
+            criteria.andEqualTo("isValid", 1);
+            List<PaymentChannel> paymentChannels = paymentChannelService.findByCondition(condition);
+            if (CollectionUtil.isEmpty(paymentChannels)) {
+                log.info("支付渠道不存在");
+                return "FAIL";
+            }
+            boolean checkSignResult = TFPayUtil.verifySignature(BeanUtil.copyProperties(dto, CheckSignDTO.class), paymentChannels.get(0).getSecretKey(), dto.getSign());
+            if (!checkSignResult) {
+                log.info("签名校验失败");
+                return "FAIL";
+            }
+            RechargeRecord record = rechargeRecordService.findBy("orderNo", dto.getOrderid());
+            if (ObjectUtil.isEmpty(record)) {
+                return "FAIL";
+            }
+            if (record.getStatus() != 1) {
+                return "FAIL";
+            }
+            record.setRealAmount(new BigDecimal(dto.getAmount()));
+            rechargeRecordService.success(record);
+            
+            // String orderNo = dto.getOrderid();
+            // RechargeRecord record = rechargeRecordService.findBy("orderNo", orderNo);
+            // if (record == null) {
+            //     return ResultGenerator.genFailResult("订单未找到");
+            // }
+            // if (StrUtil.equals("1", json.getStr("status"))) {
+            // rechargeRecordService.success(record);
+            // } else {
+            //     rechargeRecordService.fail(record);
+            // }
+            try {
+                // 新增充值回调日志
+                bizLogService.save(BizLog.builder()
+                        .bizType(1)
+                        .bizNo(dto.getOrderid())
+                        .type(3)
+                        .receiveMessage(dto.toString())
+                        .createTime(DateUtil.date())
+                        .createUser("TFPAY")
+                        .memo("充值回调").build());
+            } catch (Exception e) {
+                log.error("新增充值回调日志失败", e);
+            }
+            return "OK";
+        } finally {
+            this.lockTemplate.releaseLock(lockInfo);
         }
-        return ResultGenerator.genSuccessResult();
     }
     
     

+ 52 - 0
cif-service/src/main/java/com/txz/cif/web/ro/TFPayNotifyDTO.java

@@ -0,0 +1,52 @@
+package com.txz.cif.web.ro;
+
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/1
+ */
+
+@Data
+public class TFPayNotifyDTO {
+    
+    /**
+     * 商户编号 - 平台分配商户号
+     */
+    private String memberid;
+    
+    /**
+     * 商户订单号 - 订单号唯一, 字符长度20
+     */
+    private String orderid;
+    
+    /**
+     * 订单最终金额 - 订单金额 单位:元
+     */
+    private String amount;
+    
+    /**
+     * 平台订单号
+     */
+    private String transaction_id;
+    
+    /**
+     * 交易成功时间 - 时间格式:Y-m-d H:i:s
+     */
+    private String datetime;
+    
+    /**
+     * 交易状态 - "00" 为成功
+     */
+    private String returncode;
+    
+    /**
+     * 扩展返回 - 商户附加数据返回
+     */
+    private String attach;
+    
+    /**
+     * 签名
+     */
+    private String sign;
+}

+ 24 - 0
cif-service/src/main/java/com/txz/cif/web/vo/CreatePayVO.java

@@ -0,0 +1,24 @@
+package com.txz.cif.web.vo;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @author: MTD®️
+ * @date: 2025/9/2
+ */
+@Data
+@Builder
+public class CreatePayVO {
+    
+    /**
+     * 订单号
+     */
+    private String orderNo;
+    
+    /**
+     * 支付连接
+     */
+    private String payUrl;
+    
+}