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