Browse Source

feat: 转账手续费 3-13

max 4 months ago
parent
commit
578dd852bf

+ 5 - 0
src/config/config.max.ts

@@ -77,4 +77,9 @@ export default {
   //     },
   //   },
   // },
+  midwayLogger: {
+    default: {
+      dir: 'logs/va_max',
+    },
+  },
 } as MidwayConfig;

+ 5 - 0
src/config/config.prod.ts

@@ -49,4 +49,9 @@ export default {
   },
   mode: 'file',
   tmpdir: join(__dirname, '../tmp'), // 临时文件目录
+  midwayLogger: {
+    default: {
+      dir: `${process.env.HOME}/logs/va_server`,
+    },
+  },
 } as MidwayConfig;

+ 5 - 0
src/config/config.sandbox.ts

@@ -51,4 +51,9 @@ export default {
   },
   mode: 'file',
   tmpdir: join(__dirname, '../tmp'), // 临时文件目录
+  midwayLogger: {
+    default: {
+      dir: `${process.env.HOME}/logs/va_sandbox`,
+    },
+  },
 } as MidwayConfig;

+ 125 - 35
src/modules/api/service/admin/applications.ts

@@ -19,6 +19,7 @@ import {
 import * as md5 from 'md5';
 import { PayeeAddressEntity } from '../../../payment/entity/payee_address';
 import { EasyOpenService } from '../open';
+import { WebHookCommonService } from '../webhook_utils/common';
 
 /**
  * 开户管理
@@ -62,6 +63,9 @@ export class applicationsService extends BaseService {
   @InjectEntityModel(OpenApplicationsEntity)
   openApplicationsEntity: Repository<OpenApplicationsEntity>;
 
+  @Inject()
+  webHookCommonService: WebHookCommonService;
+
   async addApplications(params, { fail, ok }) {
     const mchInFo = await this.openAccountEntity.findOne({
       where: {
@@ -112,35 +116,39 @@ export class applicationsService extends BaseService {
   查询账户余额信息
    */
   async getApplicationsListByMchId() {
-    const merchantInfo = await this.getMerchantInfo();
-    const bank_accounts_res = await this.easyPayAdapter.request(
-      'GET',
-      `/v1/bank_accounts`,
-      {
-        page: 1,
-        size: 10,
-        account_id: merchantInfo.account_id,
-      }
-    );
-    const balances_res = await this.easyPayAdapter.request(
-      'GET',
-      `/v3/accounts/${merchantInfo.account_id}/balances`
-    );
-    bank_accounts_res.data = bank_accounts_res.data.map(elm => {
-      let amount = 0;
-      if (balances_res.data.length > 0) {
-        balances_res.data.forEach(item => {
-          if (!amount && item.currency === elm.currency) {
-            amount = item.amount;
-          }
-        });
-      }
-      return {
-        ...elm,
-        amount,
-      };
-    });
-    return bank_accounts_res;
+    try {
+      const merchantInfo = await this.getMerchantInfo();
+      const bank_accounts_res = await this.easyPayAdapter.request(
+        'GET',
+        `/v1/bank_accounts`,
+        {
+          page: 1,
+          size: 10,
+          account_id: merchantInfo.account_id,
+        }
+      );
+      const balances_res = await this.easyPayAdapter.request(
+        'GET',
+        `/v3/accounts/${merchantInfo.account_id}/balances`
+      );
+      bank_accounts_res.data = bank_accounts_res.data.map(elm => {
+        let amount = 0;
+        if (balances_res.data.length > 0) {
+          balances_res.data.forEach(item => {
+            if (!amount && item.currency === elm.currency) {
+              amount = item.amount;
+            }
+          });
+        }
+        return {
+          ...elm,
+          amount,
+        };
+      });
+      return bank_accounts_res;
+    } catch (error) {
+      console.log(147, error);
+    }
   }
 
   async getTransactionsListByMchId(params) {
@@ -188,6 +196,42 @@ export class applicationsService extends BaseService {
   // 换汇
   async exchanges(params) {
     const merchantInfo = await this.getMerchantInfo();
+    // 获取指定卖出币种余额
+    const sellCurrencyByCurrency = await this.getAccountsBalancesByCurrency(
+      merchantInfo.account_id,
+      params.sell_currency
+    );
+    const buy_amount_exchange_rate = await this.webHookCommonService.exchange_rates_buy_amount(params) // 卖出币种的金额
+    if (buy_amount_exchange_rate > sellCurrencyByCurrency) {
+      this.ctx.status = 400;
+      this.ctx.body = {
+        msg: `${params.sell_currency}不足以兑换${params.buy_currency}${params.buy_amount}`,
+      };
+      return;
+    }
+    // 获取指定卖入币种余额
+    const buyCurrencyByCurrency = await this.getAccountsBalancesByCurrency(
+      merchantInfo.account_id,
+      params.buy_currency
+    );
+    // 获取费率信息
+    const withdrawChannelFee =
+      await this.webHookCommonService.getWithdrawChannelFee({
+        account_id: merchantInfo.account_id,
+        currency: params.buy_currency,
+        order_type: OrderType.EXCHANGE,
+        channel: 'EASYPAY',
+        amount: params.buy_amount,
+        mch_id: merchantInfo.mch_id,
+      });
+    if (buyCurrencyByCurrency / 100 < params.amount + withdrawChannelFee) {
+      this.ctx.status = 400;
+      this.ctx.body = {
+        msg: `换汇之后的${params.buy_currency}余额不足以支付手续费(${withdrawChannelFee}元)`,
+      };
+      return;
+    }
+    // 获取交易之后的汇率金额是否足额
     const res = await this.easyPayAdapter.request('POST', `/v1/exchanges`, {
       account_id: merchantInfo.account_id,
       ...params,
@@ -198,7 +242,6 @@ export class applicationsService extends BaseService {
   }
   // 转账
   async transfer(params) {
-    console.log(186, params);
     const to_merchantInfo = await this.getMerchantInfo(params.to_mch_id);
     const merchantInfo = await this.getMerchantInfo();
 
@@ -212,6 +255,30 @@ export class applicationsService extends BaseService {
       amount: params.amount * 100,
       purpose: params.purpose,
     };
+
+    // 获取指定币种余额
+    const balancesByCurrency = await this.getAccountsBalancesByCurrency(
+      merchantInfo.account_id,
+      params.currency
+    );
+    // 获取费率信息
+    const withdrawChannelFee =
+      await this.webHookCommonService.getWithdrawChannelFee({
+        account_id: merchantInfo.account_id,
+        currency: params.currency,
+        order_type: OrderType.TRANSFER,
+        channel: 'EASYPAY',
+        amount: params.amount,
+        mch_id: merchantInfo.mch_id,
+      });
+    if (balancesByCurrency / 100 < params.amount + withdrawChannelFee) {
+      this.ctx.status = 400;
+      this.ctx.body = {
+        msg: '余额不足以支付手续费',
+      };
+      return;
+    }
+
     const res = await this.easyPayAdapter.request(
       'POST',
       `/v1/transfers`,
@@ -239,6 +306,28 @@ export class applicationsService extends BaseService {
         id: params.beneficiary_id,
       },
     });
+    // 获取指定币种余额
+    const balancesByCurrency = await this.getAccountsBalancesByCurrency(
+      merchantInfo.account_id,
+      params.currency
+    );
+    // 获取费率信息
+    const withdrawChannelFee =
+      await this.webHookCommonService.getWithdrawChannelFee({
+        account_id: merchantInfo.account_id,
+        currency: params.currency,
+        order_type: OrderType.PAYMENT,
+        channel: 'EASYPAY',
+        amount: params.amount,
+        mch_id: merchantInfo.mch_id,
+      });
+    if (balancesByCurrency / 100 < params.amount + withdrawChannelFee) {
+      this.ctx.status = 400;
+      this.ctx.body = {
+        msg: '余额不足以支付手续费',
+      };
+      return;
+    }
 
     const paymentsParams = {
       request_id: md5(
@@ -289,17 +378,18 @@ export class applicationsService extends BaseService {
 
   // amount 单位为分
   async getAccountsBalancesByCurrency(account_id, currency) {
-     const balances = await this.easyPayAdapter.request(
+    const balances = await this.easyPayAdapter.request(
       'GET',
       `/v3/accounts/${account_id}/balances`,
       {}
     );
     let amount = 0;
+    // console.log(334, balances, account_id, currency);
     balances.data.forEach(elm => {
-      if(elm.currency === currency && !amount ) {
-        amount = elm.amount
+      if (elm.currency === currency && !amount) {
+        amount = elm.amount;
       }
-    })
-    return amount
+    });
+    return amount;
   }
 }

+ 12 - 0
src/modules/api/service/webhook_utils/common.ts

@@ -7,6 +7,7 @@ import { OpenUserEntity } from '../../entity/open_user';
 import { WithdrawChannelEntity } from '../../entity/withdrawChannel';
 import { applicationsService } from '../admin/applications';
 import { OrderType } from '../../entity/open_payment_order';
+import { EasyPayAdapter } from '../../../payment/adapter/easypay.adapter';
 
 /**
  * webhook的公共方法
@@ -25,6 +26,9 @@ export class WebHookCommonService extends BaseService {
   @Inject()
   applicationsService: applicationsService;
 
+  @Inject()
+    easyPayAdapter: EasyPayAdapter;
+
   // 如果查询都是空的延迟处理
   async getAccountInfo(account_id) {
     const openAccount = await this.openAccountEntity.findOne({
@@ -102,4 +106,12 @@ export class WebHookCommonService extends BaseService {
       }, settime);
     });
   }
+  // 获取汇率 
+  async exchange_rates_buy_amount(params: any) {
+    const exchange_rates = await this.easyPayAdapter.request("GET", "/v1/exchange_rates", params);
+    if ( exchange_rates.data.length ) {
+      return params.buy_amount / exchange_rates.data[0].exchange_rate
+    }
+    return 0 
+  }
 }

+ 4 - 40
src/modules/payment/adapter/easypay.adapter.ts

@@ -172,50 +172,13 @@ export class EasyPayAdapter extends BaseService {
   // 获取访问令牌的方法
   async getAccessToken() {
     const currentTime = Math.floor(Date.now() / 1000); // 获取当前时间戳
-    const authUrl = await this.get_authUrl();
-    const clientId = await this.get_clientId();
-    const clientSecret = await this.get_clientSecret();
     const expiresIn = await this.get_expiresIn();
     const accessToken = await this.get_accessToken();
-    const refreshToken = await this.get_refreshToken();
-    if (((await this.get_accessToken()) && expiresIn) || 0 > currentTime) {
+    if (accessToken && Number.parseInt(`${expiresIn}`) > currentTime) {
       return accessToken; // 如果令牌有效,返回访问令牌
     }
-
-    // 如果没有重新登录
-    if (!refreshToken) {
-      // throw new Error('No refresh token available. Please login first.'); // 如果没有刷新令牌,抛出错误
-      await this.login();
-      return await this.get_accessToken(); // 返回新的访问令牌
-    }
-
-    try {
-      const response = await axios.post(`${authUrl}`, {
-        client_id: clientId, // 传递客户端ID
-        client_secret: clientSecret, // 传递客户端密钥
-        refresh_token: refreshToken, // 传递刷新令牌
-        grant_type: 'refresh_token', // 使用刷新令牌授权类型
-      });
-
-      const data = response.data;
-      await this.midwayCache.set(
-        'easypay:adapter:accessToken',
-        data.access_token
-      ); // 初始化访问令牌为空
-      await this.midwayCache.set(
-        'easypay:adapter:refreshToken',
-        data.refresh_token
-      ); // 初始化刷新令牌为空
-      await this.midwayCache.set(
-        'easypay:adapter:expiresIn',
-        currentTime + data.expires_in - 10
-      ); // 初始化过期时间为0
-      console.log('Login successful. Access Token acquired. refreshToken'); // 登录成功日志
-      return await this.get_accessToken(); // 返回新的访问令牌
-    } catch (error) {
-      console.error('Error fetching access token:', error); // 获取令牌错误日志
-      throw error; // 抛出错误
-    }
+    await this.login();
+    return await this.get_accessToken(); // 返回新的访问令牌
   }
   // 登录方法,用于获取访问令牌和刷新令牌
   async login() {
@@ -234,6 +197,7 @@ export class EasyPayAdapter extends BaseService {
           url: `${authUrl}`,
           data: `${dataString}`,
         };
+        // console.log(239, config);
         const response = await axios(config);
 
         const data = response.data;