rapid-unipay
Version:
UnionPay module
110 lines (101 loc) • 3.79 kB
JavaScript
/**
* Copyright (c) 2017 Lucky Byte, Inc.
*/
const debug = require('debug')('unipay');
const crypto = require('crypto');
const moment = require('moment');
const winston = require('winston');
const utils = require('../lib/utils');
const post = require('../lib/post');
/**
* 银联代付,后台交易,银联响应结果,外加后台通知
*
* 参数:
* dest_url: 银联代付请求地址
* params: 一个 JSON 对象,包含下列的 key:
* * merno: 商户代码
* * order_id: 商户订单号
* * cert_sn: 签名证书 Serial Number
* * sign_pem: 签名证书,PEM
* * acct_no: 卡号/账号
* * acct_name: 账户姓名(和 acct_cert_no 必填其一)
* * acct_cert_no: 账户身份证号码(和 acct_name 必填其一)
* * verify_cer: 验证响应报文签名证书
* * back_url: 后台通知地址
* * amount: 交易金额,单位为分
* * encrypt_cer: 加密证书,可选
* * encrypt_cer_sn: 加密证书序号,可选
*
* 返回:
* JSON,包含 2 个字段:
* reqt: 请求报文, JSON
* resp: 响应报文, JSON, 通过报文中 respCode 确定交易是否成功
*/
const exchange = async (dest_url, params) => {
if (!dest_url || !params) {
throw new Error('银联代付请求参数无效,用法 exchange(dest_url, {...})');
}
const required_keys = [
'merno', 'cert_sn', 'sign_pem', 'verify_cer', 'order_id',
'acct_no', 'back_url', 'amount'
]
for (let i = 0; i < required_keys.length; i++) {
const key = required_keys[i];
if (!params[key]) {
throw new Error(`银联代付请求参数缺少[${key}]`);
}
}
if (!params.acct_name && !params.acct_cert_no) {
throw new Error('代付参数 [acct_name] 和 [acct_cert_no] 必填其一');
}
const amount = parseInt(params.amount);
if (!amount) {
throw new Error(`代付请求金额[${params.amount}]无效`);
}
let reqt_json = {
version: '5.0.0',
encoding: 'UTF-8',
signMethod: '01',
txnType: '12',
txnSubType: '00',
bizType: '000401',
channelType: '07',
accessType: '0',
txnTime: moment().format('YYYYMMDDHHmmss'),
certId: params.cert_sn,
merId: params.merno,
orderId: params.order_id,
backUrl: params.back_url,
currencyCode: '156',
txnAmt: amount,
}
if (params.version) {
if (params.version != '5.0.0' && params.version != '5.1.0') {
throw new Error('参数 version 无效,必须是 5.0.0 或 5.1.0');
}
reqt_json.version = params.version;
}
if (!params.encrypt_cer) {
reqt_json.accNo = params.acct_no;
} else {
if (!params.encrypt_cer_sn) {
throw new Error('提供了加密证书,但未提供加密证书ID');
}
reqt_json.encryptCertId = params.encrypt_cer_sn;
reqt_json.accNo = utils.pubkey_encrypt(params.encrypt_cer, params.acct_no);
}
reqt_json.customerInfo = utils.build_cust_info(params);
reqt_json.signature = utils.gen_reqt_sign(reqt_json, params.sign_pem);
winston.info('银联代付请求报文:', JSON.stringify(reqt_json, null, 2));
const resp_data = await post(dest_url, reqt_json, {retry: 1});
const resp_json = utils.parse_resp_data(resp_data);
winston.info('银联代付响应报文:', JSON.stringify(resp_json, null, 2));
if (!utils.verify_resp_sign(resp_json, params.verify_cer)) {
throw new Error('验证代付响应报文签名失败');
}
debug('验证代付响应报文签名通过');
return { reqt: reqt_json, resp: resp_json }
}
module.exports = {
exchange: exchange,
}