|
@@ -11,15 +11,24 @@ import { Utils } from '../../../comm/utils';
|
|
import * as md5 from 'md5';
|
|
import * as md5 from 'md5';
|
|
import { WithdrawService } from './withdraw';
|
|
import { WithdrawService } from './withdraw';
|
|
import { WalletService } from './wallet';
|
|
import { WalletService } from './wallet';
|
|
|
|
+import { MchWithdrawChannelService } from './mchWithdrawChannel';
|
|
|
|
+import { RateService } from './rate';
|
|
|
|
+import { WithdrawStateService } from './withdrawState';
|
|
|
|
|
|
@Provide()
|
|
@Provide()
|
|
export class RepayService extends BaseService {
|
|
export class RepayService extends BaseService {
|
|
@Inject()
|
|
@Inject()
|
|
httpService: HttpService;
|
|
httpService: HttpService;
|
|
|
|
|
|
|
|
+ @Inject()
|
|
|
|
+ withdrawStateService: WithdrawStateService;
|
|
|
|
+
|
|
@Inject()
|
|
@Inject()
|
|
withdrawChannelService: WithdrawChannelService;
|
|
withdrawChannelService: WithdrawChannelService;
|
|
|
|
|
|
|
|
+ @Inject()
|
|
|
|
+ mchWithdrawChannelService: MchWithdrawChannelService;
|
|
|
|
+
|
|
@Inject()
|
|
@Inject()
|
|
merchantService: MerchantService;
|
|
merchantService: MerchantService;
|
|
|
|
|
|
@@ -32,17 +41,50 @@ export class RepayService extends BaseService {
|
|
@Inject()
|
|
@Inject()
|
|
walletService: WalletService;
|
|
walletService: WalletService;
|
|
|
|
|
|
|
|
+ @Inject()
|
|
|
|
+ rateService: RateService;
|
|
|
|
+
|
|
@Logger()
|
|
@Logger()
|
|
logger: ILogger;
|
|
logger: ILogger;
|
|
|
|
|
|
@Inject()
|
|
@Inject()
|
|
utils: Utils;
|
|
utils: Utils;
|
|
|
|
|
|
- async query(payload) {
|
|
|
|
|
|
+ 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);
|
|
|
|
+ let wallet;
|
|
|
|
+ if (payload.currency) {
|
|
|
|
+ wallet = [await this.walletService.queryByMchIdAndCurrency(payload.mchId, payload.currency)];
|
|
|
|
+ } else {
|
|
|
|
+ wallet = await this.walletService.queryByMchId(payload.mchId);
|
|
|
|
+ }
|
|
|
|
+ return wallet.map(item => {
|
|
|
|
+ return {
|
|
|
|
+ currency: item.currency,
|
|
|
|
+ balance: item.balance,
|
|
|
|
+ withdrawBalance: (+item.balance - +item.freeze).toFixed(2),
|
|
|
|
+ freeze: item.freeze,
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async validQueryBalanceParam(payload) {
|
|
|
|
+ if (_.isEmpty(payload.mchId)) {
|
|
|
|
+ throw new Error('商户号【mchId】不能为空');
|
|
|
|
+ }
|
|
|
|
+ if (_.isEmpty(payload.sign)) {
|
|
|
|
+ throw new Error('签名【sign】不能为空');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async queryWithdraw(payload) {
|
|
try {
|
|
try {
|
|
this.logger.info('代付查询', JSON.stringify(payload));
|
|
this.logger.info('代付查询', JSON.stringify(payload));
|
|
await this.validQueryParam(payload);
|
|
await this.validQueryParam(payload);
|
|
- const merchant = await this.validMerchant(payload);
|
|
|
|
|
|
+ const merchant = await this.validMerchant(payload.mchId);
|
|
await this.validSign(payload, merchant);
|
|
await this.validSign(payload, merchant);
|
|
const order = await this.withdrawService.findByOutOrderNo(
|
|
const order = await this.withdrawService.findByOutOrderNo(
|
|
payload.outOrderNo
|
|
payload.outOrderNo
|
|
@@ -56,6 +98,8 @@ export class RepayService extends BaseService {
|
|
outOrderNo: payload.outOrderNo,
|
|
outOrderNo: payload.outOrderNo,
|
|
amount: order.amount,
|
|
amount: order.amount,
|
|
status: order.status,
|
|
status: order.status,
|
|
|
|
+ charge: order.charge,
|
|
|
|
+ remark: order.remark,
|
|
};
|
|
};
|
|
} catch (err) {
|
|
} catch (err) {
|
|
this.logger.error('查询失败', JSON.stringify(payload), err.message);
|
|
this.logger.error('查询失败', JSON.stringify(payload), err.message);
|
|
@@ -75,24 +119,25 @@ export class RepayService extends BaseService {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- async order(payload) {
|
|
|
|
|
|
+ async withdraw(payload) {
|
|
let withdraw: any = null;
|
|
let withdraw: any = null;
|
|
try {
|
|
try {
|
|
this.logger.info('代付下单', JSON.stringify(payload));
|
|
this.logger.info('代付下单', JSON.stringify(payload));
|
|
await this.validParam(payload);
|
|
await this.validParam(payload);
|
|
- const merchant = await this.validMerchant(payload);
|
|
|
|
- const channel = await this.validPayWay(payload);
|
|
|
|
|
|
+ const merchant = await this.validMerchant(payload.mchId);
|
|
|
|
+ const rate = await this.validRate(payload.mchId, payload.currency);
|
|
await this.validSign(payload, merchant);
|
|
await this.validSign(payload, merchant);
|
|
- await this.validOutOrderNo(payload);
|
|
|
|
- await this.validBalance(merchant, payload);
|
|
|
|
|
|
+ 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;
|
|
let channelCharge = (+payload.amount * +channel.rate) / 100 + +channel.basicFee;
|
|
- if(channelCharge < payload.feeMin) {
|
|
|
|
- channelCharge = payload.feeMin;
|
|
|
|
|
|
+ if (channelCharge < channel.feeMin) {
|
|
|
|
+ channelCharge = channel.feeMin;
|
|
}
|
|
}
|
|
- let charge = (+payload.amount * +merchant.withdrawRate) / 100 + +merchant.withdrawBasicFee;
|
|
|
|
- if(charge < merchant.withdrawFeeMin) {
|
|
|
|
- charge = merchant.withdrawFeeMin;
|
|
|
|
|
|
+ let charge = (+payload.amount * +rate.rate) / 100 + +rate.basicFee;
|
|
|
|
+ if (charge < rate.feeMin) {
|
|
|
|
+ charge = rate.feeMin;
|
|
}
|
|
}
|
|
|
|
+ await this.validBalance(merchant, payload.currency, payload.amount + charge);
|
|
withdraw = {
|
|
withdraw = {
|
|
orderNo: this.utils.createOrderNo('DS'),
|
|
orderNo: this.utils.createOrderNo('DS'),
|
|
outOrderNo: payload.outOrderNo,
|
|
outOrderNo: payload.outOrderNo,
|
|
@@ -108,10 +153,21 @@ export class RepayService extends BaseService {
|
|
channelCharge,
|
|
channelCharge,
|
|
charge,
|
|
charge,
|
|
notifyUrl: payload.notifyUrl,
|
|
notifyUrl: payload.notifyUrl,
|
|
- status: 1,
|
|
|
|
|
|
+ status: this.withdrawStateService.STATUS.ACCEPT,
|
|
remark: ''
|
|
remark: ''
|
|
};
|
|
};
|
|
await this.withdrawService.create(withdraw);
|
|
await this.withdrawService.create(withdraw);
|
|
|
|
+ await this.walletService.updateBalance({
|
|
|
|
+ orderNo: withdraw.orderNo,
|
|
|
|
+ mchId: withdraw.mchId,
|
|
|
|
+ type: 2,
|
|
|
|
+ amount: 0,
|
|
|
|
+ currency: withdraw.currency,
|
|
|
|
+ freeze: (+withdraw.amount + +withdraw.charge)
|
|
|
|
+ })
|
|
|
|
+ if (withdraw.code) {
|
|
|
|
+ this.withdrawStateService.stateTo(withdraw.status, this.withdrawStateService.STATUS.PROCESSIONG, withdraw);
|
|
|
|
+ }
|
|
return {
|
|
return {
|
|
orderNo: withdraw.orderNo,
|
|
orderNo: withdraw.orderNo,
|
|
outOrderNo: withdraw.outOrderNo,
|
|
outOrderNo: withdraw.outOrderNo,
|
|
@@ -120,11 +176,6 @@ export class RepayService extends BaseService {
|
|
};
|
|
};
|
|
} catch (err) {
|
|
} catch (err) {
|
|
this.logger.error('代付失败', JSON.stringify(payload), err.message);
|
|
this.logger.error('代付失败', JSON.stringify(payload), err.message);
|
|
- if (withdraw) {
|
|
|
|
- withdraw.remark = err.message;
|
|
|
|
- withdraw.status = 3;
|
|
|
|
- await this.withdrawService.create(withdraw);
|
|
|
|
- }
|
|
|
|
throw new CoolCommException(err.message);
|
|
throw new CoolCommException(err.message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -143,10 +194,10 @@ export class RepayService extends BaseService {
|
|
throw new Error('代付成功回调地址【notifyUrl】不能为空');
|
|
throw new Error('代付成功回调地址【notifyUrl】不能为空');
|
|
}
|
|
}
|
|
if (_.isEmpty(payload.amount + '')) {
|
|
if (_.isEmpty(payload.amount + '')) {
|
|
- throw new Error('支付金额【amount】不能为空');
|
|
|
|
|
|
+ throw new Error('金额【amount】不能为空');
|
|
}
|
|
}
|
|
if (+payload.amount <= 0) {
|
|
if (+payload.amount <= 0) {
|
|
- throw new Error('支付金额【amount】必须大于0');
|
|
|
|
|
|
+ throw new Error('金额【amount】必须大于0');
|
|
}
|
|
}
|
|
if (_.isEmpty(payload.currency)) {
|
|
if (_.isEmpty(payload.currency)) {
|
|
throw new Error('货币单位【currency】不能为空');
|
|
throw new Error('货币单位【currency】不能为空');
|
|
@@ -168,8 +219,9 @@ export class RepayService extends BaseService {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- async validPayWay(payload) {
|
|
|
|
- if(!payload.payType) {
|
|
|
|
|
|
+ async getChannel(mchId, currency, amount) {
|
|
|
|
+ const mchWithdrawChannel = await this.mchWithdrawChannelService.getByMchIdAndCurrency(mchId, currency);
|
|
|
|
+ if (!mchWithdrawChannel) {
|
|
return {
|
|
return {
|
|
code: '',
|
|
code: '',
|
|
rate: 0,
|
|
rate: 0,
|
|
@@ -177,32 +229,46 @@ export class RepayService extends BaseService {
|
|
feeMin: 0
|
|
feeMin: 0
|
|
};
|
|
};
|
|
}
|
|
}
|
|
- const channel = await this.withdrawChannelService.queryByCode(payload.payType);
|
|
|
|
|
|
+ const channel = await this.withdrawChannelService.queryByCode(mchWithdrawChannel.code);
|
|
if (!channel) {
|
|
if (!channel) {
|
|
- throw new Error('当前通道码不可用');
|
|
|
|
|
|
+ return {
|
|
|
|
+ code: '',
|
|
|
|
+ rate: 0,
|
|
|
|
+ basicFee: 0,
|
|
|
|
+ feeMin: 0
|
|
|
|
+ };
|
|
}
|
|
}
|
|
let max = channel.max || 0;
|
|
let max = channel.max || 0;
|
|
let min = channel.min || 0;
|
|
let min = channel.min || 0;
|
|
- if (+payload.amount > +max || +payload.amount < +min) {
|
|
|
|
|
|
+ if (+amount > +max || +amount < +min) {
|
|
throw new Error(`单笔最大限额【${+min}, ${+max}】`);
|
|
throw new Error(`单笔最大限额【${+min}, ${+max}】`);
|
|
}
|
|
}
|
|
return channel;
|
|
return channel;
|
|
}
|
|
}
|
|
|
|
|
|
- async validMerchant(payload) {
|
|
|
|
- const merchant = await this.merchantService.getByMchId(payload.mchId);
|
|
|
|
|
|
+ async validMerchant(mchId) {
|
|
|
|
+ const merchant = await this.merchantService.getByMchId(mchId);
|
|
if (!merchant) {
|
|
if (!merchant) {
|
|
throw new Error('商户不存在');
|
|
throw new Error('商户不存在');
|
|
}
|
|
}
|
|
if (+merchant.status !== 1) {
|
|
if (+merchant.status !== 1) {
|
|
throw new Error('商户状态异常');
|
|
throw new Error('商户状态异常');
|
|
}
|
|
}
|
|
- if (+merchant.withdrawStatus !== 1) {
|
|
|
|
- throw new Error('商户代付状态异常');
|
|
|
|
- }
|
|
|
|
return merchant;
|
|
return merchant;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ async validRate(mchId, currency) {
|
|
|
|
+ const rate = await this.rateService.getOne({
|
|
|
|
+ mchId,
|
|
|
|
+ currency,
|
|
|
|
+ type: 2
|
|
|
|
+ });
|
|
|
|
+ if (!rate) {
|
|
|
|
+ throw new Error('商户未开通代付渠道');
|
|
|
|
+ }
|
|
|
|
+ return rate;
|
|
|
|
+ }
|
|
|
|
+
|
|
async validSign(payload, merchant) {
|
|
async validSign(payload, merchant) {
|
|
const data = { ...payload };
|
|
const data = { ...payload };
|
|
const sign = data.sign;
|
|
const sign = data.sign;
|
|
@@ -223,18 +289,18 @@ export class RepayService extends BaseService {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- async validOutOrderNo(payload: any) {
|
|
|
|
- const order = await this.withdrawService.findByOutOrderNo(payload.outOrderNo);
|
|
|
|
|
|
+ async validOutOrderNo(outOrderNo) {
|
|
|
|
+ const order = await this.withdrawService.findByOutOrderNo(outOrderNo);
|
|
if (order) {
|
|
if (order) {
|
|
throw new Error('商户订单号已存在,请勿重复下单');
|
|
throw new Error('商户订单号已存在,请勿重复下单');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- async validBalance(merchant, data) {
|
|
|
|
- const wallet = await this.walletService.queryByMerchant(merchant.mchId);
|
|
|
|
- const { balance = 0 } = wallet;
|
|
|
|
- if(+data.amount > +balance) {
|
|
|
|
- throw new Error('当前商户余额不足,请确认!当前商户余额:' + balance);
|
|
|
|
|
|
+ async validBalance(merchant, currency, amount) {
|
|
|
|
+ const wallet = await this.walletService.queryByMchIdAndCurrency(merchant.mchId, currency);
|
|
|
|
+ const { balance = 0, freeze = 0 } = wallet;
|
|
|
|
+ if (+amount > +balance - +freeze) {
|
|
|
|
+ throw new Error('当前商户余额不足,请确认!当前商户可提现余额:' + (+balance - +freeze));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|