uniapp前端支付篇之微信、抖音、快手、h5四个平台支付功能

 更新时间:2024年03月02日 10:33:17   作者:伴梦123  
支付功能在我们日常开发中经常会遇到,下面这篇文章主要给大家介绍了关于uniapp前端支付篇之微信、抖音、快手、h5四个平台支付功能的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

微信、快手、h5支付步骤大致相同,只有抖音是有自己的支付组件
项目同时支持多个(微信、快手、h5)平台支付,后端那边代码可以封装的

各平台支付大致流程都是相同的,总结了一下分为五个步骤

  • 点击支付
  • 创建订单
  • 生成密钥和支付所需要的参数
  • 支付成功
  • 查询订单状态

一、微信支付

1.支付按钮

<button @click="payTap">立即抢购</button>

2.支付事件

payTap() {
	let that = this
	// 这些参数后端一般要的
	let data = {
		openid: this.openId, //用户id 必需
		courseId: this.detailsObj.id, //课程id(商品id)必需
		promoterId: this.promoterShareId ? this.promoterShareId : '', // 分销员id
		couponId: this.detailsObj.receiveCouponId ? this.detailsObj.receiveCouponId : '', // 优惠卷id
	}
	// 如果一个项目里有多个平台支付,可以用传值来区分
	// #ifdef MP-WEIXIN
	data.platform = 1
	// #endif
	// #ifdef MP-KUAISHOU
	data.platform = 2
	// #endif
	//创建订单
	createWendaoOrder(data).then(res => {
		// 返回密钥
		createOrder({
			orderId: res.data.orderId, // 订单id
			openid: this.openId, // 用户id
		}).then(res1 => {
			// #ifdef MP-WEIXIN
			let twoData = res1.data
			// 微信支付api
			// 参数向后端要 要确保每个值就有
			uni.requestPayment({
				appId: twoData.appId,
				timeStamp: twoData.timeStamp,
				nonceStr: twoData.nonceStr,
				package: twoData.packageValue,
				signType: twoData.signType,
				paySign: twoData.paySign,
				success(result) {
					// 调起支付密码
					if (result.errMsg == "requestPayment:ok") {
						// 支付成功
						 uni.showLoading({
						 	title: '获取订单状态..',
						 	mask: true,
						 })
						orderSuccess({
							openid: that.openId, // 用户id
							orderId: res.data.orderId // 订单id
						}).then(res=>{
							uni.hideLoading();
							uni.showToast({
								title: '支付成功',
								icon: 'none'
							});
							// 重新请求下商品详情
							that.detailFn()
						})
					} else {
						uni.showModal({
							title: '',
							content: '支付失败',
							showCancel: false,
							icon: 'none',
							success(res) {}
						})
					}
				},
				fail(result) {
					console.log(result)
					uni.showModal({
						title: '',
						content: '支付失败',
						showCancel: false,
						icon: 'none',
						success(res) {}
					})
				},
			})
			// #endif
		}).catch(res => {
			console.log(res)
		})
	})
}

二、快手支付

1.支付按钮

<button @click="payTap">立即抢购</button>

2.支付事件

payTap() {
	let that = this
	// 这些参数后端一般要的
	let data = {
		openid: this.openId, //用户id 必需
		courseId: this.detailsObj.id, //课程id(商品id)必需
		promoterId: this.promoterShareId ? this.promoterShareId : '', // 分销员id
		couponId: this.detailsObj.receiveCouponId ? this.detailsObj.receiveCouponId : '', // 优惠卷id
	}
	// 如果一个项目里有多个平台支付,可以用传值来区分
	// #ifdef MP-WEIXIN
	data.platform = 1
	// #endif
	// #ifdef MP-KUAISHOU
	data.platform = 2
	// #endif
	//创建订单
	createWendaoOrder(data).then(res => {
		// 返回密钥
		createOrder({
			orderId: res.data.orderId, // 订单id
			openid: this.openId, // 用户id
		}).then(res1 => {
			// 后端返回的是这些数据
			// res1.data = {
				// order_no: "1231xxxxxxxxxxxxxxx450" 
				// order_info_token: "ChJrc01wUGF5LmxxxxxxxxxxxxxxxWYYeulijTrRyDdowh6Lvtp2MIm-t5nlq4s3xxxxxxxxxxxxxxxxxxxuh217_-giIIHDQ8yTqZqghjVraGC_NjxxxxxxxxxxxxxxKAUwAQ"
			// {
			// 快手支付api
			// #ifdef MP-KUAISHOU
			ks.pay({
				serviceId: '1',
				orderInfo: res1.data,
				success: function success(res2) {
					// 调起支付密码
					// 支付成功
					 uni.showLoading({
					 	title: '获取订单状态..',
					 	mask: true,
					 })
					orderSuccess({
						openid: that.openId, // 用户id
						orderId: res.data.orderId // 订单id
					}).then(res=>{
						uni.hideLoading();
						uni.showToast({
							title: '支付成功',
							icon: 'none'
						});
						// 重新请求下商品详情
						that.detailFn()
					})
				},
				fail: function fail(res) {
					uni.showToast({
						title: '支付失败',
						icon: 'none'
					})
				}
			})
			// #endif
		}).catch(res => {
			console.log(res)
		})
	})
}

三、抖音支付

抖音有自己的支付组件和自己的下单页面,所以需要创建一个专属于抖音的组件,并把需要的参数进行传递

1.创建组件

1.新建ttcomponents文件夹,创建完后在ttcomponents下面再新建DyPayButton文件夹,然后在DyPayButton下面创建四个文件,分别为index.js、index.json、index.ttml、index.ttss
2.要创建在App.vue同级

先在App.vue 写baseUrl和getPhoneNumber函数

<script>
	
	export default {
		onLaunch: function() {
		
		},
		onShow: function() {
			console.log('App Show')
		},
		onHide: function() {
			console.log('App Hide')
		},
		methods: {
			// 这个是接口基地址
			baseUrl(){
				return 'https://kspaycallback.wendao101.com/douyin'
			},
			/**
			 * desc: 获取手机号
			 * params:加密数据
			 * success:成功回调
			 * fail: 失败回调
			 */
			 // 这个是抖音下单页获取手机号调用的函数
			getPhoneNumber({
				params,
				success,
				fail
			}) {
				const {
					iv,
					encryptedData
				} = params;
				miniLogin().then(data=>{
					getTtOpen(data).then(data1 => {
						// 获取手机号函数
						savePhone({
							openid: data1.data.data.openId,
							iv,
							encryptedData
						}).then(data4 => {
							miniLogin().then(data6=>{
								getTtOpen(data6).then(data5 => {
									const result = {
										phoneNumber: data5.data.data.telNumber,
									}
									// 回调前端模板
									success(result)
								})
							})
						})
					})
				})
			},

		},
	}
</script>

<style lang="scss">
	@import "@/uni_modules/uview-ui/index.scss";

	@import '@/utlis/index.scss';

	/*每个页面公共css */
</style>

下面是那四个文件的内容

index.js

// 可以调用到app.vue里的方法
const app = getApp();

Component({


	properties: {
		mode: Number,
		openId: {
			type: [String, Number],
		},
		orderStatus:{
			type: [String, Number],
		},
		detailsObj: {
			type: Object,
		},
		goodsId: {
			type: String,
			value: "",
		},
		promoterShareId: Number,
	},
	data: {

	},

	methods: {
		// 提交商品信息 这个函数一进页面就会调用的
		getGoodsInfo(event) {
			const that = this
			return new Promise(resolve => {
				// 定时器是为解决 优惠卷id获取不到问题
				setTimeout(()=>{
					tt.getSystemInfo({
						success: (resPlatform)=> {
							
							let pay = that.data.detailsObj.price * 100 // 价格单位是分
							let promoterShareId = that.data.promoterShareId // 分销员id
							let required = that.data.detailsObj.giveEntityIsNeedPhone // 是否强制获取手机号
							let CouponId = that.data.detailsObj.receiveCouponId // 优惠卷id
							// 用不到的值就不用传
							let data = {
								currentPrice: pay,
								GoodsLabel: [{
										type: 'NON_REFUNDABLE'
									}
								],
								minLimits: 1,
								maxLimits: 1,
								dateRule: '周一至周日可用',
								extra: {
									promoterId: promoterShareId,
									receiveCouponId: CouponId
								},
								validation: {
									phoneNumber: {
										required: required // 手机号是否必填
									}
								},
								marketingVersion: 1,
							}
							// im客服需要提前开通
							// 判断如果用户手机是ios就走客服支付, 把imId传上就自动跳转im客服页面了,安卓不要传这个id,否则可能会导致支付不了
							if(resPlatform.platform == 'ios'){
								data.imId = '3xxxxxxxx42'
							}	
							// 然后将商品信息传入 resolve 函数
							resolve(data);
						}
					});
				},600)
			})
		},
		onError(e) {
			const {
				errNo,
				errMsg
			} = e.detail;
			if (errNo === 21514) {
				tt.showToast({
					title: "失败", // 内容
					icon: "none", // 图标
				});
			} else if (errNo === 21513) {
				tt.showToast({
					title: "获取中", // 内容
					icon: "none", // 图标
				});
			}
		},
		
		userLogin(event) {
			const {
				goodsId,
				goodsType
			} = event.detail
			return new Promise((resolve, reject) => {
				tt.login({
					success(e) {
						// 用户登录成功并获取信息,则调用 resolve 函数,跳转至提单页
						resolve();
					},
					fail() {
						// 用户登录失败,则跳转提单页失败
						_this.showTost("登录失败")
					}
				});
			});
		},
		payError(event) {
			this.showTost(event.errMsg)
		},
		// 继续支付
		handleContinutePay(event) {
		    const { status, outOrderNo, result } = event.detail;
		    if (status === 'success') {
		        const { code } = result;
		        if (code === 0) {
		            // 继续支付成功
		            // 刷新页面
					this.triggerEvent("refreshData")
					tt.showToast({
						title: "支付成功",
					});
		        }
		    } else {
		        // 继续支付失败
				tt.showToast({
					title: "继续支付失败",
					icon: "none"
				});
		    }
		},
		// 正式支付
		newButtonPay(event) {
			const {
				status,
				orderId,
				outOrderNo,
				result
			} = event.detail;
			if (status === 'success') {
				const {
					code
				} = result;
				if (code === 0) {
					tt.showLoading({
						title: "订单确认中...",
					}); 
					this.getOrderIsHaveData(outOrderNo)
				} else {
					// 支付失败(超时、取消、关闭)
					this.showTost('支付失败(超时、取消、关闭)')
				}
			} else {
				const {
					errMsg
				} = result;
				this.showTost(errMsg)
			}
		},
		showTost(tit, timeMs) {
			let time = timeMs > 0 ? timeMs : 1500;
			tt.showToast({
				title: tit,
				icon: "none",
				duration: time,
			});
		},
		// 重新订单
		getOrderIsHaveData(orderId) {
			let data = {
				openid: this.data.openId,
				orderId,
			}
			tt.request({
				url: app.baseUrl() + "/order/order_success",
				method: 'POST',
				data,
				success: (res) => {
					this.setOrderIsHaveData(res.data.orderStatus, orderId)
				}
			})
		},
		setOrderIsHaveData(data, orderId) {
			if (data == 0) {
				setTimeout(() => {
					_this.getOrderIsHaveData(orderId)
				}, 1000);
			} else {
				tt.hideLoading();
				tt.navigateBack(-1);
				this.triggerEvent("refreshData")
			}
		},

		// 退款
		onApplyrefund(event) {
			console.log(event)

			const {
				orderId
			} = event.detail;
			const extra = {
				orderId
			}; // 开发者需要透传的参数,可自定义内容
			return new Promise(resolve => {
				resolve(extra);
			});
		},
		onRefund(event) {
			console.log(event)
			const {
				status,
				result
			} = event.detail;
			if (status === 'success') {
				const {
					refundId,
					outRefundNo
				} = result;
			} else {
				const {
					errMsg
				} = result;
				tt.showToast({
					title: e.detail.errMsg ? e.detail.errMsg : '失败',
					icon: "none"
				});
			}
		},
		refundError(e) {
			console.log(e)
			if (e.detail.errNo == 21531) {
				tt.showToast({
					title: "不符合退款要求",
					icon: "none"
				});
			} else {
				tt.showToast({
					title: e.detail.errMsg ? e.detail.errMsg : '失败',
					icon: "none"
				});
			}
		},
	},
});

index.json

{
  "component": true,
  "usingComponents": {}
}

index.ttml

<!-- 立即抢购 -->
<block tt:if="{{mode==2}}">  
	<pay-button class="{{classsname}}" mode="{{2}}" goods-id="{{detailsObj.productId}}" goods-type="{{1}}"
		biz-line="{{2}}" bind:getgoodsinfo="getGoodsInfo" bind:placeorder="userLogin" marketing-ready="{{true}}"
		bind:pay="newButtonPay" bind:error="onError"></pay-button>
</block>

<block tt:if="{{mode==1}}">
	<!-- 继续支付 -->
	<pay-button tt:if="{{orderStatus==0}}" class="{{classsname}}" order-status="{{0}}" order-id="{{orderData.orderId}}"
		bind:pay="handleContinutePay"></pay-button>

	<!-- 退款 -->
	<pay-button class="order_ljzf" mode="{{1}}" goods-type="{{1}}" order-status="{{1}}" order-id="{{orderData.orderId}}"
		:refund-total-amount="{{orderData.coursePrice}}" biz-line="{{2}}" marketing-ready="{{true}}"
		catch:applyrefund="onApplyrefund" catch:refund="onRefund" catch:error="refundError"
		tt:if="{{orderData.orderStatus==1}}"></pay-button>

	<!-- 退款状态 -->
	<pay-button class="order_tk" mode="{{1}}" goods-type="{{1}}"
		order-status="{{orderData.orderStatus=='4'?2:orderData.orderStatus=='2'?3:orderData.orderStatus=='5'?4:4 }}"
		refund-id="{{orderData.orderId}}" biz-line="{{2}}" marketing-ready="{{true}}" catch:applyrefund="onApplyrefund"
		catch:refund="onRefund" catch:error="refundError"
		tt:if="{{orderData.orderStatus!=1 && orderData.orderStatus!=0}}"></pay-button>
</block>

index.ttss

按钮样式根据自己的来

.save_one {
	width: 100%;
	height: 100%;
}

.payButton {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 310rpx;
	height: 90rpx;
	border-radius: 45rpx;
	box-sizing: border-box;
	background-color: #E10000;
	color: #fff;
	border: 2rpx solid #E10000;
}

.payButtonItem {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 183rpx;
	height: 57rpx;
	background: #E10000;
	border-radius: 29rpx;
	border: 1rpx solid #E10000;
	font-size: 26rpx;
	font-family: "Noto Sans SC";
	font-weight: 600;
	color: #fff;
	line-height: 37rpx;
	box-sizing: border-box;
	margin-right: 16rpx;
}

2.在pages.json给商品详情页面注册组件

"pages": [{
	"path": "details/details",
	"style": {
		// #ifdef MP-TOUTIAO
		// 这个是需要加的,否则显示不出来
		"usingComponents": {
			"zijie-pay-button": "/ttcomponents/DyPayButton/index"
		},
		//#endif
		"navigationBarTitleText": "xxxx",
		"enablePullDownRefresh": false
	}
}]

3.在商品详情页面中使用组件

components:{
	// #ifdef MP-TOUTIAO
	"zijie-pay-button": "../../ttcomponents/DyPayButton/index",
	//#endif
}
<zijie-pay-button 
	v-if="detailsObj.productId"
	:openId="openId"
	:mode='2' 
	:detailsObj="detailsObj" 
	:classsname="'save_one'"
	:promoterShareId="promoterShareId"
	@refreshData="refreshData"
></zijie-pay-button>
>
// v-if: 判断有没有商品id
// openId: 用户id
// mode: 商品类型
// detailsObj: 商品详情页的数据
// classsname: 按钮类名
// promoterShareId: 分销员id
// @refreshData: 用于支付成功回调刷新页面

4.h5支付

主要是为解决微信ios无法支付的问题,ios走h5支付渠道

1.创建按钮

// 这个按钮是uView组件
<u-button 
text="联系老师" 
v-if="isIosDouYin" 
shape="circle" 
color="#E10000"
:send-message-title="detailsObj.title?detailsObj.title:''"
:send-message-img="detailsObj.coverPicUrl"
:send-message-path="'/pages_details/details/details?courseId=' + detailsObj.id + '&promoterId=' + promoterShareId + '&couponId=' + detailsObj.receiveCouponId + '&appNameType=1&platform=1&openid=' + openId"
show-message-card="true" 
open-type="contact"></u-button>

// isIosDouYin: 判断是否为ios系统
// send-message-title: 商品标题
// :send-message-img: 商品封面
// :send-message-path: 跳转路径
// show-message-card: 发送卡片

2.创建页面

在详情页同级目录创建一个页面detailsContact.vue,后端可以重定向到这个页面

var _this;
export default {
		data() {
			return {
				openid: '', // 用户id
				h5PayData:{}, // h5提交的参数
				h5OrderId: '' // 订单id
			}
		},
		onLoad(options) {
			_this = this
			// 页面一进来就调用支付 带上路径传的值
			this.payTap(options)
		},
		methods:{
			payTap(queryObj) {
				let that = this
				that.openid = queryObj.openid
				let data = {
					openid: queryObj.openid, // 用户id
					courseId: queryObj.courseId, // 课程id
					promoterId: queryObj.promoterId ? queryObj.promoterId : 0, // 推广员id
					couponId: queryObj.couponId ? queryObj.couponId : 0, // 优惠卷id
					appNameType: queryObj.appNameType, // 区分哪个小程序
					platform : queryObj.platform // 区分平台
				}
				
				createWendaoOrder1(data).then(res => {
					createOrder1({
						orderId: res.data.orderId,
						openid: queryObj.openid,
					}).then(res1 => {
						// #ifdef H5
						that.h5PayData = res1.data
						that.h5OrderId = res.data.orderId
						that.getWxOfficePay()
						// #endif
					}).catch(res => {
						console.log(res)
					})
				})
				
			},
			getWxOfficePay() {
				if (typeof WeixinJSBridge == "undefined") {
					if (document.addEventListener) {
						document.addEventListener('WeixinJSBridgeReady', _this.getWxOfficePayPage, false);
					} else if (document.attachEvent) {
						document.attachEvent('WeixinJSBridgeReady', _this.getWxOfficePayPage);
						document.attachEvent('onWeixinJSBridgeReady', _this.getWxOfficePayPage);
					}
				} else {
					_this.getWxOfficePayPage()
				}

			},
			getWxOfficePayPage() {
				const that = this
				console.log("h5支付调起支付面板 ")
				WeixinJSBridge.invoke(
					'getBrandWCPayRequest', {
						appId: _this.h5PayData.appId,
						timeStamp: _this.h5PayData.timeStamp,
						nonceStr: _this.h5PayData.nonceStr,
						package: _this.h5PayData.packageValue,
						signType: _this.h5PayData.signType,
						paySign: _this.h5PayData.paySign,
					},
					function(res) {
						if (res.err_msg == "get_brand_wcpay_request:ok") {
							uni.showLoading({
								title: '获取订单状态..',
								mask: true,
							})
							// 确认订单
							uni.request({
								url: app.baseUrl() + "/order/order_success",
								method: 'POST',
								data: {
									openid: that.openId,
									orderId: that.h5OrderId
								},
								success: (res) => {
									uni.hideLoading();
									uni.showModal({
										title: '',
										content: '支付成功,请返回小程序查看课程',
										showCancel: false,
										icon: 'none'
									})
								}
							})
						} else {
							uni.showModal({
								title: '',
								content: '支付失败',
								showCancel: false,
								icon: 'none',
								success(res) {}
							})
						}
					}
				);
			},
		}
	}

总结 

到此这篇关于uniapp前端支付篇之微信、抖音、快手、h5四个平台支付功能的文章就介绍到这了,更多相关uniapp前端微信、抖音、快手、h5支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue 如何配置eslint代码检查

    vue 如何配置eslint代码检查

    这篇文章主要介绍了vue 如何配置eslint代码检查,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue中Promise的使用方法详情

    vue中Promise的使用方法详情

    这篇文章主要介绍了vue中Promise的使用方法详情,Promise可以说是异步编程的一种解决方法,主要是为了解决代码乱的情景而出现的,下文介绍其具体用法,需要的小伙伴可以参考一下
    2022-03-03
  • 基于electron+vue3+ts搭建桌面端应用并且可以热更新

    基于electron+vue3+ts搭建桌面端应用并且可以热更新

    这篇文章主要为大家详细介绍了如何基于electron+vue3+ts搭建桌面端应用并且可以热更新,文中的示例代码讲解详细,感兴趣的小伙伴可以参考下
    2023-10-10
  • vue实现表格动态嵌入折线图的绘制代码

    vue实现表格动态嵌入折线图的绘制代码

    这篇文章给大家介绍了vue实现表格动态嵌入折线图的绘制方法,文中有详细完整的代码示例攻大家参考,对大家的学习或工作有一定的参考价值,需要的朋友可以参考下
    2023-10-10
  • vue使用canvas手写输入识别中文

    vue使用canvas手写输入识别中文

    这篇文章主要介绍了vue使用canvas手写输入识别中文,工作时遇到一些项目如:系统上的输入法使用不方便,客户要求做一个嵌入web网页的手写输入法。下面我们来看看文章得具体描述吧
    2021-11-11
  • Vue组件内部实现一个双向数据绑定的实例代码

    Vue组件内部实现一个双向数据绑定的实例代码

    这篇文章主要介绍了Vue组件内部实现一个双向数据绑定的实例代码,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-04-04
  • vue 2.x 中axios 封装的get 和post方法

    vue 2.x 中axios 封装的get 和post方法

    本文通过实例代码给大家介绍了vue 2.x 中axios 封装的get 和post方法,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2018-02-02
  • 如何使用vite搭建vue3项目详解

    如何使用vite搭建vue3项目详解

    Vite 是一个面向现代浏览器的更轻,更快的web应用开发工具,下面这篇文章主要给大家介绍了关于如何使用vite搭建vue3项目的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • element el-tree组件的动态加载、新增、更新节点的实现

    element el-tree组件的动态加载、新增、更新节点的实现

    这篇文章主要介绍了element el-tree组件的动态加载、新增、更新节点的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • vue3自定义组件之v-model实现父子组件双向绑定

    vue3自定义组件之v-model实现父子组件双向绑定

    这篇文章主要介绍了vue3自定义组件之v-model实现父子组件双向绑定方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08

最新评论