uniapp前端支付篇(微信、抖音、快手、h5)四个平台支付

uniapp前端支付篇(微信、抖音、快手、h5)四个平台支付

前言

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

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

  1. 点击支付
  2. 创建订单
  3. 生成密钥和支付所需要的参数
  4. 支付成功
  5. 查询订单状态

一、微信支付

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,
				su***ess(result) {
					// 调起支付密码
					if (result.errMsg == "requestPayment:ok") {
						// 支付成功
						 uni.showLoading({
						 	title: '获取订单状态..',
						 	mask: true,
						 })
						orderSu***ess({
							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',
							su***ess(res) {}
						})
					}
				},
				fail(result) {
					console.log(result)
					uni.showModal({
						title: '',
						content: '支付失败',
						showCancel: false,
						icon: 'none',
						su***ess(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,
				su***ess: function su***ess(res2) {
					// 调起支付密码
					// 支付成功
					 uni.showLoading({
					 	title: '获取订单状态..',
					 	mask: true,
					 })
					orderSu***ess({
						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.新建tt***ponents文件夹,创建完后在tt***ponents下面再新建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.***/douyin'
			},
			/**
			 * desc: 获取手机号
			 * params:加密数据
			 * su***ess:成功回调
			 * fail: 失败回调
			 */
			 // 这个是抖音下单页获取手机号调用的函数
			getPhoneNumber({
				params,
				su***ess,
				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,
									}
									// 回调前端模板
									su***ess(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();

***ponent({


	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({
						su***ess: (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({
					su***ess(e) {
						// 用户登录成功并获取信息,则调用 resolve 函数,跳转至提单页
						resolve();
					},
					fail() {
						// 用户登录失败,则跳转提单页失败
						_this.showTost("登录失败")
					}
				});
			});
		},
		payError(event) {
			this.showTost(event.errMsg)
		},
		// 继续支付
		handleContinutePay(event) {
		    const { status, outOrderNo, result } = event.detail;
		    if (status === 'su***ess') {
		        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 === 'su***ess') {
				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_su***ess",
				method: 'POST',
				data,
				su***ess: (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 === 'su***ess') {
				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

{
  "***ponent": true,
  "using***ponents": {}
}

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
		// 这个是需要加的,否则显示不出来
		"using***ponents": {
			"zijie-pay-button": "/tt***ponents/DyPayButton/index"
		},
		//#endif
		"navigationBarTitleText": "xxxx",
		"enablePullDownRefresh": false
	}
}]

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

***ponents:{
	// #ifdef MP-TOUTIAO
	"zijie-pay-button": "../../tt***ponents/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_su***ess",
								method: 'POST',
								data: {
									openid: that.openId,
									orderId: that.h5OrderId
								},
								su***ess: (res) => {
									uni.hideLoading();
									uni.showModal({
										title: '',
										content: '支付成功,请返回小程序查看课程',
										showCancel: false,
										icon: 'none'
									})
								}
							})
						} else {
							uni.showModal({
								title: '',
								content: '支付失败',
								showCancel: false,
								icon: 'none',
								su***ess(res) {}
							})
						}
					}
				);
			},
		}
	}
转载请说明出处内容投诉
CSS教程_站长资源网 » uniapp前端支付篇(微信、抖音、快手、h5)四个平台支付

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买