W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
版本要求:小程序開(kāi)發(fā)者工具 0.70 及以上版本。
沙箱環(huán)境可以讓開(kāi)發(fā)者在小程序上線(xiàn)到正式環(huán)境之前進(jìn)行調(diào)試和測(cè)試,不用擔(dān)心測(cè)試數(shù)據(jù)干擾正式環(huán)境,從而安全且輕松地驗(yàn)證支付等關(guān)鍵場(chǎng)景。
本篇文檔借助 demo 項(xiàng)目來(lái)演示沙箱環(huán)境的使用方法。
下載并安裝 小程序開(kāi)發(fā)者工具(簡(jiǎn)稱(chēng) IDE)。
https://sandboxdemo.alipaydev.com
。const URL = 'https://sandboxdemo.alipaydev.com';
const SIGN_TYPE = 'RSA2';
// 沙箱環(huán)境
const GATEWAY_URL = 'https://openapi.alipaydev.com/gateway.do';
// 線(xiàn)上環(huán)境
// const GATEWAY_URL = 'https://openapi.alipay.com/gateway.do';
const APP_ID = '{appId}';
const APP_PRIVATE_KEY = '{app私鑰}';
const ALIPAY_PUBLIC_KEY = '{app對(duì)應(yīng)的支付寶公鑰}';
// 調(diào)用支付寶開(kāi)放接口,沙箱環(huán)境傳參示例。
// 在正式環(huán)境中請(qǐng)勿從前端傳遞密鑰!
my.request({
url: '{exampleApi}',
data: {
appId: APP_ID,
appPrivateKey: APP_PRIVATE_KEY,
alipayPublicKey: ALIPAY_PUBLIC_KEY,
gatewayUrl: GATEWAY_URL,
signType: SIGN_TYPE,
}
}
點(diǎn)擊 預(yù)覽 按鈕,即生成二維碼,使用沙箱錢(qián)包掃碼即可體驗(yàn) demo。
本 demo 是為了支持開(kāi)發(fā)者使用自己的 appId 體驗(yàn)小程序支付服務(wù),所以采取了前端傳輸 appId、appprivatekey、alipaypublickey 到后端的方式。
上線(xiàn)小程序到生產(chǎn)環(huán)境,為了避免安全風(fēng)險(xiǎn),請(qǐng)將這些信息直接配置到后端應(yīng)用中,不要從前端傳到后端。
https://openapi.alipay.com/gateway.do
為避免安全風(fēng)險(xiǎn),在小程序正式上線(xiàn)時(shí),請(qǐng)不要使用在本 demo 中使用過(guò)的密鑰。
提示:使用線(xiàn)上環(huán)境的 appId,需要綁定“小程序支付”功能包,只有企業(yè)賬號(hào)才能綁定,如下圖所示:
為了調(diào)用支付寶沙箱環(huán)境部署的改造后的demo后端服務(wù),修改后的 client/pages/index/index.js 文件如下:
請(qǐng)將 APP_ID、APP_PRIVATE_KEY、ALIPAY_PUBLIC_KEY 改為自己沙箱小程序的 appId、應(yīng)用私鑰、對(duì)應(yīng)的支付寶公鑰。
沙箱小程序信息查看地址: https://openhome.alipay.com/platform/sandboxMini.htm
import format from './utils';
const URL = 'https://sandboxdemo.alipaydev.com';
const SIGN_TYPE = 'RSA2';
// 沙箱環(huán)境
const GATEWAY_URL = 'https://openapi.alipaydev.com/gateway.do';
// 線(xiàn)上環(huán)境
// const GATEWAY_URL = 'https://openapi.alipay.com/gateway.do';
const APP_ID = '{appId}';
const APP_PRIVATE_KEY = '{app私鑰}';
const ALIPAY_PUBLIC_KEY = '{app對(duì)應(yīng)的支付寶公鑰}';
Page({
data: {
paymentHistory: null, //支付歷史記錄
isPaying: false, //支付狀態(tài)
uid: null, //用戶(hù)ID
isLogin: false //登錄狀態(tài)
},
/**
* @name onClickHandler
* @description 查看/支付按鈕操作
*/
async onClickHandler() {
this.setData({
isPaying: true
});
if (!this.data.isLogin) {
//未登錄狀態(tài)
try {
const auth = await this.getAuthCode('auth_user');
const user = await this.getUserByAuthCode(auth.authCode);
const history = await this.getPaymentHistoryByUID(user.userId);
this.setData({
isPaying: false,
paymentHistory: history,
isLogin: true,
uid: user.userId
});
} catch (error) {
this.setData({
isPaying: false
});
this.showToast(error.message, 'exception');
}
} else {
// 已登錄
try {
const auth = await this.getAuthCode('auth_user');
const trade = await this.getTradeNo(auth.authCode, this.data.uid);
const payStatus = await this.cashPaymentTrade(trade.tradeNo);
this.showToast(payStatus.message);
const updatePayment = await this.updatePaymentListByTradeNo(trade.tradeNo);
this.setData({
paymentHistory: updatePayment,
isPaying: false
});
} catch (error) {
this.setData({
isPaying: false
});
this.showToast(error.message, 'exception');
}
}
},
getAvatarHandler() {
return new Promise(async (resolve, reject) => {
try {
await this.getAuthCode('auth_user');
const user = await this.getAuthUserInfo();
resolve(user);
} catch (error) {
reject(error);
}
});
},
getAuthUserInfo() {
return new Promise((resolve, reject) => {
my.getAuthUserInfo({
success: (user) => {
resolve(user);
},
fail: (error) => {
reject({
message: '獲取用戶(hù)頭像失敗',
error
});
}
});
});
},
toast(message) {
my.showToast({
content: message,
duration: 3000
});
},
/**
* @name onRefundPayHandler
* @description 發(fā)起退款
* @param {*} event
*/
async onRefundPayHandler(event) {
const { key } = event.target.dataset;
const refundItem = await this.findActiveTradeByNo(key);
try {
if (refundItem !== null) {
const refundOrder = await this.refundPaymentByTradeNo(
refundItem.tradeNo,
refundItem.totalAmount
);
const updatePayment = await this.updatePaymentListByTradeNo(refundOrder.tradeNo);
this.showToast('退款成功');
this.setData({
paymentHistory: updatePayment
});
} else {
this.showToast('未知支付訂單', 'exception');
}
} catch (error) {
this.showToast(error.message, 'exception');
}
},
/**
* @name onRepeatPayHandler
* @description 列表重新付款
* @param {*} event
*/
async onRepeatPayHandler(event) {
const { key } = event.target.dataset;
const repeatItem = await this.findActiveTradeByNo(key);
try {
if (repeatItem !== null) {
const payStatus = await this.cashPaymentTrade(repeatItem.tradeNo);
this.showToast(payStatus.message);
const updatePayment = await this.updatePaymentListByTradeNo(repeatItem.tradeNo);
this.setData({
paymentHistory: updatePayment
});
} else {
this.showToast('未知支付訂單', 'exception');
}
} catch (error) {
this.showToast(error.message, 'exception');
}
},
/**
* @name findActiveTradeByNo
* @description 查找當(dāng)前操作項(xiàng)
* @param {*} tradeNo
* @returns
*/
async findActiveTradeByNo(tradeNo) {
const findItem = this.data.paymentHistory.find((item) => {
return item.key === tradeNo;
});
if (findItem !== undefined) {
findItem.actionStatus = true;
this.setData({
paymentHistory: this.data.paymentHistory
});
return findItem;
} else {
return null;
}
},
/**
* @name updatePaymentListByTradeNo
* @description 根據(jù)tradeNo更新列表數(shù)據(jù)
* @param {*} tradeNo
* @returns
*/
async updatePaymentListByTradeNo(tradeNo) {
let isExistOrder = false;
const order = await this.queryPaymentByTradeNo(tradeNo);
const formatHistory = this.data.paymentHistory.map((item) => {
if (item.tradeNo === order.tradeNo) {
isExistOrder = true;
item.key = order.tradeNo;
item.tradeNo = order.tradeNo;
item.actionStatus = false;
item.totalAmount = order.totalAmount;
item.tradeStatus = order.tradeStatus;
item.viewTime = format(order.sendPayDate, 'yyyy-MM-dd hh:mm:ss');
}
return item;
});
if (!isExistOrder) {
const addOrder = {};
addOrder.key = order.tradeNo;
addOrder.actionStatus = false;
addOrder.tradeNo = order.tradeNo;
addOrder.totalAmount = order.totalAmount;
addOrder.tradeStatus = order.tradeStatus;
addOrder.viewTime = format(order.sendPayDate, 'yyyy-MM-dd hh:mm:ss');
formatHistory.unshift(addOrder);
}
return formatHistory;
},
/***************************/
/******* 封裝服務(wù)端 API ******/
/***************************/
/**
* @name getUserByAuthCode
* @description 獲取用戶(hù)信息
* @param {*} authCode
* @returns
*/
getUserByAuthCode(authCode) {
return new Promise((resolve, reject) => {
my.request({
url: `${URL}/alipay/pay/alipayUserInfo`,
data: {
appId: APP_ID,
appPrivateKey: APP_PRIVATE_KEY,
alipayPublicKey: ALIPAY_PUBLIC_KEY,
gatewayUrl: GATEWAY_URL,
signType: SIGN_TYPE,
authCode: authCode
},
success: (result) => {
if (!result.data.success) {
reject({
...result.data,
message: '獲取用戶(hù)信息失敗'
});
}
resolve(result.data);
},
fail: (err) => {
reject({
...err,
message: '獲取用戶(hù)信息異常'
});
}
});
});
},
/**
* @name getPaymentHistoryByUID
* @description 獲取登錄用戶(hù)的支付歷史記錄
* @param {*} uid
* @returns {Array/object}
*/
getPaymentHistoryByUID(uid) {
return new Promise((resolve, reject) => {
my.request({
url: `${URL}/alipay/pay/userPay`,
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
data: {
appId: APP_ID,
appPrivateKey: APP_PRIVATE_KEY,
alipayPublicKey: ALIPAY_PUBLIC_KEY,
gatewayUrl: GATEWAY_URL,
signType: SIGN_TYPE,
userId: uid
},
success: (result) => {
if (!result.data.success) {
reject({
...result.data,
message: '獲取支付歷史失敗'
});
} else {
const formatHistory = result.data.alipayTradeQueryList.map((item) => {
const order = {};
order.key = item.tradeNo;
order.tradeNo = item.tradeNo;
order.actionStatus = false;
order.totalAmount = item.totalAmount;
order.tradeStatus = item.tradeStatus;
order.viewTime = format(item.sendPayDate, 'yyyy-MM-dd hh:mm:ss');
return order;
});
resolve(formatHistory);
}
},
fail: (err) => {
reject({
...err,
message: '獲取支付歷史異常'
});
}
});
});
},
/**
* @name getTradeNo
* @description 創(chuàng)建支付交易訂單
* @param {*} authCode
* @param {*} uid
* @returns {object}
*/
getTradeNo(authCode, uid) {
return new Promise((resolve, reject) => {
my.request({
url: `${URL}/alipay/pay/alipayTradeCreate`,
data: {
appId: APP_ID,
appPrivateKey: APP_PRIVATE_KEY,
alipayPublicKey: ALIPAY_PUBLIC_KEY,
gatewayUrl: GATEWAY_URL,
signType: SIGN_TYPE,
total_amount: '0.01',
out_trade_no: `${new Date().getTime()}_demo_pay`,
scene: 'bar_code',
auth_code: authCode,
subject: '小程序支付演示DEMO',
buyer_id: uid
},
success: (result) => {
if (!result.data.success) {
reject({
...result.data,
message: '創(chuàng)建支付訂單失敗'
});
} else {
resolve(result.data);
}
},
fail: (err) => {
reject({
...err,
message: '創(chuàng)建支付訂單異常'
});
}
});
});
},
/**
* @name queryPaymentByTradeNo
* @description 查詢(xún)單筆訂單
* @param {*} tradeNo
* @returns
*/
queryPaymentByTradeNo(tradeNo) {
return new Promise((resolve, reject) => {
my.request({
url: `${URL}/alipay/pay/alipayTradeQuery`,
data: {
appId: APP_ID,
appPrivateKey: APP_PRIVATE_KEY,
alipayPublicKey: ALIPAY_PUBLIC_KEY,
gatewayUrl: GATEWAY_URL,
signType: SIGN_TYPE,
trade_no: tradeNo
},
success: (result) => {
if (!result.data.success) {
reject({
message: '支付查詢(xún)失敗',
...result.data
});
} else {
resolve(result.data);
}
},
fail: (err) => {
reject({
message: '支付查詢(xún)異常',
...err
});
}
});
});
},
/**
* @name refundPaymentByTradeNo
* @description 退款流程
* @param {*} tradeNo
* @param {*} refundAmount
*/
refundPaymentByTradeNo(tradeNo, refundAmount) {
return new Promise((resolve, reject) => {
my.request({
url: `${URL}/alipay/pay/alipayTradeRefund`,
data: {
appId: APP_ID,
appPrivateKey: APP_PRIVATE_KEY,
alipayPublicKey: ALIPAY_PUBLIC_KEY,
gatewayUrl: GATEWAY_URL,
signType: SIGN_TYPE,
trade_no: tradeNo,
refund_amount: refundAmount
},
success: (result) => {
if (!result.data.success) {
reject({
message: '退款失敗',
...result.data
});
} else {
resolve(result.data);
}
},
fail: (err) => {
reject({
message: '退款異常',
...err
});
}
});
});
},
/***************************/
/******* 封裝小程序 API ******/
/***************************/
/**
* @name getAuthCode
* @description 獲取用戶(hù)授權(quán)
* @param {string} [scopeCode='auth_user']
* @returns {object}
*/
getAuthCode(scopeCode = 'auth_user') {
return new Promise((resolve, reject) => {
my.getAuthCode({
scopes: scopeCode,
success: (auth) => {
console.log(auth);
resolve(auth);
},
fail: (err) => {
console.log(err);
reject({ ...err, message: '獲取用戶(hù)授權(quán)失敗' });
}
});
});
},
/**
* @name cashPaymentTrade
* @description 發(fā)起支付
* @param {*} tradeNo
* @returns
*/
cashPaymentTrade(tradeNo) {
return new Promise((resolve, reject) => {
my.tradePay({
tradeNO: tradeNo,
success: (result) => {
if (result.resultCode != 9000) {
resolve({
status: false,
message: result.memo,
...result
});
} else {
resolve({
status: true,
message: '支付成功',
...result
});
}
},
fail: (err) => {
reject({
status: false,
message: '支付異常',
...err
});
}
});
});
},
/**
* @name showToast
* @description 通用提示信息
* @param {*} message
* @param {string} [type='none']
*/
showToast(message, type = 'none') {
my.showToast({
type,
content: message,
duration: 3000
});
}
});
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: