PayPal支付开发(Vue.js -- node.jsKoa2)

补充一下:想选择Paypal做支付的,慎选,由于网络服务器网速原因访问部分网站网速极其的慢,他的支付网站还设置timeout过时,导致跳转到他的支付页 资源都没全部拉取下来就timeout掉了。有段时间情况好一点,有段时间完全不能访问!

注:找了个韩国的VPN网络测速正常。可能跟国内外网络封锁有很大关系!

开发者中心地址:https://developer.paypal.com/developer/applications/

开发文档:https://developer.paypal.com/docs/api/quickstart/payments/#additional-information

开发文档为多种语言提供测试demo。

1.进入开发者中心注册一个账户用于测试。Sandbox用于测试API里面有虚拟货币用于支付,Live真实支付环境。

在Sandbox下 CreateApp创建一个项目。

拿到Client ID与Secret。准备工作就以上这么多。

一:开发步骤

//安装paypal依赖库
cnpm install paypal-rest-sdk

二:创建configuration文件。

var paypal = require('paypal-rest-sdk');

paypal.configure({
    'mode': 'sandbox', //sandbox or live
    'client_id': '填入刚刚申请的client Id',
    'client_secret': '填入刚刚申请的secret'
})

三:准备请求接口。

/**
 * (新)实体商品Paypal支付
 */
router.post('/getPaymentPhysical', controllPayPal.getPaymentPhysical)

/**
 * (新)实体商品支付成功回调创建订单!
 */
router.get('/PaymentSucceedPhysical', controllPayPal.PaymentSucceedPhysical)

四:业务层

const PayPalImpl = require('../src/PayPalImpl')
const utils = require('../resource/utils')
const jwt = require("jsonwebtoken")
const config = require('../config')

/**
 * 商品支付
 * @param {*} ctx 
 */
async function getPaymentPhysical(ctx) {
    //商品参数信息
    var create_payment_json = JSON.stringify({
        intent: 'sale',
        payer: {
            payment_method: 'paypal'
        },
        redirect_urls: {//return_url支付成功回调  cancel_url取消支付返回跳转地址。
            return_url: "http://mychen.vip:3001/petshop/v1/api/PaymentSucceedPhysical?orderContent=" + ctx.request.body.orderContent,
            cancel_url: "http://127.0.0.1:8081/#/PhysicalProductDetails"
        },
        transactions: [{ //amount 对象 跟item_list对象要注意一下,下面前端页集成数据结构有详细描述
            amount: {
                total: ctx.request.body.totalPrice,
                currency: 'USD',
                details: {
                    subtotal: ctx.request.body.subtotal,
                    tax: ctx.request.body.tax
                }
            },
            description: '支付测试!',
            // invoice_number: '485787589673',
            payment_options: {
                allowed_payment_method: 'INSTANT_FUNDING_SOURCE'
            },
            item_list: {
                items: ctx.request.body.items
            }
        }]
    });
    await PayPalImpl.getPaymentImpl(create_payment_json).then(res => {
        console.log(res)
        ctx.body = res
    })
}

/**
 * 商品支付成功回调创建订单
 */
async function PaymentSucceedPhysical(ctx) {
    var paymentId = ctx.query.paymentId;
    var payerId = { payer_id: ctx.query.PayerID };
    var insertStr = jwt.verify(ctx.query.orderContent, config.secret2);
    for (var i = 0; i < insertStr.length; i++) {
        insertStr[i].physicalOrderId = utils.generateProductId("");   //订单id
        insertStr[i].paymentId = paymentId;
        insertStr[i].payerId = payerId.payer_id;
        insertStr[i].Status = 101;
        insertStr[i].findStatus = 0;
        insertStr[i].createTime = utils.getTime();
        insertStr[i].userOpenId = jwt.verify(insertStr[i].userOpenId, config.secret)
    }
    console.log(insertStr)
    await PayPalImpl.PaymentSucceedPhysicalImpl(paymentId, payerId, insertStr).then(res => {
        ctx.response.redirect("http://127.0.0.1:8081/#/PhysicalProductDetails")
    })
}

五:逻辑层

var paypal = require('paypal-rest-sdk');
require('../configuretion');
const MongoClient = require('mongodb').MongoClient
const config = require('../config');

async function getPaymentImpl(create_payment_json) {
    return new Promise((resolv) => {
        //打开付款页面
        paypal.payment.create(create_payment_json, function (error, payment) {
            var links = {};
            if (error) {
                console.error(JSON.stringify(error));
                resolv({ code: 201, msg: '请求失败!', url: "" })
            } else {
                // Capture HATEOAS links
                payment.links.forEach(function (linkObj) {
                    links[linkObj.rel] = {
                        href: linkObj.href,
                        method: linkObj.method
                    };
                })
                // If redirect url present, redirect user
                if (links.hasOwnProperty('approval_url')) {
                    // REDIRECT USER TO links['approval_url'].href;
                    console.log(links.approval_url.href)
                    resolv({ code: 200, msg: '请求成功!', url: links.approval_url.href })
                } else {
                    console.error('no redirect URI present');
                    resolv({ code: 201, msg: '请求失败!', url: "" })
                }
            }
        });
    })
}

async function PaymentSucceedPhysicalImpl(paymentId, payerId, insertStr) {
    return new Promise((resolv) => {
        paypal.payment.execute(paymentId, payerId, function (error, payment) {
            if (error) {
                console.error(JSON.stringify(error));
            } else {
                if (payment.state == 'approved') {
                    console.log('payment completed successfully');
                    MongoClient.connect(config.Mongose.url, { useNewUrlParser: true }, function (err, db) {
                        if (err) throw err;
                        var dbo = db.db("petshop");
                        dbo.collection('physicalOrders').insertMany(insertStr, function (err, res) {
                            if (err) throw err;
                            db.close();
                            resolv({ code: 200, msg: "付款成功!" });
                        })
                    })
                } else {
                    console.log('payment not successful');
                    resolv({ code: 201, msg: "付款失败!" })
                }
            }
        })
    })
}

六:前端集成支付数据。

    onSubmit() {
		if(this.addressContent) {
		    Toast.loading({
				mask: true,
				message: "正在跳转三方支付网站!请勿操作!"
			});
			console.log(this.orderContent)
			console.log(this.addressContent)
			var contents = [{//这个是我的订单数据结构,订单数据结构按自己维护为准。之所以用数组是为了方便以后购物车多商品支付。
				userOpenId: JSON.parse(localStorage.getItem("userInfo")).openId,
				openId: this.orderContent.openId,
				ShopName: this.$store.state.StoreDetails.ShopName,
				paymentAmount: this.orderContent.Price,
				freight: this.orderContent.trafficPrice,
				comAmount: 1,
				consignee: this.addressContent,
				orders: [{productId:this.orderContent.productId,sum:this.value}]
			}]
			var data = {
//注意:你的items参数中的price值一定要与支付金额一致否则getPaymentPhysical接口执行出错。
				totalPrice: this.orderContent.Price+this.orderContent.trafficPrice,
				subtotal: this.orderContent.Price+this.orderContent.trafficPrice,
				tax: 0,
				items: [{
				    name: this.orderContent.productName,//商品名称
				    quantity: this.value.toString(), //商品数量
				    price: this.orderContent.Price, //商品价格
				    tax: '0',   //税率,建议不要维护税率直接给0后台自己记录税率问题就好了。PayPal税率结算机制我没弄懂,生成支付路径一直报错。
				    sku: this.orderContent.productTaste,
				    currency: 'USD'
			    },{
				    name: '运费',
				    quantity: '1',
				    price: this.orderContent.trafficPrice,
				    tax: '0', 
				    sku: '运费',
				    currency: 'USD'
			    }],
			    orderContent: this.$jwt.sign(JSON.stringify(contents), this.$secretKey)
			}
			console.log(contents)
			console.log(data)
			this.$Request_post(this.$request + '/getPaymentPhysical', data).then(res => {
				console.log(res)
				window.location.replace(res.data.url)
			})
		} else {
			console.log("请去添加您的收货地址")
			Dialog.confirm({
				title: '提示!',
				message: '您还没有添加您的收货地址!'
			}).then(() => {
				this.$router.push({
					name: "AddConsignee"
				})
			}).catch(() => {
				// on cancel
					console.log("取消")
				});
			}
	},

注意:你的items参数中的price值一定要与支付金额一致否则getPaymentPhysical接口执行出错

再次申明:contents与orderContent是个人维护的数据结构,订单信息最好按自己系统要求满足。

同时关于支付成功的回调接口只能用get请求。(重点要考的)但是可以携带参数比如 ?productId=123456

PayPal支付跳转时跟网络环境也有很大关系,国内外网络限速会导致跳转时经常出现连接超时。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章