沙箱環(huán)境 (Beta) 是協(xié)助開發(fā)者進(jìn)行接口功能開發(fā)及主要功能聯(lián)調(diào)的輔助環(huán)境,模擬了開放平臺部分產(chǎn)品的主要功能和主要邏輯。可用于在產(chǎn)品上線前了解環(huán)境、組合和調(diào)試各種接口。
沙箱環(huán)境配置
打開 支付寶開發(fā)者中心 并登錄,點擊 => 進(jìn)入我的控制臺(也可能登錄之后自動進(jìn)入),
在開發(fā)者中心中點擊開發(fā)服務(wù)下的研發(fā)服務(wù),就進(jìn)入沙箱環(huán)境頁面了,
在沙箱應(yīng)用可以看到基本配置。
密鑰配置
點擊 RSA2(SHA256)密鑰 設(shè)置,
點擊 支付寶密鑰生成器,下載對應(yīng)版本的工具,下載完成后將工具安裝在不包含空格的目錄中,
然后點打開,點擊生成密鑰,即可生成商戶應(yīng)用私鑰與商戶應(yīng)用公鑰。然后點擊復(fù)制公鑰。
回到沙箱界面,選擇公鑰,并把剛剛復(fù)制的公鑰粘貼進(jìn)去;
然后就可以得到支付寶公鑰了,代碼中會用到。
node API 配置
首先安裝 Alipay SDK:
npm install alipay-sdk -S
// sdk 配置語法
alipaySdk.exec(method, params, options);
- method:字符串類型,調(diào)用的 Api,比如 alipay.trade.page.pay ;
- params:可選參數(shù),對象結(jié)構(gòu),Api 的請求參數(shù);
- options:包含
- validateSign:布爾值,是否對返回值驗簽,需要依賴支付寶公鑰;
- formData:對象結(jié)構(gòu),文件上傳類接口的請求參數(shù);
- log:對象結(jié)構(gòu),存在時會調(diào)用 info、error 方法寫日志。
由于每次調(diào)用 AlipaySdk 的 API 都是同一個對象,所以該對象只需要實例化一次:
// alipay.js 這里單獨存放一個文件中,需要時引入即可
const AlipaySdk = require('alipay-sdk').default; // 引入 SDK
const alipaySdk = new AlipaySdk({
AppId: 'appId', // 開放平臺上創(chuàng)建應(yīng)用時生成的 appId
signType: 'RSA2', // 簽名算法,默認(rèn) RSA2
gateway: 'https://openapi.alipaydev.com/gateway.do', // 支付寶網(wǎng)關(guān)地址 ,沙箱環(huán)境下使用時需要修改
alipayPublicKey: 'public_key', // 支付寶公鑰,需要對結(jié)果驗簽時候必填
privateKey: 'private_key', // 應(yīng)用私鑰字符串
});module.exports = alipaySdk;
要完成支付,需要以下幾個步驟,
- 服務(wù)器端需要調(diào)用支付 API alipay.trade.page.pay 來獲取支付頁面的地址;
- 將得到的支付地址發(fā)給客戶端,由客戶端進(jìn)行頁面跳轉(zhuǎn);
先來看看服務(wù)端接口的實現(xiàn):
var express = require('express');
var router = express.Router();const alipaySdk = require('../utils/alipay');
const AlipayFormData = require('alipay-sdk/lib/form').default; // alipay.trade.page.pay 返回的內(nèi)容為 Form 表單
router.post('/pcpay', (req, res) => {
(async () => { // 調(diào)用 setMethod 并傳入 get,會返回可以跳轉(zhuǎn)到支付頁面的 url
const formData = new AlipayFormData();
formData.setMethod('get');
// 通過 addField 增加參數(shù)
// 在用戶支付完成之后,支付寶服務(wù)器會根據(jù)傳入的 notify_url,以 POST 請求的形式將支付結(jié)果作為參數(shù)通知到商戶系統(tǒng)。
formData.addField('notifyUrl', 'http://www.com/notify'); // 支付成功回調(diào)地址,必須為可以直接訪問的地址,不能帶參數(shù)
formData.addField('bizContent', {
outTradeNo: req.body.outTradeNo, // 商戶訂單號,64個字符以內(nèi)、可包含字母、數(shù)字、下劃線,且不能重復(fù)
productCode: 'FAST_INSTANT_TRADE_PAY', // 銷售產(chǎn)品碼,與支付寶簽約的產(chǎn)品碼名稱,僅支持FAST_INSTANT_TRADE_PAY
totalAmount: '0.01', // 訂單總金額,單位為元,精確到小數(shù)點后兩位
subject: '商品', // 訂單標(biāo)題
body: '商品詳情', // 訂單描述
}); // 如果需要支付后跳轉(zhuǎn)到商戶界面,可以增加屬性"returnUrl"
const result = await alipaySdk.exec(
'alipay.trade.page.pay', // 統(tǒng)一收單下單并支付頁面接口
{}, // api 請求的參數(shù)(包含“公共請求參數(shù)”和“業(yè)務(wù)參數(shù)”)
{ formData: formData }, ); // result 為可以跳轉(zhuǎn)到支付鏈接的 url
res.json({ url: result }); })();});
然后就是前端頁面,這個比較簡單,就是點擊支付按鈕,向服務(wù)器發(fā)起請求,拿到返回的支付頁面地址后進(jìn)行跳轉(zhuǎn):
$.ajax({
method: 'POST',
url: '/alipay/pcpay',
data: { outTradeNo // 商戶訂單號,必須保證唯一,生成方法有很多,可以去看我的代碼 }}).done(function(res) {
window.open(res.url, '_blank');
}).fail(function(err) {
console.log(err);
});
如果上訴沒有問題,我們應(yīng)該能看到這樣的頁面:
如果是使用沙箱環(huán)境,必須下載沙箱錢包來完成支付,下載地方如下所示:
下載完成后,使用沙箱提供的賬號登陸即可,隨便充值,隨便消費,其實沙箱錢包里就是一個數(shù)字而已。
然后這里補充一個點,就是前端如何判斷用戶是否已經(jīng)支付。我們都知道前端的手段是不被信任的,所以不能依賴前端來判斷,唯一可靠的方法是使用支付寶 API 中的 alipay.trade.query 來查詢是否已經(jīng)完成支付。
可以通過前端發(fā)送訂單號到服務(wù)器進(jìn)行查詢,服務(wù)器向支付寶服務(wù)器查詢該訂單號支付的相關(guān)信息,通過交易狀態(tài)來確定是否已經(jīng)完成支付,具體服務(wù)器配置如下:
const axIOS = require('axios');
const alipaySdk = require('../utils/alipay');
const AlipayFormData = require('alipay-sdk/lib/form').default;
router.get('/query', function(req, res) {
(async function() {
const { outTradeNo } = req.query; const formData = new AlipayFormData(); formData.setMethod('get');
formData.addField('bizContent', {
outTradeNo }); // 通過該接口主動查詢訂單狀態(tài) const result = await alipaySdk.exec( 'alipay.trade.query',
{}, { formData: formData }, ); axios({ method: 'GET',
url: result }) .then(data => {
let r = data.data.alipay_trade_query_response; if(r.code === '10000') { // 接口調(diào)用成功
switch(r.trade_status) { case 'WAIT_BUYER_PAY':
res.send('交易創(chuàng)建,等待買家付款');
break;
case 'TRADE_CLOSED':
res.send('未付款交易超時關(guān)閉,或支付完成后全額退款');
break;
case 'TRADE_SUCCESS':
res.send('交易支付成功');
break;
case 'TRADE_FINISHED':
res.send('交易結(jié)束,不可退款');
break;
} } else if(r.code === '40004') {
res.send('交易不存在');
} }) .catch(err => { res.json({ msg: '查詢失敗',
err }); }); })();});
【本文轉(zhuǎn)載自我的微信公眾號-飛舟技術(shù)社區(qū)】
【喜歡我的文章歡迎 轉(zhuǎn)發(fā) 點贊 與 關(guān)注,我會經(jīng)常與大家分享前端的知識點的】