import axios from 'axios'; import { ApiOptions, AppOptions, PayDomain, ResStatus } from './interface'; import md5 from 'md5'; /** * 牛盾聚合支付 * @link https://ndpay.qyyapp.com/ */ export class Base { protected site = <{ domain: string; channel: number }>{ domain: '', // 系统域名 channel: 0, }; protected xm = { baseUrl: PayDomain, appOptions: { mchNo: '', appId: '', secret: '' } as AppOptions, apiOptions: { version: '1.0', reqTime: 0, signType: 'MD5', sign: '' } as ApiOptions, }; constructor(params:any) { const { appId, mchNo, secret }: AppOptions = params.appConf; this.site.domain = params.domain; axios.defaults.baseURL = this.xm.baseUrl; axios.defaults.headers['Content-Type'] = 'application/json'; this.xm.appOptions.appId = appId; this.xm.appOptions.mchNo = mchNo; this.xm.appOptions.secret = secret; return this; } protected async apiPost(url: string, data: any) { Object.assign(data, this.xm.appOptions, this.xm.apiOptions); if (data.notifyUrl) { data.notifyUrl = this.site.domain + data.notifyUrl; } data.reqTime = new Date().getTime(); // 过滤body为空的参数 Object.keys(data).forEach(key => { if (data[key] === null || data[key] === '') { delete data[key]; } }); data.sign = this.sign(data); const res = (await axios.post(url, data)).data; if (res.code != ResStatus.success) { throw new Error(res.msg); } if (res.data?.errMsg) { throw new Error(res.data.errMsg); } return res; } // 签名 protected sign(data: any) { if (!this.xm.appOptions.secret) throw new Error('聚合支付密钥不能为空!'); delete data?.sign; const bodyStr = Object.keys(data) .sort() .map(key => `${key}=${data[key]}`) .join('&') + '&key=' + this.xm.appOptions.secret; return md5(bodyStr).toUpperCase(); } // 回调通知 protected notify({ sign, ...data }: any, success: () => void, fail: (msg: string) => void) { // 验证签名 const check = sign === this.sign(data); if (sign && check) { // 在回调函数 success 中写 支付成功的逻辑 success(); } else { console.log('-----------appConf.data', data); console.log('-----------调签名验证', check); fail('签名验证未通过!'); } } }