搜索
您的当前位置:首页正文

使用UniApp实现多平台支付集成:小程序、Apple IAP、App端微信与支付宝支付,H5沙箱支付【超详解】

来源:步旅网

uniapp各平台支付功能

目录

APP端支付参考资料

申请开通各平台APP支付

在使用支付前,需要向各支付平台申请开通支付功能【要有营业执照才可以开通】。

Apple应用内支付(IAP)

因为苹果应用内置流程与其他三方支付平台存在差异,需要单独参考ios平台使用。

注意事项:

  • 要成为Apple 开发者人员,需要向Apple支付开发人员年费。【 https://developer.apple.com】,【https://developer.apple.com/cn】
  • iOS平台苹果审核规范要求,应用中虚拟物品交易必需使用Apple应用内支付,实物交易才能使用第三方支付(支付宝和微信支付)
    HBuilder 里的调试基座默认不带IAP支付通道,如果需要调试IAP需要使用开发证书生成一个自定义调试基座,用来实现IAP的开发和调试。 自定义调试基座使用方法。

支付宝支付

登录支付宝账号,创建应用接入支付宝APP支付如下步骤【前提需要营业执照】。

  • 步骤一、 创建应用,登录,创建应用并提交审核,审核通过后会生成应用唯一标识,APPID,并且可以申请开通开放产品使用权限,通过AppID应用才能调用开放产品的接口能力。

  • 步骤二、开通App支付功能,应用创建完成后,系统会自动跳转到应用详情页面。开发者可以点击 添加能力 来添加 App 支付能力。添加功能后开发者需要在商家中心中进行 签约,第三方应用开发者可以 代商户签约。

    得到沙箱测试账号

步骤三、 配置密钥(获取公钥、私钥)
为了保证交易双方(商户和支付宝)的身份和数据安全,商户在调用接口前,需要配置双方密钥,对交易数据进行双方校验。密钥包含应用私钥APP_PRIVATE_KEY和应用公钥 APP_PUBLIC_KEY。生成密钥后,商户需要在开放平台控制台进行密钥配,配置完成后可以获取支付宝公钥 ALIPAY_PUBLIC_KEY,配置的详细步骤请参考 配置应用环境。您还可以通过 生成密钥并上传 学习密钥的配置。密钥的配置旨在对交易数据进行双方校验。

微信支付

步骤一、创建应用

服务端生成支付订单接口

在App端调用支付功能时,需要先在调用服务器上生成预支付订单接口,再将预支付订单信息传递给支付接口。

支付宝业务流程图

客户端请求后台服务端获取签名后的订单信息,客户端带上订单信息(字符串)请求支付接口。

微信支付业务流程图

客户端请求后台服务端获取代签名的支付订单信息,然后客户端带上订单信息(Object类型)请求支付接口,其中订单信息请求请参考官网文档

HBuilderX中配置使用支付功能

从微信开放平台申请获取配置参数后(Apple应用内支付和支付宝无需配置),需要再HBuilderX中配置并提交云端打包才能生效。

勾选后会显示支持的支付模块,可根据应用需要进行选择配置

Apple应用内支付【仅支持IOS平台】

支付宝支付【支持Android及iOS平台】

微信支付

获取appid填入,去微信开放平台申请应用的AppId值,UniversalLinks:iOS平台通用链接,必须与微信开放平台配置的一致,参考iOS平台微信SDK配置通用链接(UniversalLinks)

功能实现

APP端支付宝/微信支付实现

发送请求到后台服务端,后台服务端生成订单信息,相应预支付订单信息,通过预支付订单信息请求支付接口。

import api from '@/api/order.js'
export default {
	methods: {
		// 获取订单信息 (微信小程序支付需要openid)
		getOrderInfo(openid) {
			// 不要少了async
			return new Promise( async (resolve, reject) => {
				let res = null
				let data = {} // 服务接口请求参数
				if(this.provider === 'alipay') {
					res = await api.getOrderInfoAlipay(data)
				}else if(this.provider === 'wxpay') {
					res = await api.getOrderInfoWxpay(data)
				}
				if (res && res.code === 20000) {
					resolve(res.data)
				} else {
					reject(new Error('获取支付信息失败' + res.message))
				}
			})
		},
	// 微信、支付宝等支付
	async payHandler() {
		// 支付中
		this.loading = true
		// #ifdef APP-PLUS
		// 1. 发送请求到服务端,服务端生成订单信息,响应预支付订单
		let orderInfo = await this.getOrderInfo();
		if (!orderInfo) {
			uni.showModal({
				content: '获取支付信息失败',
				showCancel: false
			})
			return
		}
		// 2. 请求支付
		uni.requestPayment({
			provider: this.provider, // 支付渠道
			orderInfo: orderInfo,
			success: (e) => {
				console.log("success", e);
				uni.showToast({
					title: "支付成功!"
				})
			},
			fail: (e) => {
				console.log("fail", e);
				uni.showModal({
					content: "支付失败!",
					showCancel: false
				})
			},
			complete: () => {
				this.loading = false;
			}
		})
		// #endif
	},
}

微信小程序支付功能实现

微信小程序运行在微信里面,需要支付的话,首先要知道是谁的微信里面,就需要将当前微信进行登录获取获取code,再根据code获取openid的值。
步骤一、先登录微信小程序获取用户code,再请求服务端获取openid,:

// 登录微信小程序
loginWeixinMp() {
		return new Promise((resolve, reject) => {
			// 1. 先使用微信登录小程序响应 code,
			uni.login({
				provider: 'weixin',
				success: (res) => {
					console.log('登录', res)
					const code = res.code
					// 2. 请求服务端通过code获取openid
					let openid = 'xx'
					uni.setStorageSync('openid', openid)
					resolve(openid)
				},
				fail(err) {
					reject(err)
				}
			})
		})
},

步骤二、通过openid再获取订单预支付信息,直接在getOrderInfo写死一个模拟预支付信息即可。


getOrderInfo(openid) {
	return new Promise(async (resolve, reject) => {
		if (openid) {
			// 微信小程序获取订单信息,发送请求 
			// let orderInfo = await api.getOrderInfoWxpayMP(openid) 如果有真实接口替换即可
			let orderInfo = {
				"timeStamp": "1414561699",
				"nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
				"package": "prepay_id=wx201410272009395522657a690389285100", //预支付交易会话标识( prepay_id) 
				"signType": "RSA",
				"paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ",
			}
			resolve(orderInfo)
			return
		}
		let res = null
		let data = {} // 服务接口请求参数
		if (this.provider === 'alipay') {
			res = await api.getOrderInfoAlipay(data)
		} else if (this.provider === 'wxpay') {
			res = await api.getOrderInfoWxpay(data)
		}
		if (res && res.code === 20000) {
			resolve(res.data)
		} else {
			reject(new Error('获取支付信息失败' + res.message))
		}
	})
},

步骤三、通过订单预支付信息,去调用支付接口

// 微信小程序支付
async wxPayHandler() {
	this.loading = true
	// 1. 先获取用户code, 再获取openid
	let openid = uni.getStorageSync('openid')
	if (!openid) {
		try {
			openid = await this.loginWeixinMp()
		} catch (e) {
			console.error(e)
		}
		if (!openid) {
			uni.showModal({
				content: '获取openid失败',
				showCancel: false
			})
			this.loading = false
			return
		}
	}
	// 2. 通过 openid 再获取订单信息,
	let orderInfo = await this.getOrderInfo(openid)
	// 3. 通过订单预支付信息,去调用支付接口
	uni.requestPayment({
		...orderInfo,
		success: (res) => {
			uni.showToast({
				title: "支付成功!"
			})
		},
		fail: (res) => {
			uni.showModal({
				content: "支付失败,原因为: " + res.errMsg,
				showCancel: false
			})
		},
		complete: () => {
			this.loading = false;
		}
	})
}

因为用的是模拟的假数据,会报权限不足问题,如果真实开发中调用后端请求服务拿到商品信息,先登录微信小程序获取用户code,再请求服务端获取openid,通过openid再获取订单预支付信息,直接在getOrderInfo写死一个模拟预支付信息即可,通过订单预支付信息,去调用支付接口即可。

Apple应用内支付

苹果应用内置流程与其它三方支付平台存在差异,请单独参考iOS 平台使用,项目中参考支付文档最下面
流程:

在onLoad中先获取Apple支付渠道的实例。

onLoad: function(option) {
	// 获取支付金额
	if (option.params) {
		const params = JSON.parse(option.params)
		this.price = params.price
		this.courseIds = params.courseIds
	}
	// 查询余额
	this.loadData()
	// 获取apple支付渠道实例
	plus.payment.getChannels((channels) => {
		console.log("获取到channel" + JSON.stringify(channels))
		for (var i in channels) {
			var channel = channels[i];
			if (channel.id === 'appleiap') {
				iapChannel = channel;
				this.requestOrder();
			}
		}
		if (!iapChannel) {
			this.errorMsg()
		}
	}, (error) => {
		this.errorMsg()
	});
},

requestOrder环境检测方法


			requestOrder() {
				uni.showLoading({
					title: '检测支付环境...'
				})
				//必须调用此方法向Appstore请求有效的商品详情,才能进行 iap 支付,
				iapChannel.requestOrder(this.moneyList, (orderList) => {
					this.disabled = false;
					console.log('requestOrder success666: ' + JSON.stringify(orderList));
					uni.hideLoading();
				}, (e) => {
					console.log('requestOrder failed: ' + JSON.stringify(e));
					uni.hideLoading();
					this.errorMsg()
				});
			},

弹窗提示判断,如果环境不支持apple支付则弹窗提醒

errorMsg() {
	uni.showModal({
		content: "暂不支持苹果 iap 支付",
		showCancel: false
	})
},

如果当前环境可以支付,则使用uni.requestPayment进行apple支

// Apple应用内支付 
iosPayHandler() {
	this.loading = true;
	uni.requestPayment({
		provider: 'appleiap',
		orderInfo: {
			productid: this.applePrice // 商品金额
		},
		success: (e) => {
			console.log("success", e);
			uni.showToast({
				title: "支付成功!"
			})
			// 再进行app内部扣款
		},
		fail: (e) => {
			console.log("fail", e);
			uni.showModal({
				content: "支付失败,原因为: " + e.errMsg,
				showCancel: false
			})
		},
		complete: () => {
			this.loading = false;
		}
	})
},

总体代码:

<template>
	<view>
		<view class="money column center">
			<text>余额:</text>
			<text>{{parseFloat(balance).toFixed(2)}}</text>
		</view>
		<view class="recharge">
			<view>充值</view>
			<view class="list">
				<view v-for="(item, index) in 6" :key="index" :class="{active: activeIndex===index}"
					@click="clickItem(index, item)">
					<view>{{index+1}}00</view>
					<view>{{index+1}}00</view>
				</view>
			</view>
		</view>
		<view class="desc">
			<view>充值说明:</view>
			<view>
				1.IOS设备的APP要进行充值后,使用虚拟币消费。<br>
				2.充值后不能在非IOS设备使用,与安卓版和网站余额不通用。<br>
				3.充值后没有使用期限,但不可提现、退换、转让和申请发票。<br>
				4.如遇无法充值、充值失败等问题,可关注[梦学谷]公众号,联系我们解决。<br>
			</view>
		</view>
		<view class="bottom center">
			<button class="btn" :loading="loading" :disabled="disabled" @click="iosPayHandler">立即充值</button>
		</view>
	</view>
</template>
<script>
	import api from '@/api/order.js'
	let iapChannel = null // 苹果内部支付渠道
	export default {
		data() {
			return {
				activeIndex: 0,
				loading: false, // 是否充值中
				disabled: true, //要先检查支付环境是否iap,不支付则点击立即支付无效
				balance: 0, // 余额
				moneyList: [], // 充值列表展示金额
				price: 0, // 支付金额
				courseIds: [], // 支付的课程ids
				applePrice: 30, // ios充值金额
			}
		},
		onLoad: function(option) {
			// 获取支付金额
			if (option.params) {
				const params = JSON.parse(option.params)
				this.price = params.price
				this.courseIds = params.courseIds
			}
			// 查询余额
			this.loadData()
			// 获取apple支付渠道实例
			plus.payment.getChannels((channels) => {
				console.log("获取到channel" + JSON.stringify(channels))
				for (var i in channels) {
					var channel = channels[i];
					if (channel.id === 'appleiap') {
						iapChannel = channel;
						this.requestOrder();
					}
				}
				if (!iapChannel) {
					this.errorMsg()
				}
			}, (error) => {
				this.errorMsg()
			});
		},
		methods: {
			requestOrder() {
				uni.showLoading({
					title: '检测支付环境...'
				})
				//必须调用此方法向Appstore请求有效的商品详情,才能进行 iap 支付,
				iapChannel.requestOrder(this.moneyList, (orderList) => {
					this.disabled = false;
					console.log('requestOrder success666: ' + JSON.stringify(orderList));
					uni.hideLoading();
				}, (e) => {
					console.log('requestOrder failed: ' + JSON.stringify(e));
					uni.hideLoading();
					this.errorMsg()
				});
			},
			iosPayHandler() {
				this.loading = true;
				uni.requestPayment({
					provider: 'appleiap',
					orderInfo: {
						productid: this.applePrice // 商品id
					},
					success: (e) => {
						console.log("success", e);
						uni.showToast({
							title: "支付成功!"
						})
						// 调用接口,立即扣款购买商品
					},
					fail: (e) => {
						console.log("fail", e);
						uni.showModal({
							content: "支付失败,原因为: " + e.errMsg,
							showCancel: false
						})
					},
					complete: () => {
						this.loading = false;
					}
				})
			},
			errorMsg() {
				uni.showModal({
					content: "暂不支持苹果 iap 支付",
					showCancel: false
				})
			},
			clickItem(index, item) {
				this.activeIndex = index
				this.applePrice = item
			},
			async loadData() {
				// 查询余额
				const {
					data
				} = await api.getUserBalance()
				this.balance = data
				// 要进行支付,则计算还差多少金额, 则充值多少,
				if (this.price) {
					// ios充值多少 = 余额-付款金额 < 0 : 余额不够:充足 const applePrice=this.balance - this.price // console.log('applePrice', applePrice)
					// 取正数,向上取整 this.applePrice=Math.ceil(Math.abs(applePrice)) } // 充值列表展示金额 for(let i=0; i<6; i++) { // 6个元素,每个加30元
					this.moneyList.push(this.applePrice + i * 30)
				}
			},

		}
	}
</script>

<style lang="scss">
	.money {
		height: 288rpx;
		background-color: $mxg-color-primary;
		color: #FFFFFF;
		font-size: 88rpx;

		text:first-child {
			color: #e7e4e9;
			font-size: 30rpx;
		}
	}

	.recharge {
		margin: 20rpx 0 0 20rpx;
		color: #999;
		font-size: 30rpx;

		.list {
			margin-top: 20rpx;
			text-align: center;

			>view {
				float: left;
				width: 225rpx;
				margin-right: 10rpx;
				margin-bottom: 15rpx;
				background-color: #fff;
				border-radius: 10rpx;
				padding: 20rpx 0;
				border: 1px solid $mxg-color-grey;
				flex-wrap: wrap;

				view:first-child {
					color: $mxg-text-color-red;
					font-size: 36rpx;
				}
			}
		}
	}

	.active {
		box-shadow: 0 0 0 .5px $mxg-text-color-red;
	}

	.desc {
		// 清除浮动
		clear: both;
		margin: 0 20rpx;
		font-size: 30rpx;
		line-height: 45rpx;
		color: #6e6d70;

		view:first-child {
			padding-top: 50rpx;
			padding-bottom: 30rpx;
			font-weight: bold;
		}

		view:last-child {
			padding-bottom: 120rpx;
		}
	}

	/* 底部 */
	.bottom {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		height: 100rpx;
		background-color: #FFFFFF;
		border-top: 1px solid #F1F1F1;
	}

	.btn {
		width: 700rpx;
		background-color: $mxg-color-primary;
		color: #fff;
		border-radius: 50rpx;
		line-height: 80rpx;
		font-size: 30rpx;

		&::after {
			// 加载中时,隐藏边框
			border: none;
		}
	}
</style>

效果:由于我用的是mock数据,说一检测环境就会失败判断,到了生产环境就会弹出apple应用支付弹窗。

Appstore审核报PGPay SDK不允许上架的问题
A:数字类产品(比如购买会员等不需要配送实物的商品),Apple规定必须使用苹果IAP应用内支付,给Apple分成30%。打包的时候不要勾选微信或支付宝等其他支付方式。如果你提交的包里包含了微信支付宝等支付的sdk,即使没使用,Appstore也会认为你有隐藏方式,以后会绕过IAP,不给Apple分成,因此拒绝你的App上线。云打包时,manifest里选上支付模块,但sdk配置里去掉微信支付和支付宝支付。很多开发者的Android版是包含微信和支付宝支付的,此时注意分开判断。

H5沙箱支付功能实现实现

配置沙箱

沙箱支持产品

沙箱测试账号

带着商品信息请求后端接口,获取paymentUrl然后通过window.location.href = res.paymentUrl进行跳转


通过location.href跳转到如下页面即可使用沙箱测试账号进行登录。下图为支付宝登录页面,可能会违规。
注意: 沙箱测试支付功能一定要开启无痕浏览进行测试

使用支付宝开放平台给的沙箱测试账号进行测试:如下找到
输入沙箱账号密码


支付成功

交易成功后,此页面会跳转到前端传递给后端的配置的跳转页面,支付成功后就会跳回到指定页面。

配置的支付成功后的跳转页面,在此页面调用后端接口,后端返回支付状态,根据支付状态再在此页面进行其他业务逻辑处理。

H5支付第二种方法

微信支付:
支付宝:

  • 普通浏览器平台的支付,仍然是常规web做法。uni-app未封装。
  • 在普通浏览器里也可以调起微信进行支付,这个在微信叫做H5支付,此功能未开放给普通开发者,需向微信 单独申请,
  • 微信内嵌浏览器运行H5版时,可通过js sdk实现微信支付,需要引入一个单独的js,

完结~,如有不足,后继补充。。。。

因篇幅问题不能全部显示,请点此查看更多更全内容

Top