文章目录
- 前言
- 一、微信支付小程序内部API功能实现
- 1.相关小程序代码
- 2.pay支付组件
- 3.效果
前言
微信支付是腾讯集团旗下的第三方支付平台,致力于为用户和企业提供安全、便捷、专业的在线支付服务。以“微信支付,不止支付”为核心理念,为个人用户创造了多种便民服务和应用场景。微信支付为各类企业以及小微商户提供专业的收款能力,运营能力,资金结算解决方案,以及安全保障。用户可以使用微信支付来购物、吃饭、旅游、就医、交水电费等。企业、商品、门店、用户已经通过微信连在了一起,让智慧生活,变成了现实。
小程序实现微信支付主要有两种方式:
- 小程序内部API,要求商户开通了小程序支付功能
- 第三方网站
一、微信支付小程序内部API功能实现
统一下单接口地址: URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder URL地址:https://api2.mch.weixin.qq.com/pay/unifiedorder
1.相关小程序代码
代码语言:javascript复制// miniprogram/pages/confirm-order/index.js
Page({
/**
* 页面的初始数据
*/
data: {
carts:[],
userMessage:'',
totalPrice:0,
address:{
userName:'选择'
},
submchPayParams: {},
submchPayorderResult:{},
prepareSubmchPay: false
},
/**
* 确认订单
*/
onSubmit(e){
wx.showActionSheet({
itemList: ['默认支付', '小微商户'],
success:(res)=> {
console.log(res.tapIndex)
let index = res.tapIndex
if (index == 0){// 默认支付
this.startNormalPay(e)
}
},
fail (res) {
console.log(res.errMsg)
}
})
},
// 发起支付
async startNormalPay(e) {
if (!this.data.address.id) {
wx.showModal({
title: '没有选择收货地址',
showCancel: false
})
return
}
let address = this.data.address
let addressDesc = `${address.userName},${address.telNumber},${address.region.join('')}${address.detailInfo}`
let carts = this.data.carts
let goodsCartsIds = carts.map(item => item.id)
let goodsNameDesc = carts.map(item => `${item.goods_name}(${item.sku_desc})x${item.num}`).join(',')
if (goodsNameDesc.length > 200) goodsNameDesc = goodsNameDesc.substr(0, 200) ".."
let data = {
totalFee: this.data.totalPrice,
addressId: address.id,
addressDesc,
goodsCartsIds,
goodsNameDesc
}
let res = await wx.wxp.request4({
url: 'http://localhost:3000/user/my/order2',
method: 'post',
data
})
console.log(res);
let payArgs = res.data.data.params
wx.requestPayment({
timeStamp: payArgs.timeStamp,
nonceStr: payArgs.nonceStr,
package: payArgs.package,
signType: 'MD5',
paySign: payArgs.paySign,
success:async res1=> {
console.log('success', res1);
// requestPayment:ok
if (res1.errMsg == 'requestPayment:ok') {
// 微信支付成功
await wx.wxp.showModal({
title: '支付成功',
showCancel: false
})
this.removeCartsGoods(goodsCartsIds)
} else {
// {errMsg: "requestPayment:fail cancel"}
wx.showModal({
title: '支付取消或失败了,请稍后得试',
showCancel: false,
})
}
},
fail:(err1)=> {
console.log('fail', err1);
}
})
},
// 将已经下单的商品从购物车中移除
async removeCartsGoods(goodsCartsIds) {
let data = {
ids: goodsCartsIds
}
let res2 = await wx.wxp.request4({
url: 'http://localhost:3000/user/my/carts',
method: 'delete',
data
})
console.log('res2', res2);
if (res2.data.msg == 'ok') {
wx.switchTab({
url: '/pages/cart/index',
})
} else {
wx.showModal({
title: '更新购物车数据失败',
showCancel: false
})
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
const eventChannel = this.getOpenerEventChannel()
eventChannel.on('cartData', (res)=> {
// console.log(res)
this.setData({
carts:res.data
})
this.calcTotalPrice()
})
},
// 准备跳转地址列表表,选取地址
toSelectAddress(){
wx.navigateTo({
url: '/pages/address-list/index',
success:res=>{
res.eventChannel.on('selectAddress', address=>{
address.addressInfo = address.region.join('') address.detailInfo
this.setData({
address
})
})
}
})
},
// 重新计算总价
calcTotalPrice(){
let totalPrice = 0
let carts = this.data.carts
carts.forEach(item=>{
totalPrice = item.price * item.num
})
this.setData({
totalPrice
})
},
})
代码语言:javascript复制<van-cell-group>
<van-cell bind:click="toSelectAddress" is-link icon="location-o" size="large" title="{{address.userName}}" value="{{address.telNumber}}" label="{{address.addressInfo}}" />
</van-cell-group>
<view style="padding:10px;">
<block wx:for="{{carts}}" wx:key="id">
<van-card custom-class="goods-card" price="{{item.price*item.num/100}}元" desc="{{item.sku_desc}}"
title="{{item.goods_name}}" thumb="{{item.goods_image}}">
<view slot="footer">
<text>x{{item.num}}</text>
</view>
</van-card>
</block>
</view>
<van-cell-group title=" ">
<van-cell title="优惠" value="暂无可用" is-link />
</van-cell-group>
<van-cell-group title=" ">
<van-cell title="配置方式" value="快递免运费" />
<van-field model:value="{{ userMessage }}" label="买家留言" border="{{ false }}" placeholder="留言建议提前协商" />
</van-cell-group>
<van-cell-group title=" ">
<van-cell title="商品金额" value="¥900" />
<van-cell title="运费" value=" ¥200" />
<van-cell custom-class="total-price" title="" value="合计:¥900" />
</van-cell-group>
<van-submit-bar
price="{{ totalPrice }}"
button-text="提交订单"
bind:submit="onSubmit"
/>
<!-- 小微商户支付 -->
<pay wx:if="{{ prepareSubmchPay }}" params="{{ submchPayParams }}" bindsuccess="bindPaySuccess" bindfail="bindPayFail" bindcomplete="bindPayComplete" />
代码语言:javascript复制{
"navigationBarTitleText": "确认订单",
"usingComponents": {
"van-cell": "@vant/weapp/cell/index",
"van-cell-group": "@vant/weapp/cell-group/index",
"van-checkbox": "@vant/weapp/checkbox/index",
"van-checkbox-group": "@vant/weapp/checkbox-group/index",
"van-card": "@vant/weapp/card/index",
"van-stepper": "@vant/weapp/stepper/index",
"van-field": "@vant/weapp/field/index",
"van-submit-bar": "@vant/weapp/submit-bar/index",
"pay": "../../components/pay/index"
}
}
代码语言:javascript复制/* miniprogram/pages/confirm-order/index.wxss */
.goods-card{
background-color: #fefefe !important;
}
.goods-card .van-card__title{
margin-top: 10px;
}
.goods-card .van-card__img {
border-radius: 10px;
}
.goods-card-container {
display:flex;margin:10px;background:#fefefe;
}
.goods-card-container .goods-card-container{
padding-top: 10px;
}
.total-price .van-cell__value{
color: rgb(236, 176, 98);
}
page{
padding-bottom: 100px;
}
2.pay支付组件
代码语言:javascript复制// componenets/xunhupay/xunhupay.js
Component({
/**
* 组件的属性列表
*/
properties: {
params: { // 支付订单参数
type: Object,
value: null
},
envVersion: {
type: String,
value: "release"
}
},
/**
* 组件的初始数据
*/
data: {
showPayModal: false,
paying: false
},
/**
* 组件的方法列表
*/
methods: {
setPaying(newPayingData) {
this.setData({
paying: newPayingData
})
this.triggerEvent('dataChange', { paying: newPayingData })
},
onTapCancel () {
// 用户点击了支付组件外的地方(灰色地方)
console.log(' 跳转到 xunhupay 小程序失败 - 用户点击了提醒窗体以外的地方')
this.triggerEvent('fail', { navigateSuccess: false })
this.triggerEvent('complete')
},
navigateSuccess () {
console.log(' 跳转到 xunhupay 小程序成功')
// 成功跳转:标记支付中状态
this.setPaying(true)
},
navigateFail (e) {
// 跳转失败
console.log(' 跳转到 xunhupay 小程序失败 - 失败回调', e)
this.triggerEvent('fail', { navigateSuccess: false, info: e })
this.triggerEvent('complete')
}
},
/**
* 组件生命周期
*/
lifetimes: {
// 组件显示时,自动触发小程序跳转
attached() {
this.setPaying(false)
if (!this.data.params) {
console.error(' 跳转到 xunhupay 小程序失败 - 错误:没有传递跳转参数', r)
this.triggerEvent('fail', { error: true, navigateSuccess: false })
this.triggerEvent('complete')
}
// 监听 app.onShow 事件
wx.onAppShow(appOptions => {
if (!this.data.paying) return;
// 恢复支付前状态
this.setPaying(false)
if (appOptions.scene === 1038 && appOptions.referrerInfo.appId === 'wx2574b5c5ee8da56b') {
// 来源于 xunhupay 小程序返回
console.log('确认来源于 xunhupay 回调返回', appOptions)
let extraData = appOptions.referrerInfo.extraData
// if (extraData.success) { 这个地方demo中有错误,字段已经更改了,但是代码没有更新
if (extraData.paySuccess) {
this.triggerEvent('success', { navigateSuccess: true, info: extraData })
this.triggerEvent('complete')
} else {
this.triggerEvent('fail', { navigateSuccess: true, info: extraData })
this.triggerEvent('complete')
}
}
})
// 尝试直接跳转到 xunhupay 发起小程序支付
wx.navigateToMiniProgram({
appId: 'wx2574b5c5ee8da56b',
path: 'pages/cashier/cashier',
extraData: this.data.params,
envVersion: this.data.envVersion,
success: r => {
console.log('跳转到 xunhupay 小程序成功', r)
// 成功跳转:标记支付中状态
this.setPaying(true)
},
fail: e => {
// 跳转失败:弹出提示组件引导用户跳转
console.log('跳转到 xunhupay 小程序失败 - 准备弹窗提醒跳转', e)
this.setData({
showPayModal: true
})
}
})
}
}
})
代码语言:javascript复制{
"component": true,
"usingComponents": {}
}
代码语言:javascript复制<view class="bg" catchtap="onTapCancel"></view>
<view wx:if="{{ showPayModal }}" class="modal">
<view class="content">支付需要跳转到第三方平台进行支付</view>
<navigator class="button" target="miniProgram" app-id="wx2574b5c5ee8da56b" path="pages/cashier/cashier" extra-data="{{ params }}" version="{{ envVersion }}" bindsuccess="navigateSuccess" bindfail="navigateFail">确认跳转</navigator>
</view>
代码语言:javascript复制.bg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: black;
opacity: 0.5;
}
.button {
background: none;
}
.button::after {
border: none;
}
.modal {
position: fixed;
left: 10vw;
top: 30vh;
width: 80vw;
height: 20vh;
background-color: white;
border-radius: 5rpx;
text-align: center;
line-height: 10vh;
}
.modal .content {
height: 10vh;
color: #9d9d9d;
font-size: 28rpx;
}
.modal .button {
height: 10vh;
color: #3cc51f;
font-size: 36rpx;
}