wechat-pay-sdk
Version:
sdk for wechat developer
139 lines (126 loc) • 4 kB
text/typescript
import * as js2xml from 'js2xmlparser';
import * as xml2json from 'xml2json';
import * as md5 from 'md5';
import * as request from 'request';
import * as url from 'url';
import { readomString } from './util';
import { WechatPayOptions, WechatSign, WechatOpenidRes, PaymentArgs } from './interface';
class WechatPay {
// api hostname
baseUrl: string;
appid: string;
secret: string;
// tslint:disable-next-line:variable-name
mch_id: string | number;
// 商户密钥
key: string;
baseApiObj = {
protocol: 'https',
hostname: 'api.weixin.qq.com',
};
constructor(options: WechatPayOptions) {
if (!options) {
throw new Error('options is required');
}
this.baseUrl = options.baseUrl || 'https://api.weixin.qq.com';
this.appid = options.appid;
this.secret = options.secret;
this.mch_id = options.mch_id;
this.key = options.key;
}
/**
* 获取用户openid
* @param code url上的code
* @param callback 回调
*/
getUserOpenId(code: string, callback?: (result: WechatOpenidRes) => void): Promise<WechatOpenidRes> {
const { appid, secret } = this;
return new Promise((resolve, reject) => {
const openidUrl = url.format(Object.assign({}, {
pathname: '/sns/jscode2session',
query: {
appid,
secret,
js_code: code,
grant_type: 'authorization_code',
},
}, this.baseApiObj));
request.get(openidUrl, { json: true }, (err, res, data) => {
if (err || res.statusCode !== 200) {
throw new Error('get openid failed');
} else {
resolve(data as WechatOpenidRes);
if (callback) callback(data);
}
});
});
}
/**
* generator sign
* @param obj
*/
private _generatorSign(obj: any) {
let sortArr = Object.keys(obj).sort();
let sortStr = '';
sortArr = sortArr.map((key) => {
return `${key}=${obj[key]}`;
});
sortArr.push(`key=${this.key}`);
sortStr = sortArr.join('&');
return md5(sortStr).toUpperCase();
}
/**
* 发起微信支付
* @param options 发起支付的参数
* @param callback 回调函数
*/
payment(options: PaymentArgs, callback?: (result: any) => void) {
if (!options) throw new Error('payment method need args');
return new Promise((resolve, reject) => {
const basicReq = {
appid: this.appid,
mch_id: this.mch_id,
nonce_str: readomString(32),
trade_type: 'JSAPI',
};
const customerReq = Object.assign({}, basicReq, options);
const sign = this._generatorSign(customerReq);
customerReq.sign = sign;
const modal2xml = js2xml.parse('xml', customerReq);
request({
url: url.format(Object.assign({}, this.baseApiObj, { hostname: 'api.mch.weixin.qq.com', pathname: '/pay/unifiedorder' })),
method: 'POST',
body: modal2xml,
}, (err, res, data) => {
let originalData: any = { xml: {} }, wechatPayData: any = {};
if (err || res.statusCode !== 200) {
reject(new Error('connect failed'));
return;
}
try {
originalData = JSON.parse(xml2json.toJson(data));
} catch (error) {
reject(new Error('xml parse failed'));
return;
}
if (originalData.xml.return_code === 'SUCCESS') {
const prepay_id = originalData.xml.prepay_id;
wechatPayData = {
appId: this.appid,
timeStamp: new Date().getTime(),
nonceStr: readomString(32),
package: `prepay_id=${ prepay_id }`,
signType: 'MD5',
};
wechatPayData.paySign = this._generatorSign(wechatPayData);
}
resolve({
original_data: originalData.xml,
wechatpay_data: wechatPayData,
});
if (callback) callback({ original_data: originalData, wechatpay_data: wechatPayData });
});
});
}
}
export = WechatPay;