|
@@ -0,0 +1,394 @@
|
|
|
+import { Inject, Logger, Provide } from '@midwayjs/decorator';
|
|
|
+import { BaseService } from '@cool-midway/core';
|
|
|
+import * as _ from 'lodash';
|
|
|
+import * as moment from 'moment';
|
|
|
+import { Utils } from '../../../../comm/utils';
|
|
|
+import { HttpService } from '@midwayjs/axios';
|
|
|
+import { ILogger } from '@midwayjs/logger';
|
|
|
+
|
|
|
+const HOST = "https://payment.alchemytech.cc";
|
|
|
+const MCH_ID = 'AC202299319';
|
|
|
+const KEY = 'r1t8hnmj86i1zt7ywbin';
|
|
|
+const PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIiPOgAEa8cMN08+sGKsu9lcm61aZmIXh/Yqfc4S2P234uN8MjAhzINVmYDDXSc0oSQkmqwOoUaf3uRDUUTkAVYzNcYg/IPuStsgEtbinsZSFJ5/+zhW82qd/AOsMxgcN3Jm5sNZa87Yu+E+tEUOXMRmxu4PTBA2lyBjN3D8sGsDAgMBAAECgYBEDXg+dPWO4rAXfiqhaepNJmDwUUoPXSGk08UQ8oSPX9miOwy5vsYvtvNB31nnRUt+ev1XfAzz6IzHnSJ9XdCp/Xv+BjGmD5JK3Li0wcpBuVCcd4GbwMto1+TTw3AXa9QXAnwWTUwHWPBCQAhhPUGxp5BdJ6ut2qX9h3GLqcZISQJBAMAW5M3w8YCzhRBbAJkSIg8fhQA+A9mdRw6gS0rMZiKHEudh7mvoFXWEQsy04OSrKvfQ1HBR+zkuG1+mcoTDTtkCQQC1/pnyKxumd9d/aF/5RJAiwSHinN2O16cROXYGoAndWVQJp7rvtu2AdTFC3S2eL3pQEN/g9ljGNorfKfzw/dc7AkAeGfhVBXLy9i7d90TKt/q6X/gZp5421dyywA/Mcud2dbBSrhgMtNvYBJfrOFUdwG0FVKZVy6MQvNbxUEYV4/1RAkEAjizuGw+uNCgfQ7tIbrUvXNdAH4aoVzUFoSEgoSBZVIC6dCiCk0520Am9iBy9zHwOs4nbPCV3SLHHX3uvzJyhjQJAUurBjyMEzomk8yjkcqIu4LCHKWEjsInJPeWTJ/uNEXwEqaS6wXpcJdSNWehFFr1kgSxsldod2r43XuvwiyC8Dw==\n" + "-----END RSA PRIVATE KEY-----"
|
|
|
+const PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUgjXpKpG6+o/uoJqRdpVaQae5IKgtXtEqoZXNQnAFk+wk/toyDxPmQNn5+Uj2pdwFBOzKTuvfKDVZHnLTgRz6mHfKrNboBwqHCj4WCzDj02P30QArr2KbuY+kfRdD8ckuFUlmMDXCFcqa+XZaCiZAcepDQ/RH7l6mL7A4/FdgMwIDAQAB';
|
|
|
+const PAY_URL = '/nh-gateway/card/payment';
|
|
|
+const QUERY_URL = '/nh-gateway/card/query'
|
|
|
+// const BALANCE_URL = '/api/v3/bra/query/balance'
|
|
|
+// const BANK_URL = '/api/v3/bra/query/bank';
|
|
|
+// const WITHDRAW_URL = '/api/v3/bra/createTransferOrder';
|
|
|
+// const QUERY_WITHDRAW_URL = '/api/v3/bra/query/transferOrder'
|
|
|
+
|
|
|
+const NOTIFY_HOST = `http://157.175.73.225/api/admin/dj/open/ach/notifyOrder`;
|
|
|
+// const WITHDRAW_NOTIFY_HOST = `http://157.175.73.225/api/admin/dj/open/ach/notifyWithdraw`;
|
|
|
+
|
|
|
+const PAY_TYPE = {
|
|
|
+ "INR": 'INDIA_PAY',
|
|
|
+ "BRL": "BRAZIL_PAY",
|
|
|
+ "MXN": "MEXICO_PAY",
|
|
|
+ "JPY": "JAPAN_PAY",
|
|
|
+ "VND": "VIETNAM_PAY",
|
|
|
+ "PHP": "PHILIPPINES_PAY",
|
|
|
+ "IDR": "INDONESIA_PAY"
|
|
|
+}
|
|
|
+
|
|
|
+const PAY_MODEL = {
|
|
|
+ "BANK": 'BankTransfer',
|
|
|
+ "PIX": "Pix",
|
|
|
+ "UPI": "UPI",
|
|
|
+ "MOMO": "MOMO_PAY",
|
|
|
+ "COINS": "Coins",
|
|
|
+ "BPI": "BPI",
|
|
|
+ "PAYMAYA": "Paymaya",
|
|
|
+ "QRIS": "QRIS",
|
|
|
+ "VA": "DIRECT"
|
|
|
+
|
|
|
+}
|
|
|
+@Provide()
|
|
|
+export class AchService extends BaseService {
|
|
|
+ @Inject()
|
|
|
+ utils: Utils;
|
|
|
+ @Inject()
|
|
|
+ httpService: HttpService;
|
|
|
+ @Logger()
|
|
|
+ logger: ILogger;
|
|
|
+
|
|
|
+ validOrderParam(payload) {
|
|
|
+ switch (payload.payType) {
|
|
|
+ case "PIX": {
|
|
|
+ if (_.isEmpty(payload.idendifyType)) {
|
|
|
+ throw new Error('身份类型【idendifyType】不能为空');
|
|
|
+ }
|
|
|
+ if (_.isEmpty(payload.idendifyNumber)) {
|
|
|
+ throw new Error('用户的真实CPF号码【idendifyNumber】不能为空');
|
|
|
+ }
|
|
|
+ if (_.isEmpty(payload.firstName)) {
|
|
|
+ throw new Error('【firstName】不能为空');
|
|
|
+ }
|
|
|
+ if (_.isEmpty(payload.lastName)) {
|
|
|
+ throw new Error('【lastName】不能为空');
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "UPI": {
|
|
|
+ if (_.isEmpty(payload.phone)) {
|
|
|
+ throw new Error('用户手机号【phone】不能为空');
|
|
|
+ }
|
|
|
+ if (_.isEmpty(payload.email)) {
|
|
|
+ throw new Error('用户邮箱【email】不能为空');
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case "QRIS": {
|
|
|
+ if (_.isEmpty(payload.firstName)) {
|
|
|
+ throw new Error('【firstName】不能为空');
|
|
|
+ }
|
|
|
+ if (_.isEmpty(payload.lastName)) {
|
|
|
+ throw new Error('【lastName】不能为空');
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async order(payload, extra) {
|
|
|
+ const param: any = {
|
|
|
+ merchantNo: MCH_ID,
|
|
|
+ timeStamp: +moment(),
|
|
|
+ merchantOrderNo: payload.orderNo,
|
|
|
+ payType: PAY_TYPE[payload.currency],
|
|
|
+ payModel: PAY_MODEL[payload.payType],
|
|
|
+ orderAmount: (+payload.amount * 100).toFixed(0),
|
|
|
+ productDetail: "good",
|
|
|
+ callbackUrl: NOTIFY_HOST,
|
|
|
+ };
|
|
|
+ if (extra.firstName) {
|
|
|
+ param.firstName = extra.firstName;
|
|
|
+ }
|
|
|
+ if (extra.lastName) {
|
|
|
+ param.lastName = extra.lastName;
|
|
|
+ }
|
|
|
+ if (extra.phone) {
|
|
|
+ param.phone = extra.phone;
|
|
|
+ }
|
|
|
+ if (extra.email) {
|
|
|
+ param.email = extra.email;
|
|
|
+ }
|
|
|
+ if (extra.payBankCode && payload.currency === 'VND' && payload.payType === 'BANK') {
|
|
|
+ param.payBankCode = extra.payBankCode;
|
|
|
+ }
|
|
|
+ const signStr = this.utils.signSort(param);
|
|
|
+ param.sign = this.utils.signByMD5WithRSA(signStr, PRIVATE_KEY);
|
|
|
+ if (extra.idendifyType) {
|
|
|
+ param.idendifyType = extra.idendifyType;
|
|
|
+ }
|
|
|
+ if (extra.idendifyNumber) {
|
|
|
+ param.idendifyNumber = extra.idendifyNumber;
|
|
|
+ }
|
|
|
+ if (extra.userIp) {
|
|
|
+ param.userIp = extra.userIp;
|
|
|
+ }
|
|
|
+ param.language = "en";
|
|
|
+ param.noticeUrl = payload.returnUrl;
|
|
|
+ const res = await this.httpService.postForm(HOST + PAY_URL, param);
|
|
|
+ this.logger.info('下单接口返回', param, JSON.stringify(res.data));
|
|
|
+ const { meta = {}, data = {} } = res.data;
|
|
|
+ const { success, code, message } = meta
|
|
|
+ if (success && code === '0000') {
|
|
|
+ return {
|
|
|
+ payUrl: data.webUrl,
|
|
|
+ traceNo: data?.orderNo
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ throw new Error(message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async query(payload) {
|
|
|
+ const param: any = {
|
|
|
+ merchantNo: 'AC202299319',
|
|
|
+ timeStamp: 1735185420342,
|
|
|
+ merchantOrderNo: 'DS202412251203055901',
|
|
|
+ };
|
|
|
+ const signStr = this.utils.signSort(param);
|
|
|
+ console.log(param, signStr)
|
|
|
+ param.sign = this.utils.signByMD5WithRSA(signStr, PRIVATE_KEY);
|
|
|
+ const res = await this.httpService.postForm(HOST + QUERY_URL, param);
|
|
|
+ this.logger.info('查询接口返回', param, JSON.stringify(res.data));
|
|
|
+ const { meta = {}, data = {} } = res.data;
|
|
|
+ const { success, code, message } = meta
|
|
|
+ if (success && code === '0000') {
|
|
|
+ if (data.orderStatus === 'SUCCESS') {
|
|
|
+ return {
|
|
|
+ status: 1,
|
|
|
+ traceNo: data.orderNo,
|
|
|
+ date: new Date()
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ return {
|
|
|
+ status: 0
|
|
|
+ };
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new Error(message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // async handleOrderNotify(payload, headers) {
|
|
|
+ // const sign = headers['sign'];
|
|
|
+ // const timestamp = headers['timestamp'];
|
|
|
+ // const nonce = headers['nonce'];
|
|
|
+ // const signStr = this.utils.signSort({
|
|
|
+ // timestamp,
|
|
|
+ // nonce,
|
|
|
+ // access_key: ACCESS_KEY,
|
|
|
+ // ...payload
|
|
|
+ // });
|
|
|
+ // const validSign = this.utils.signByHmacSha1(signStr, SECRET_KEY);
|
|
|
+ // if (sign !== validSign) {
|
|
|
+ // throw new Error('sign error');
|
|
|
+ // }
|
|
|
+ // if (+payload.orderStatusCode !== 2) {
|
|
|
+ // throw new Error('order no success');
|
|
|
+ // }
|
|
|
+ // return {
|
|
|
+ // date: new Date(),
|
|
|
+ // orderNo: payload.externalOrderId,
|
|
|
+ // traceNo: payload.orderId,
|
|
|
+ // status: 1,
|
|
|
+ // };
|
|
|
+ // }
|
|
|
+
|
|
|
+ // async queryBalance() {
|
|
|
+ // const param = {};
|
|
|
+ // 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.get(HOST + BALANCE_URL, {
|
|
|
+ // 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[0]) {
|
|
|
+ // return {
|
|
|
+ // balance: data[0].accountBalance,
|
|
|
+ // freeze: data[0].accountFreezeAmount
|
|
|
+ // }
|
|
|
+ // } else {
|
|
|
+ // 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,
|
|
|
+ // accountId: payload.accountNo,
|
|
|
+ // notifyUrl: WITHDRAW_NOTIFY_HOST
|
|
|
+ // };
|
|
|
+ // 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, desc, data } = res.data;
|
|
|
+ // if (success && data.orderId) {
|
|
|
+ // return {
|
|
|
+ // status: 2,
|
|
|
+ // traceNo: data.orderId
|
|
|
+ // };
|
|
|
+ // } else {
|
|
|
+ // return {
|
|
|
+ // status: 3,
|
|
|
+ // message: desc || msg
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ // async handleWithdrawNotify(payload, headers) {
|
|
|
+ // const sign = headers['sign'];
|
|
|
+ // const timestamp = headers['timestamp'];
|
|
|
+ // const nonce = headers['nonce'];
|
|
|
+ // const signStr = this.utils.signSort({
|
|
|
+ // timestamp,
|
|
|
+ // nonce,
|
|
|
+ // access_key: ACCESS_KEY,
|
|
|
+ // ...payload
|
|
|
+ // });
|
|
|
+ // const validSign = this.utils.signByHmacSha1(signStr, SECRET_KEY);
|
|
|
+ // if (sign !== validSign) {
|
|
|
+ // throw new Error('sign error');
|
|
|
+ // }
|
|
|
+ // if (+payload.orderStatusCode !== 8 && +payload.orderStatusCode !== 4 && +payload.orderStatusCode !== 16) {
|
|
|
+ // throw new Error('order no result');
|
|
|
+ // }
|
|
|
+ // let status = 2;
|
|
|
+ // if (+payload.orderStatusCode === 8) {
|
|
|
+ // status = 8;
|
|
|
+ // } else if (+payload.orderStatusCode === 4 || +payload.orderStatusCode === 16) {
|
|
|
+ // status = 3;
|
|
|
+ // }
|
|
|
+ // return {
|
|
|
+ // date: new Date(),
|
|
|
+ // status: status,
|
|
|
+ // message: payload.errorMsg,
|
|
|
+ // orderNo: payload.externalOrderId
|
|
|
+ // };
|
|
|
+ // }
|
|
|
+
|
|
|
+ // async queryWithdraw(payload) {
|
|
|
+ // const param = {
|
|
|
+ // externalOrderId: payload.orderNo
|
|
|
+ // };
|
|
|
+ // 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 + QUERY_WITHDRAW_URL, param, {
|
|
|
+ // headers: {
|
|
|
+ // 'access_key': ACCESS_KEY,
|
|
|
+ // 'timestamp': timestamp,
|
|
|
+ // 'nonce': nonce,
|
|
|
+ // 'sign': sign
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // this.logger.info('查询代付接口返回', JSON.stringify(res.data));
|
|
|
+ // const { success, msg, data = [] } = res.data;
|
|
|
+ // const orderData = data.find(item => item.externalOrderId === payload.orderNo)
|
|
|
+ // if (success && orderData) {
|
|
|
+ // if (+orderData.orderStatus === 4 || +orderData.orderStatus === 16) {
|
|
|
+ // return {
|
|
|
+ // date: new Date(),
|
|
|
+ // status: 3,
|
|
|
+ // message: orderData.errorMsg,
|
|
|
+ // orderNo: orderData.externalOrderId
|
|
|
+ // };
|
|
|
+ // } else if (+orderData.orderStatus === 8) {
|
|
|
+ // return {
|
|
|
+ // date: new Date(),
|
|
|
+ // status: 8,
|
|
|
+ // message: orderData.errorMsg,
|
|
|
+ // orderNo: orderData.externalOrderId
|
|
|
+ // };
|
|
|
+ // }
|
|
|
+ // return {
|
|
|
+ // status: 1,
|
|
|
+ // traceNo: orderData.orderId,
|
|
|
+ // date: new Date()
|
|
|
+ // };
|
|
|
+ // } else {
|
|
|
+ // return {
|
|
|
+ // status: 3,
|
|
|
+ // message: '渠道订单不存在'
|
|
|
+ // };
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+}
|