Browse Source

更新

1. 下发表增加多个参数
2. 修复查询余额接口BUG
3. 增加支持银行查询接口
4. hambit代付对接(未完成)
test 7 tháng trước cách đây
mục cha
commit
1c5ed68b9c

+ 6 - 0
src/modules/dj/controller/admin/open.ts

@@ -109,6 +109,12 @@ export class PayOpenController extends BaseController {
     return this.ok(await this.repayService.queryBalance(payload));
   }
 
+  @CoolTag(TagTypes.IGNORE_TOKEN)
+  @Post('/getBanks', { summary: '代付银行查询', middleware: [WhiteIpMiddleware] })
+  public async getBanks(@Body(ALL) payload: any) {
+    return this.ok(await this.repayService.getBanks(payload));
+  }
+
   @CoolTag(TagTypes.IGNORE_TOKEN)
   @Post('/notifyTest', { summary: '回调通知' })
   public async notifyTest(@Body(ALL) payload: any) {

+ 10 - 4
src/modules/dj/entity/withdraw.ts

@@ -29,11 +29,17 @@ export class WithdrawEntity extends BaseEntity {
   @Column({ comment: '货币单位' })
   currency: string;
 
-  @Column({ comment: '收款账号' })
-  accountNo: string;
+  @Column({ comment: '用户证件信息' })
+  personIdType: string;
+
+  @Column({ comment: '用户证件ID' })
+  personId: string;
 
-  @Column({ comment: '收款人姓名' })
-  accountName: string;
+  @Column({ comment: '用户姓名' })
+  personName: string;
+
+  @Column({ comment: '银行账号' })
+  accountNo: string;
 
   @Column({ comment: '银行编码' })
   bankCode: string;

+ 14 - 2
src/modules/dj/service/channels/dispatch.ts

@@ -22,7 +22,7 @@ export class DispatchService extends BaseService {
 
   @Inject()
   hambitInrService: HambitInrService;
-  
+
   async refund(refund) {
     try {
       const channel = await this.channelService.queryByCode(refund.code);
@@ -38,7 +38,7 @@ export class DispatchService extends BaseService {
   async queryRefund(refund) {
     try {
       const channel = await this.channelService.queryByCode(refund.code);
-      if (!this[channel.service] || !this[channel.service].refund) {
+      if (!this[channel.service] || !this[channel.service].queryRefund) {
         throw new Error('该渠道不支持退款');
       }
       return await this[channel.service].queryRefund(refund);
@@ -132,4 +132,16 @@ export class DispatchService extends BaseService {
       throw new Error('代付回调处理接口失败,失败原因:' + e.message);
     }
   }
+
+
+  async getBanks(channel, data) {
+    try {
+      if (!this[channel.service] || !this[channel.service].queryBanks) {
+        return []
+      }
+      return await this[channel.service].queryBanks(data);
+    } catch (e) {
+      throw new Error('查询支持银行接口失败,失败原因:' + e.message);
+    }
+  }
 }

+ 76 - 0
src/modules/dj/service/channels/hambitBra.ts

@@ -13,6 +13,8 @@ const SECRET_KEY = 'cc2355c62627a1a437cf54d8b491aa6f82d30b31'
 const PAY_URL = '/api/v3/bra/createCollectingOrder';
 const QUERY_URL = '/api/v3/bra/query/collectingOrder'
 const BALANCE_URL = '/api/v3/bra/query/balance'
+const BANK_URL = '/api/v3/bra/query/bank';
+const WITHDRAW_URL = '/api/v3/bra/pix/createTransferOrder';
 
 const NOTIFY_HOST = `http://157.175.73.225/api/admin/dj/open/hambit/bra/notifyOrder`;
 
@@ -159,4 +161,78 @@ export class HambitBraService extends BaseService {
       throw new Error(msg);
     }
   }
+
+  async queryBanks(payload) {
+    const param = {
+      currencyType: 'BRL',
+    };
+    const timestamp = +moment();
+    const nonce = uuidv4();
+    const signStr = this.utils.signSort({
+      timestamp,
+      nonce,
+      access_key: ACCESS_KEY,
+      ...param
+    });
+    const sign = this.utils.signByHmacSha1(signStr, SECRET_KEY);
+    const res = await this.httpService.post(HOST + BANK_URL, param, {
+      headers: {
+        'access_key': ACCESS_KEY,
+        'timestamp': timestamp,
+        'nonce': nonce,
+        'sign': sign
+      }
+    });
+    this.logger.info('查询银行接口返回', param, JSON.stringify(res.data));
+    const { success, msg, data } = res.data;
+    if (success) {
+      return data.map(item => {
+        return {
+          bankName: item.bankNames,
+          bankCode: ''
+        }
+      })
+    } else {
+      throw new Error(msg);
+    }
+  }
+
+  async withdraw(payload) {
+    const param = {
+      currencyAmount: payload.amount.toFixed(2),
+      channelType: CHANNEL_TYPE,
+      externalOrderId: payload.orderNo,
+      personIdType: payload.personIdType,
+      personId: payload.personId,
+      personName: payload.personName,
+      accountType: payload.bankCode
+    };
+    const timestamp = +moment();
+    const nonce = uuidv4();
+    const signStr = this.utils.signSort({
+      timestamp,
+      nonce,
+      access_key: ACCESS_KEY,
+      ...param
+    });
+    const sign = this.utils.signByHmacSha1(signStr, SECRET_KEY);
+    const res = await this.httpService.post(HOST + WITHDRAW_URL, param, {
+      headers: {
+        'access_key': ACCESS_KEY,
+        'timestamp': timestamp,
+        'nonce': nonce,
+        'sign': sign
+      }
+    });
+    this.logger.info('下单接口返回', param, JSON.stringify(res.data));
+    const { success, msg, data } = res.data;
+    if (success && data.cashierUrl) {
+      return {
+        payUrl: data.cashierUrl,
+        traceNo: data?.currencyOrderVo?.orderId
+      };
+    } else {
+      throw new Error(msg);
+    }
+  }
 }

+ 36 - 0
src/modules/dj/service/channels/hambitInr.ts

@@ -13,6 +13,7 @@ const SECRET_KEY = 'cc2355c62627a1a437cf54d8b491aa6f82d30b31'
 const PAY_URL = '/api/v3/ind/createCollectingOrder';
 const QUERY_URL = '/api/v3/ind/query/collectingOrder'
 const BALANCE_URL = '/api/v3/ind/query/balance'
+const BANK_URL = '/api/v3/ind/query/bank'
 
 const NOTIFY_HOST = `http://157.175.73.225/api/admin/dj/open/hambit/inr/notifyOrder`;
 
@@ -157,4 +158,39 @@ export class HambitInrService extends BaseService {
       throw new Error(msg);
     }
   }
+
+  async queryBanks(payload) {
+    const param = {
+      bankName: '',
+    };
+    const timestamp = +moment();
+    const nonce = uuidv4();
+    const signStr = this.utils.signSort({
+      timestamp,
+      nonce,
+      access_key: ACCESS_KEY,
+      ...param
+    });
+    const sign = this.utils.signByHmacSha1(signStr, SECRET_KEY);
+    const res = await this.httpService.post(HOST + BANK_URL, param, {
+      headers: {
+        'access_key': ACCESS_KEY,
+        'timestamp': timestamp,
+        'nonce': nonce,
+        'sign': sign
+      }
+    });
+    this.logger.info('查询银行接口返回', param, JSON.stringify(res.data));
+    const { success, msg, data } = res.data;
+    if (success) {
+      return data.map(item => {
+        return {
+          bankName: item.bankName,
+          bankCode: item.bankCode
+        }
+      })
+    } else {
+      throw new Error(msg);
+    }
+  }
 }

+ 1 - 1
src/modules/dj/service/pay.ts

@@ -255,7 +255,7 @@ export class PayService extends BaseService {
     const signStr = this.utils.signSort(data);
     const verifySign = md5(signStr + `&key=${merchant.key}`);
     this.logger.info(
-      '代收下单加密结果',
+      '加密结果',
       sign,
       signStr,
       verifySign,

+ 59 - 44
src/modules/dj/service/repay.ts

@@ -14,12 +14,16 @@ import { WalletService } from './wallet';
 import { MchWithdrawChannelService } from './mchWithdrawChannel';
 import { RateService } from './rate';
 import { WithdrawStateService } from './withdrawState';
+import { PayService } from './pay';
 
 @Provide()
 export class RepayService extends BaseService {
   @Inject()
   httpService: HttpService;
 
+  @Inject()
+  payService: PayService;
+
   @Inject()
   withdrawStateService: WithdrawStateService;
 
@@ -50,14 +54,47 @@ export class RepayService extends BaseService {
   @Inject()
   utils: Utils;
 
+  async getBanks(payload) {
+    this.logger.info('代付支持银行查询', JSON.stringify(payload));
+    await this.validGetBanksParam(payload);
+    const merchant = await this.payService.validMerchant(payload.mchId);
+    await this.payService.validSign(payload, merchant);
+    const channels = await this.withdrawChannelService.queryByCurrency(payload.currency);
+    const result = await Promise.all(channels.map(item => {
+      return this.dispatchService.getBanks(item, { currency: payload.currency });
+    }))
+    let list = [];
+    result.forEach(item => {
+      list = list.concat(item)
+    })
+    return list;
+  }
+
+  async validGetBanksParam(payload) {
+    if (_.isEmpty(payload.mchId)) {
+      throw new Error('商户号【mchId】不能为空');
+    }
+    if (_.isEmpty(payload.currency)) {
+      throw new Error('货币单位【currency】不能为空');
+    }
+    if (_.isEmpty(payload.sign)) {
+      throw new Error('签名【sign】不能为空');
+    }
+  }
+
   async queryBalance(payload) {
     this.logger.info('余额查询', JSON.stringify(payload));
     await this.validQueryBalanceParam(payload);
-    const merchant = await this.validMerchant(payload.mchId);
-    await this.validSign(payload, merchant);
+    const merchant = await this.payService.validMerchant(payload.mchId);
+    await this.payService.validSign(payload, merchant);
     let wallet;
     if (payload.currency) {
-      wallet = [await this.walletService.queryByMchIdAndCurrency(payload.mchId, payload.currency)];
+      const data = await this.walletService.queryByMchIdAndCurrency(payload.mchId, payload.currency);
+      if (data) {
+        wallet = [data];
+      } else {
+        wallet = [];
+      }
     } else {
       wallet = await this.walletService.queryByMchId(payload.mchId);
     }
@@ -84,8 +121,8 @@ export class RepayService extends BaseService {
     try {
       this.logger.info('代付查询', JSON.stringify(payload));
       await this.validQueryParam(payload);
-      const merchant = await this.validMerchant(payload.mchId);
-      await this.validSign(payload, merchant);
+      const merchant = await this.payService.validMerchant(payload.mchId);
+      await this.payService.validSign(payload, merchant);
       const order = await this.withdrawService.findByOutOrderNo(
         payload.outOrderNo
       );
@@ -124,9 +161,9 @@ export class RepayService extends BaseService {
     try {
       this.logger.info('代付下单', JSON.stringify(payload));
       await this.validParam(payload);
-      const merchant = await this.validMerchant(payload.mchId);
+      const merchant = await this.payService.validMerchant(payload.mchId);
       const rate = await this.validRate(payload.mchId, payload.currency);
-      await this.validSign(payload, merchant);
+      await this.payService.validSign(payload, merchant);
       await this.validOutOrderNo(payload.outOrderNo);
       const channel = await this.getChannel(merchant.mchId, payload.currency, payload.amount);
       let channelCharge = (+payload.amount * +channel.rate) / 100 + +channel.basicFee;
@@ -146,8 +183,10 @@ export class RepayService extends BaseService {
         mchId: merchant.mchId,
         amount: payload.amount,
         currency: payload.currency,
+        personIdType: payload.personIdType,
+        personId: payload.personId,
+        personName: payload.personName,
         accountNo: payload.accountNo,
-        accountName: payload.accountName,
         bankCode: payload.bankCode,
         bankName: payload.bankName,
         channelCharge,
@@ -202,12 +241,18 @@ export class RepayService extends BaseService {
     if (_.isEmpty(payload.currency)) {
       throw new Error('货币单位【currency】不能为空');
     }
+    if (_.isEmpty(payload.personIdType)) {
+      throw new Error('用户证件信息【personIdType】不能为空');
+    }
+    if (_.isEmpty(payload.personId)) {
+      throw new Error('用户证件号【personId】不能为空');
+    }
+    if (_.isEmpty(payload.personName)) {
+      throw new Error('用户姓名【personName】不能为空');
+    }
     if (_.isEmpty(payload.accountNo)) {
       throw new Error('银行账号【accountNo】不能为空');
     }
-    if (_.isEmpty(payload.accountName)) {
-      throw new Error('收款人姓名【accountName】不能为空');
-    }
     if (_.isEmpty(payload.bankName)) {
       throw new Error('银行名称【bankName】不能为空');
     }
@@ -246,17 +291,6 @@ export class RepayService extends BaseService {
     return channel;
   }
 
-  async validMerchant(mchId) {
-    const merchant = await this.merchantService.getByMchId(mchId);
-    if (!merchant) {
-      throw new Error('商户不存在');
-    }
-    if (+merchant.status !== 1) {
-      throw new Error('商户状态异常');
-    }
-    return merchant;
-  }
-
   async validRate(mchId, currency) {
     const rate = await this.rateService.getOne({
       mchId,
@@ -269,26 +303,6 @@ export class RepayService extends BaseService {
     return rate;
   }
 
-  async validSign(payload, merchant) {
-    const data = { ...payload };
-    const sign = data.sign;
-    delete data.sign;
-    const signStr = this.utils.signSort(data);
-    const verifySign = md5(signStr + `&key=${merchant.key}`);
-    this.logger.info(
-      '代付下单加密结果',
-      sign,
-      signStr,
-      verifySign,
-      sign === verifySign
-    );
-    if (sign !== verifySign) {
-      throw new Error(
-        '验签失败,请确认签名是否正确,正确待签名字符串为:' + signStr
-      );
-    }
-  }
-
   async validOutOrderNo(outOrderNo) {
     const order = await this.withdrawService.findByOutOrderNo(outOrderNo);
     if (order) {
@@ -299,8 +313,9 @@ export class RepayService extends BaseService {
   async validBalance(merchant, currency, amount) {
     const wallet = await this.walletService.queryByMchIdAndCurrency(merchant.mchId, currency);
     const { balance = 0, freeze = 0 } = wallet;
-    if (+amount > +balance - Math.abs(+freeze)) {
-      throw new Error('当前商户余额不足,请确认!当前商户可提现余额:' + (+balance - +freeze));
+    const withdrawAmount = (+balance - Math.abs(+freeze)).toFixed(2)
+    if (+amount > +withdrawAmount) {
+      throw new Error('当前商户余额不足,请确认!当前商户可提现余额:' + +withdrawAmount);
     }
   }
 }

+ 4 - 2
src/modules/dj/service/withdraw.ts

@@ -124,7 +124,7 @@ export class WithdrawService extends BaseService {
     }
     const sql = `SELECT a.id, a.orderNo, a.outOrderNo, a.traceNo, a.mchId,  a.code, a.amount, a.currency, 
     ${this.utils.isMerchant(roleIds) ? '' : 'a.channelCharge,'} 
-    a.charge, a.status, a.notifyUrl,  a.remark, a.createTime, a.updateTime, a.accountNo, a.accountName, a.bankName, a.bankCode 
+    a.charge, a.status, a.notifyUrl,  a.remark, a.createTime, a.updateTime, a.personIdType, a.personId, a.accountNo, a.personName, a.bankName, a.bankCode 
     FROM dj_withdraw a WHERE 1=1
       ${this.setSql(orderNo, 'and a.orderNo like ?', [`%${orderNo}%`])}
       ${this.setSql(outOrderNo, 'and a.outOrderNo like ?', [`%${outOrderNo}%`])}
@@ -163,8 +163,10 @@ export class WithdrawService extends BaseService {
     if (!withdraw) {
       throw new Error('订单不存在');
     }
+    withdraw.personIdType = param.personIdType;
+    withdraw.personId = param.personId;
+    withdraw.personName = param.personName;
     withdraw.accountNo = param.accountNo;
-    withdraw.accountName = param.accountName;
     withdraw.bankCode = param.bankCode;
     withdraw.bankName = param.bankName;
     withdraw.remark = param.remark;

+ 7 - 0
src/modules/dj/service/withdrawChannel.ts

@@ -10,6 +10,13 @@ export class WithdrawChannelService extends BaseService {
   @InjectEntityModel(WithdrawChannelEntity)
   withdrawChannelEntity: Repository<WithdrawChannelEntity>;
 
+  async queryByCurrency(currency) {
+    return await this.withdrawChannelEntity.findBy({
+      status: 1,
+      currency,
+    });
+  }
+
   async queryByCode(code) {
     return await this.withdrawChannelEntity.findOneBy({
       status: 1,