爱贝支付 - 服务端 - nodejs实现

爱贝支付nodejs实现,完整的代码,复制即可执行

总结几个小坑:

1. 使用私钥进行签名的问题,爱贝支付的签名规则是采用RSA MD5数字签名算法,私钥签名、公钥验签

    所以本案例中用的是 nodejs 的 crypto 模块

// 签名

crypto.createSign('md5WithRSAEncryption');

//验签

crypto.createVerify('md5WithRSAEncryption');

2. 请求下单爱贝提示请求参数错误的问题,最终使用了 nodejs 的 request 模块,案例中有代码

3. 私钥和公钥的问题,建议放在同一个目录下
 

代码如下:

/**

 * created by zhoubotong on 2019/06/03

 */

const path = require('path');

const fs = require('fs');

const crypto = require('crypto');

const ORDER_SUCCESS = 0;

const ORDER_FAILED = 1;

class IPay {

    constructor(config) {

        this.init();

    }

    init() {

        this.payConfig = {

            APP_NAME: "",

            APP_ID: "",

            APP_GATEWAY_URL: "",//用于接收异步通知

            APP_PRIVATE_KEY_PATH: path.join(__dirname, './keys/rsa_private_key.pem'),//应用私钥

            APP_PUBLIC_KEY_PATH: path.join(__dirname, './keys/rsa_public_key.pem'),//应用公钥

            ORDER_URL: "https://cp.iapppay.com/payapi/order",

            H5_PAY_GATEWAY: "https://web.iapppay.com/h5/gateway?",

            H5_PAY_SUCCESS_URL: "",

            H5_PAY_CANCEL_URL: "",

        }

    }


    /**

     * h5支付

     * @param userId

     * @param subject

     * @param outTradeNo

     * @param totalAmount

     * @returns {Promise.<*>}

     */

    async h5(userId, subject, outTradeNo, totalAmount) {

        // 拼装参数

        let params = this._buildParams(userId, subject, outTradeNo, totalAmount);

        // 发起下单请求

        let res = await this._httpsFormPost(this.payConfig.ORDER_URL, params);

        // 解析结果

        let orderData = this._parseOrderResult(res);

        if (orderData.errcode === ORDER_SUCCESS) {

            // h5 支付

            let params = {

                tid: orderData.transid,

                app: this.payConfig.APP_ID,

                url_r: this.payConfig.H5_PAY_SUCCESS_URL,

                url_h: this.payConfig.H5_PAY_CANCEL_URL,

            };

            params = JSON.stringify(params);

            let sign = this._buildSign(params);

            let data = "data=" + encodeURIComponent(params) + "&sign=" + encodeURIComponent(sign) + "&signtype=RSA";

            // url + data

            return this.payConfig.H5_PAY_GATEWAY + data;

        } else {

            console.error(orderData.errmsg);

            return null;

        }

    }

    /**

     * 解析下单结果

     *  原始数据:transdata=%7B%22transid%22%3A%2232641906040908136002%22%7D&sign=TEpZZAbyWA2H0MQCC1Cdny6BttEqeN4vnmO1S%2FuhwUk87de911VeoGAHHADp1S7bDl02d9asPZor03TQMiuCU2VGbyc8Hk5xzgZZrybFfRO02QjXT6Pe6kvLw6FSBhnKewLWXhOvZOCRrwUtcBvh%2FDGlBxPxdrfhw7zA%2FfwoO2s%3D&signtype=RSA

     *  下单成功:transdata={"transid":"32641906040908136002"}&sign=TEpZZAbyWA2H0MQCC1Cdny6BttEqeN4vnmO1S/uhwUk87de911VeoGAHHADp1S7bDl02d9asPZor03TQMiuCU2VGbyc8Hk5xzgZZrybFfRO02QjXT6Pe6kvLw6FSBhnKewLWXhOvZOCRrwUtcBvh/DGlBxPxdrfhw7zA/fwoO2s=&signtype=RSA

     *  下单失败:transdata={"code":1002,"errmsg":"请求参数错误"}

     * @param data

     * @private

     */

    _parseOrderResult(data) {

        let ret = {};

        // 1 进行 url 解码

        data = decodeURIComponent(data);

        // 2 根据 & 切割

        let dataArr = data.split("&");

        // 3 获取数据,

        // 下单成功: transdata、sign、signtype 三个数据

        // 下单失败: transdata 一个数据

        if (dataArr.length === 3) {

            // 获取transdata

            let transdata = dataArr[0].replace("transdata=", "");

            // 获取sign

            let sign = dataArr[1].replace("sign=", "");

            // signtype

            let signtype = dataArr[2].replace("signtype=", "");

            // 4 验签

            if (this.verifySign(transdata, sign)) {

                let res = transdata.match("\\d+");

                ret.errcode = ORDER_SUCCESS;

                ret.transid = res[0];

                ret.transdata = transdata;

                ret.sign = sign;

                ret.signtype = signtype;

            } else {

                console.error(`验签不通过`);

                ret.errcode = ORDER_FAILED;

                ret.errmsg = dataArr[0];

            }

        } else {

            ret.errcode = ORDER_FAILED;

            ret.errmsg = dataArr[0];

        }

        return ret;

    }


    /**

     * 构建app支付需要的参数

     * @param userId        用户id

     * @param subject       商品名称

     * @param outTradeNo    自己的订单号

     * @param totalAmount   金额

     * @returns {string}

     * @private

     */

    _buildParams(userId, subject, outTradeNo, totalAmount) {

        let params = {

            waresid: 1,

            appuserid: "" + userId,

            price: parseFloat(totalAmount),

            appid: "" + this.payConfig.APP_ID,

            waresname: subject,

            notifyurl: this.payConfig.APP_GATEWAY_URL,

            currency: "RMB",

            cporderid: outTradeNo,

        };

        params = JSON.stringify(params);

        let sign = this._buildSign(params);

        return {

            transdata: decodeURIComponent(params),

            sign: decodeURIComponent(sign),

            signtype: "RSA",

        };

    }

    /**

     * 根据参数构建签名

     * @param params

     * @private

     */

    _buildSign(params) {

        let privateKeyStr = fs.readFileSync(this.payConfig.APP_PRIVATE_KEY_PATH, 'utf8');

        // 私钥 + 参数 ras md5

        let sign = crypto.createSign('md5WithRSAEncryption');

        sign.update(Buffer.from(params));

        return sign.sign(privateKeyStr, 'base64');

    }


    /**

     * 验证签名

     * @param params

     * @param sign

     * @returns {*}

     */

    verifySign(params, sign) {

        try {

            let publicKey = fs.readFileSync(this.payConfig.APP_PUBLIC_KEY_PATH, 'utf8');

            let verify = crypto.createVerify('md5WithRSAEncryption');

            verify.update(Buffer.from(params));

            return verify.verify(publicKey, sign, 'base64')

        } catch (e) {

            console.error(e);

            return false;

        }

    }


    /**

     * 发起下单请求

     * @param host

     * @param data

     * @returns {Promise}

     * @private

     */

    _httpsFormPost(host, data) {

        let request = require('request');

        let options = {

            url: host,

            form: data,

            encoding: 'utf8'

        };

        return new Promise((resolve, reject) => {

            request.post(options, (err, res, body) => {

                if (err) {

                    reject('error posting json: ', err);

                }

                try {

                    let json = JSON.parse(body);

                    json.cookie = res.headers['set-cookie'];

                    resolve(json);

                } catch (e) {

                    resolve(body);

                }

            });

        });

    }

}

module.exports = IPay;


async function main() {

    try {

        let iPay = new IPay();

        let userId = "test1";

        let subject = "test";

        let outTradeNo = "15527268917182646317";

        let totalAmount = 1.1;

        let data = await iPay.h5(userId, subject, outTradeNo, totalAmount);

        console.log(data);

    } catch (e) {

        console.log(e)

    }

}

// main();

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值