|
@@ -10,6 +10,8 @@ import { DispatchService } from './channels/dispatch';
|
|
|
import { Utils } from '../../../comm/utils';
|
|
|
import * as md5 from 'md5';
|
|
|
import { OrderService } from './order';
|
|
|
+import { RateService } from './rate';
|
|
|
+import { MchChannelService } from './mchChannel';
|
|
|
|
|
|
@Provide()
|
|
|
export class PayService extends BaseService {
|
|
@@ -19,6 +21,9 @@ export class PayService extends BaseService {
|
|
|
@Inject()
|
|
|
channelService: ChannelService;
|
|
|
|
|
|
+ @Inject()
|
|
|
+ mchChannelService: MchChannelService;
|
|
|
+
|
|
|
@Inject()
|
|
|
merchantService: MerchantService;
|
|
|
|
|
@@ -28,6 +33,9 @@ export class PayService extends BaseService {
|
|
|
@Inject()
|
|
|
dispatchService: DispatchService;
|
|
|
|
|
|
+ @Inject()
|
|
|
+ rateService: RateService;
|
|
|
+
|
|
|
@Logger()
|
|
|
logger: ILogger;
|
|
|
|
|
@@ -38,7 +46,7 @@ export class PayService extends BaseService {
|
|
|
try {
|
|
|
this.logger.info('代收查询', JSON.stringify(payload));
|
|
|
await this.validQueryParam(payload);
|
|
|
- const merchant = await this.validMerchant(payload);
|
|
|
+ const merchant = await this.validMerchant(payload.mchId);
|
|
|
await this.validSign(payload, merchant);
|
|
|
const order = await this.orderService.findByOutOrderNo(
|
|
|
payload.outOrderNo
|
|
@@ -76,19 +84,29 @@ export class PayService extends BaseService {
|
|
|
try {
|
|
|
this.logger.info('代收下单', JSON.stringify(payload));
|
|
|
await this.validParam(payload);
|
|
|
- const channel = await this.validPayWay(payload);
|
|
|
- const merchant = await this.validMerchant(payload);
|
|
|
+ const merchant = await this.validMerchant(payload.mchId);
|
|
|
+ const rate = await this.validRate(payload.mchId, payload.payType, payload.currency);
|
|
|
+ const channel = await this.validPayWay(payload.mchId, payload.payType, payload.amount);
|
|
|
await this.validSign(payload, merchant);
|
|
|
await this.validOutOrderNo(payload);
|
|
|
+ let channelCharge = (+payload.amount * +channel.rate) / 100 + +channel.basicFee;
|
|
|
+ if(channelCharge < channel.feeMin) {
|
|
|
+ channelCharge = channel.feeMin;
|
|
|
+ }
|
|
|
+ let charge = (+payload.amount * +rate.rate) / 100 + +rate.basicFee;
|
|
|
+ if(charge < rate.feeMin) {
|
|
|
+ charge = rate.feeMin;
|
|
|
+ }
|
|
|
order = {
|
|
|
orderNo: this.utils.createOrderNo('DS'),
|
|
|
outOrderNo: payload.outOrderNo,
|
|
|
traceNo: '',
|
|
|
+ payType: payload.payType,
|
|
|
code: channel.code,
|
|
|
mchId: merchant.mchId,
|
|
|
amount: payload.amount,
|
|
|
- channelCharge: (+payload.amount * +channel.rate) / 100,
|
|
|
- charge: (+payload.amount * +merchant.rate) / 100,
|
|
|
+ channelCharge,
|
|
|
+ charge,
|
|
|
date: null,
|
|
|
notifyUrl: payload.notifyUrl,
|
|
|
returnUrl: payload.returnUrl,
|
|
@@ -104,7 +122,7 @@ export class PayService extends BaseService {
|
|
|
order,
|
|
|
channel
|
|
|
);
|
|
|
- if(isDone) {
|
|
|
+ if (isDone) {
|
|
|
order.payUrl = payUrl;
|
|
|
order.traceNo = traceNo;
|
|
|
await this.orderService.create(order);
|
|
@@ -154,28 +172,71 @@ export class PayService extends BaseService {
|
|
|
// throw new Error('支付金额【amount】必须为整数');
|
|
|
// }
|
|
|
if (_.isEmpty(payload.payType)) {
|
|
|
- throw new Error('通道码【payType】不能为空');
|
|
|
+ throw new Error('支付方式【payType】不能为空');
|
|
|
}
|
|
|
if (_.isEmpty(payload.sign)) {
|
|
|
throw new Error('签名【sign】不能为空');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async validPayWay(payload) {
|
|
|
- const channel = await this.channelService.getOne(payload.payType);
|
|
|
- if (!channel) {
|
|
|
- throw new Error('当前通道码不可用');
|
|
|
+ async validRate(mchId, payType, currency) {
|
|
|
+ const rate = await this.rateService.getOne({
|
|
|
+ mchId,
|
|
|
+ payType,
|
|
|
+ currency,
|
|
|
+ type: 1
|
|
|
+ });
|
|
|
+ if (!rate) {
|
|
|
+ throw new Error('商户该支付方式未开通');
|
|
|
+ }
|
|
|
+ return rate;
|
|
|
+ }
|
|
|
+
|
|
|
+ async validPayWay(mchId, payType, amount) {
|
|
|
+ const mchChannels = await this.mchChannelService.getByMchIdAndType(mchId, payType);
|
|
|
+ if (mchChannels.length === 0) {
|
|
|
+ throw new Error('暂无可用支付路由');
|
|
|
}
|
|
|
- let max = channel.max || 0;
|
|
|
- let min = channel.min || 0;
|
|
|
- if (+payload.amount > +max || +payload.amount < +min) {
|
|
|
- throw new Error(`单笔最大限额【${+min}, ${+max}】`);
|
|
|
+ let channels = await this.channelService.getByCodes(mchChannels.map(item => item.code));
|
|
|
+ if (channels.length === 0) {
|
|
|
+ throw new Error('暂无可用支付路由');
|
|
|
+ }
|
|
|
+ // 筛选出金额范围符合的,并且计算出所有可用通道的最大金额和最小金额
|
|
|
+ let allMax = 0;
|
|
|
+ let allMin = 0;
|
|
|
+ channels = channels.filter(item => {
|
|
|
+ let max = item.max || 0;
|
|
|
+ let min = item.min || 0;
|
|
|
+ if (item.max > allMax) {
|
|
|
+ allMax = item.max;
|
|
|
+ }
|
|
|
+ if (item.min < allMin) {
|
|
|
+ allMin = 0;
|
|
|
+ }
|
|
|
+ const mchChannel = mchChannels.find(i => i.code === item.code);
|
|
|
+ item['weight'] = mchChannel.weight || 0;
|
|
|
+ return +amount >= +min && +amount <= max
|
|
|
+ })
|
|
|
+ if (channels.length === 0) {
|
|
|
+ throw new Error(`单笔最大限额【${+allMin}, ${+allMax}】`);
|
|
|
+ }
|
|
|
+ let weight = 0;
|
|
|
+ channels.forEach(item => {
|
|
|
+ weight += item['weight']
|
|
|
+ });
|
|
|
+ const ramdon = Math.floor(Math.random() * weight);
|
|
|
+ let totalWeight = 0;
|
|
|
+ for (let i = 0; i < channels.length; i++) {
|
|
|
+ const item = channels[i];
|
|
|
+ totalWeight += item['weight']
|
|
|
+ if (ramdon < totalWeight) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
}
|
|
|
- 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) {
|
|
|
throw new Error('商户不存在');
|
|
|
}
|