回到顶部

React网页应用调起支付宝沙箱测试实现

时间:5个月前   作者:请喊我大龙哥   浏览:48   [站内原创,转载请注明出处]

标签: Node.js   支付宝   React  

React+Redux网页应用调起支付宝沙箱测试实现

1、新建React+Redux项目,创建空页面,增加一个支付链接,编写Redux方法调用Api生成支付链接   

  render() {
    let searchParam = {
      loginState: this.props.loginFailed,
      schcode: this.props.schcode,
      stucode: this.props.stucode
    }
    return (
      <div>
        <Profile name={this.props.username} image={this.props.photo} />
        <div id='testreducer' style={{ color: 'red', textAlign: 'center' }}>{this.state.helloValue}</div>
        <div onClick={this.alipay.bind(this)} style={{ color: 'red', textAlign: 'center', border: '1px solid blue', marginTop: '100px' }}>
          点击跳转支付宝支付
        </div>
      </div>
    );
  }
  alipay() {
    let obj = {}
    obj.total_amount = 100;
    obj.stucode = this.props.stucode;
    obj.schcode = this.props.schcode;
    obj.subject = '支付宝沙箱环境支付测试';
    this.props.getAlipay.call(this, obj);
  }

2、支付宝沙箱注册应用和支付宝沙箱版本安装

https://openhome.alipay.com/platform/appDaily.htm?tab=info 支付宝登录后查看个人测试AppID


使用支付宝公钥私钥工具,生成公钥:https://docs.open.alipay.com/291/105971/

在沙箱工具扫描二维码安装安卓版本支付宝沙箱测试版本

3、Api项目编写支付宝调起相关代码逻辑

定义appid、网关等参数

//支付宝参数
const app_id = 'xxxxxx';
const return_url = 'http://172.16.117.43:3001';
const notify_url = 'http://172.16.117.43:8001/api/alipay/alipaynotify';
const gateway = 'https://openapi.alipaydev.com/gateway.do?';
声明api方法alipaysign,post类型,用于接收React页面的api请求,最终res.json(util.success(finalparams))返回支付宝支付链接
/**
 * 获取支付宝支付地址带参
 */
router.post('/alipaysign', function (req, res) {
    let total_amount = req.body.total_amount;
    let schcode = req.body.schcode;
    let stucode = req.body.stucode;
    let subject = req.body.subject;

    let out_trade_no = moment().format('YYYYMMDDHHmmss') + moment().millisecond(); // + '_' + schcode + '_' + stucode;
    console.log(out_trade_no)

    //生成系统订单并入库
    let addJson = {
        out_trade_no: out_trade_no,
        schcode: schcode,
        stucode: stucode
    };
    // alipay.addNewOrder(addJson,function(err,data){  ...  });

    let params = new Map();
    params.set('app_id', app_id);
    params.set('method', 'alipay.trade.wap.pay');
    params.set('charset', 'utf-8');
    params.set('sign_type', 'RSA2');
    params.set('timestamp', moment().format('YYYY-MM-DD HH:mm:ss'));
    params.set('version', '1.0');
    params.set('return_url', return_url);
    params.set('notify_url', notify_url);
    params.set('biz_content', buildBizContent(subject, out_trade_no, total_amount));
    params.set('sign', buildSign(params));
    let finalparams = gateway + Array.from(params).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');
    console.log(finalparams)
    res.json(util.success(finalparams));
})

生成业务请求参数的集合

function buildBizContent(subject, outTradeNo, totalAmount) {
    let bizContent = {
        subject: subject,
        out_trade_no: outTradeNo,
        total_amount: totalAmount,
        product_code: 'QUICK_MSECURITY_PAY',
    };
    return JSON.stringify(bizContent);
}

根据参数构建签名

function buildSign(paramsMap) {
    let paramsList = Array.from(paramsMap);
    paramsList.sort();
    let paramsString = paramsList.map(([k, v]) => `${k}=${v}`).join('&');
    let privateKey = fs.readFileSync(path.join(__dirname, 'alipay_private.pem'), 'utf8');
    let signType = paramsMap.get('sign_type');
    return signWithPrivateKey(signType, paramsString, privateKey);
}

通过私钥给字符串签名

function signWithPrivateKey(signType, content, privateKey) {
    let sign;
    if (signType.toUpperCase() === 'RSA2') {
        sign = crypto.createSign("RSA-SHA256");
    } else if (signType.toUpperCase() === 'RSA') {
        sign = crypto.createSign("RSA-SHA1");
    } else {
        throw new Error('请传入正确的签名方式,signType:' + signType);
    }
    sign.update(content);
    return sign.sign(privateKey, 'base64');
}

上面的代码就是一个完整的生成合规的支付宝支付链接,需要注意要用支付宝工具生成公钥私钥文件放在合适的位置。


4、测试支付

5、接收支付宝异步通知

/**
 * 通过私钥给字符串签名
 * @param signType      返回参数的签名类型:RSA2或RSA
 * @param content       需要加密的字符串
 * @param privateKey    私钥
 * @returns {number | PromiseLike<ArrayBuffer>}
 * @private
 */
function signWithPrivateKey(signType, content, privateKey) {
    let sign;
    if (signType.toUpperCase() === 'RSA2') {
        sign = crypto.createSign("RSA-SHA256");
    } else if (signType.toUpperCase() === 'RSA') {
        sign = crypto.createSign("RSA-SHA1");
    } else {
        throw new Error('请传入正确的签名方式,signType:' + signType);
    }
    sign.update(content);
    return sign.sign(privateKey, 'base64');
}


/**
 * 验证支付宝异步通知的合法性
 * @param params  支付宝异步通知结果的参数
 * @returns {*}
 */
function verifySign(params) {
    try {
        let sign = params['sign']; //签名
        let signType = params['sign_type']; //签名类型
        let paramsMap = new Map();
        for (let key in params) {
            paramsMap.set(key, params[key]);
        }
        let paramsList = Array.from(paramsMap);
        paramsList.sort();
        let paramsString = paramsList.map(([k, v]) => `${k}=${decodeURIComponent(v)}`).join('&');
        let publicKey = fs.readFileSync(path.join(__dirname, 'alipay_public.pem'), 'utf8');
        return verifyWithPublicKey(signType, sign, paramsString, publicKey);
    } catch (e) {
        console.error(e);
        return false;
    }
}

/**
 * 验证签名
 * @param signType      返回参数的签名类型:RSA2或RSA
 * @param sign          返回参数的签名
 * @param content       参数组成的待验签串
 * @param publicKey     支付宝公钥
 * @returns {*}         是否验证成功
 * @private
 */
function verifyWithPublicKey(signType, sign, content, publicKey) {
    try {
        let verify;
        if (signType.toUpperCase() === 'RSA2') {
            verify = crypto.createVerify('RSA-SHA256');
        } else if (signType.toUpperCase() === 'RSA') {
            verify = crypto.createVerify('RSA-SHA1');
        } else {
            throw new Error('未知signType:' + signType);
        }
        verify.update(content);
        return verify.verify(publicKey, sign, 'base64')
    } catch (err) {
        console.error(err);
        return false;
    }
}

/**
 * 获取支付宝支付异步通知
 */
router.post('/alipaynotify', function (req, res) {
    let signType = req.body.sign_type; //签名类型:商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
    let sign = req.body.sign; //签名:请参考<a href="#yanqian" class="bi-link">异步返回结果的验签</a>
    let tradeNo = req.body.trade_no; //支付宝交易号:支付宝交易凭证号
    let outTradeNo = req.body.out_trade_no; //商户订单号:原支付请求的商户订单号
    let buyerLogonId = req.body.buyer_logon_id; //买家支付宝账号:买家支付宝账号
    let sellerId = req.body.seller_id; //卖家支付宝用户号:卖家支付宝用户号 
    let tradeStatus = req.body.trade_status; //交易状态:交易目前所处的状态,见<a href="#jiaoyi" class="bi-link">交易状态说明</a>
    let totalAmount = req.body.total_amount; //订单金额:本次交易支付的订单金额,单位为人民币(元)
    let receiptAmount = req.body.receipt_amount; //实收金额:商家在交易中实际收到的款项,单位为元
    let gmtPayment = req.body.gmt_payment;//交易付款时间:该笔交易的买家付款时间。格式为yyyy-MM-dd HH:mm:ss

    let isSuccess = verifySign(req.body);
    console.log(isSuccess)
    if (isSuccess) {
        if (tradeStatus === 'TRADE_FINISHED') {
            //交易状态TRADE_FINISHED的通知触发条件是商户签约的产品不支持退款功能的前提下,买家付款成功;或者,商户签约的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限。

        } else if (tradeStatus === 'TRADE_SUCCESS') {
            //状态TRADE_SUCCESS的通知触发条件是商户签约的产品支持退款功能的前提下,买家付款成功

        } else if (tradeStatus === 'WAIT_BUYER_PAY') {
            //交易创建,等待买家付款

        } else if (tradeStatus === 'TRADE_CLOSED') {
            //未付款交易超时关闭,或支付完成后全额退款

        }
        res.send('success');
    } else {
        res.send('fail');
    }
});


请喊我大龙哥最后编辑于:5个月前

内容均为作者独立观点,不代表八零IT人立场,如涉及侵权,请及时告知。

评论努力加载中...
暂无评论
暂无评论

手机扫码阅读

热门相关

加载中...
关于我们   联系我们   申请友链   赞助记录   站点地图
© 2014 - 2017 www.80iter.com All Rights Reserved. 京ICP备14042174号-1
本站遵循 CC BY 4.0 协议,转载请注明出处 。