我們?cè)谧鲂〕绦蛑Ц断嚓P(guān)的開發(fā)時(shí),總會(huì)遇到這些難題。小程序調(diào)用微信支付時(shí),必須要有自己的服務(wù)器,有自己的備案域名,有自己的后臺(tái)開發(fā)。這就導(dǎo)致我們做小程序支付時(shí)的成本很大。本節(jié)就來教大家如何使用小程序云開發(fā)實(shí)現(xiàn)小程序支付功能的開發(fā)。不用搭建自己的服務(wù)器,不用有自己的備案域名。只需要簡(jiǎn)簡(jiǎn)單單的使用小程序云開發(fā)。
老規(guī)矩先看效果圖:
本節(jié)知識(shí)點(diǎn)
1,云開發(fā)的部署和使用
2,支付相關(guān)的云函數(shù)開發(fā)
3,商品列表
4,訂單列表
5,微信支付與支付成功回調(diào)
支付成功給用戶發(fā)送推送消息的功能會(huì)在后面講解。
下面就來教大家如何借助云開發(fā)使用小程序支付功能。
支付所需要用到的配置信息
1,小程序appid
2,云開發(fā)環(huán)境id
3,微信商戶號(hào)
4,商戶密匙
一,準(zhǔn)備工作
1,已經(jīng)申請(qǐng)小程序,獲取小程序 AppID 和 Secret 在小程序管理后臺(tái)中,【設(shè)置】 →【開發(fā)設(shè)置】 下可以獲取微信小程序 AppID 和 Secret。
2,微信支付商戶號(hào),獲取商戶號(hào)和商戶密鑰在微信支付商戶管理平臺(tái)中,【賬戶中心】→【商戶信息】 下可以獲取微信支付商戶號(hào)。
在【賬戶中心】 ?> 【API安全】 下可以設(shè)置商戶密鑰。
這里特殊說明下,個(gè)人小程序是沒有辦法使用微信支付的。所以如果想使用微信支付功能,必須是非個(gè)人賬號(hào)(當(dāng)然個(gè)人可以辦個(gè)體戶工商執(zhí)照來注冊(cè)非個(gè)人小程序賬號(hào))
?
二,商品列表的實(shí)現(xiàn)
效果圖如下,由于本節(jié)重點(diǎn)是支付的實(shí)現(xiàn),所以這里只簡(jiǎn)單貼出關(guān)鍵代碼。
wxml布局如下:
<view class="container"> <view class="good-item" wx:for="{{goods}}" wx:key="*this" ontap="getDetail" data-goodid="{{item._id}}"> <view class="good-image"> <image src="{{pic}}"></image> </view> <view class="good-detail"> <view class="title">商品: {{item.name}}</view> <view class="content">價(jià)格: {{item.price / 100}} 元 </view> <button class="button" type="primary" bindtap="makeOrder" data-goodid="{{item._id}}" >下單</button> </view> </view> </view>
我們所需要做的就是借助云開發(fā)獲取云數(shù)據(jù)庫(kù)里的商品信息,然后展示到商品列表,關(guān)于云開發(fā)獲取商品列表并展示本節(jié)不做講解(感興趣的同學(xué)可以翻看我的歷史博客,有寫過的)
三,支付云函數(shù)的創(chuàng)建
首先看下我們支付云函數(shù)都包含那些內(nèi)容
簡(jiǎn)單先講解下每個(gè)的用處
config下的index.js是做支付配置用的,主要配置支付相關(guān)的賬號(hào)信息
lib是用的第三方的支付庫(kù),這里不做講解。
重點(diǎn)講解的是云函數(shù)入口 index.js
下面就來教大家如何去配置
1,配置config下的index.js,
這一步所需要做的就是把小程序appid,云開發(fā)環(huán)境ID,商戶id,商戶密匙。填進(jìn)去。
2,配置入口云函數(shù)
詳細(xì)代碼如下,代碼里注釋很清除了,這里不再做單獨(dú)講解:
const cloud = require('wx-server-sdk') cloud.init() const app = require('tcb-admin-node'); const pay = require('./lib/pay'); const { mpAppId, KEY } = require('./config/index'); const { WXPayConstants, WXPayUtil } = require('wx-js-utils'); const Res = require('./lib/res'); const ip = require('ip'); /** * * @param {obj} event * @param {string} event.type 功能類型 * @param {} userInfo.openId 用戶的openid */ exports.main = async function(event, context) { const { type, data, userInfo } = event; const wxContext = cloud.getWXContext() const openid = userInfo.openId; app.init(); const db = app.database(); const goodCollection = db.collection('goods'); const orderCollection = db.collection('order'); // 訂單文檔的status 0 未支付 1 已支付 2 已關(guān)閉 switch (type) { // [在此處放置 unifiedorder 的相關(guān)代碼] case 'unifiedorder': { // 查詢?cè)撋唐?ID 是否存在于數(shù)據(jù)庫(kù)中,并將數(shù)據(jù)提取出來 const goodId = data.goodId let goods = await goodCollection.doc(goodId).get(); if (!goods.data.length) { return new Res({ code: 1, message: '找不到商品' }); } // 在云函數(shù)中提取數(shù)據(jù),包括名稱、價(jià)格才更合理安全, // 因?yàn)閺亩死飩鬟^來的商品數(shù)據(jù)都是不可靠的 let good = goods.data[0]; // 拼湊微信支付統(tǒng)一下單的參數(shù) const curTime = Date.now(); const tradeNo = `${goodId}-${curTime}`; const body = good.name; const spbill_create_ip = ip.address() || '127.0.0.1'; // 云函數(shù)暫不支付 http 觸發(fā)器,因此這里回調(diào) notify_url 可以先隨便填。 const notify_url = 'http://www.qq.com'; //'127.0.0.1'; const total_fee = good.price; const time_stamp = '' + Math.ceil(Date.now() / 1000); const out_trade_no = `${tradeNo}`; const sign_type = WXPayConstants.SIGN_TYPE_MD5; let orderParam = { body, spbill_create_ip, notify_url, out_trade_no, total_fee, openid, trade_type: 'JSAPI', timeStamp: time_stamp, }; // 調(diào)用 wx-js-utils 中的統(tǒng)一下單方法 const { return_code, ...restData } = await pay.unifiedOrder(orderParam); let order_id = null; if (return_code === 'SUCCESS' && restData.result_code === 'SUCCESS') { const { prepay_id, nonce_str } = restData; // 微信小程序支付要單獨(dú)進(jìn)地簽名,并返回給小程序端 const sign = WXPayUtil.generateSignature({ appId: mpAppId, nonceStr: nonce_str, package: `prepay_id=${prepay_id}`, signType: 'MD5', timeStamp: time_stamp }, KEY); let orderData = { out_trade_no, time_stamp, nonce_str, sign, sign_type, body, total_fee, prepay_id, sign, status: 0, // 訂單文檔的status 0 未支付 1 已支付 2 已關(guān)閉 _openid: openid, }; let order = await orderCollection.add(orderData); order_id = order.id; } return new Res({ code: return_code === 'SUCCESS' ? 0 : 1, data: { out_trade_no, time_stamp, order_id, ...restData } }); } // [在此處放置 payorder 的相關(guān)代碼] case 'payorder': { // 從端里出來相關(guān)的訂單相信 const { out_trade_no, prepay_id, body, total_fee } = data; // 到微信支付側(cè)查詢是否存在該訂單,并查詢訂單狀態(tài),看看是否已經(jīng)支付成功了。 const { return_code, ...restData } = await pay.orderQuery({ out_trade_no }); // 若訂單存在并支付成功,則開始處理支付 if (restData.trade_state === 'SUCCESS') { let result = await orderCollection .where({ out_trade_no }) .update({ status: 1, trade_state: restData.trade_state, trade_state_desc: restData.trade_state_desc }); let curDate = new Date(); let time = `${curDate.getFullYear()}-${curDate.getMonth() + 1}-${curDate.getDate()} ${curDate.getHours()}:${curDate.getMinutes()}:${curDate.getSeconds()}`; } return new Res({ code: return_code === 'SUCCESS' ? 0 : 1, data: restData }); } case 'orderquery': { const { transaction_id, out_trade_no } = data; // 查詢訂單 const { data: dbData } = await orderCollection .where({ out_trade_no }) .get(); const { return_code, ...restData } = await pay.orderQuery({ transaction_id, out_trade_no }); return new Res({ code: return_code === 'SUCCESS' ? 0 : 1, data: { ...restData, ...dbData[0] } }); } case 'closeorder': { // 關(guān)閉訂單 const { out_trade_no } = data; const { return_code, ...restData } = await pay.closeOrder({ out_trade_no }); if (return_code === 'SUCCESS' && restData.result_code === 'SUCCESS') { await orderCollection .where({ out_trade_no }) .update({ status: 2, trade_state: 'CLOSED', trade_state_desc: '訂單已關(guān)閉' }); } return new Res({ code: return_code === 'SUCCESS' ? 0 : 1, data: restData }); } } }
其實(shí)我們支付的關(guān)鍵功能都在上面這些代碼里面了。
再來看下,支付的相關(guān)流程截圖
上圖就涉及到了我們的訂單列表,支付狀態(tài),支付成功后的回調(diào)。
今天就先講到這里,后面會(huì)繼續(xù)給大家講解支付的其他功能。比如支付成功后的消息推送,也是可以借助云開發(fā)實(shí)現(xiàn)的。
?
本文摘自 :https://blog.51cto.com/u