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支付跳轉時跟網絡環境也有很大關係,國內外網絡限速會導致跳轉時經常出現連接超時。

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