uni-app app跳转小程序,在小程序进行微信支付,支付成功小程序再返回APP

1.APP跳转小程序

bash 复制代码
goWX(payConfirmList) {
	//小程序跳转路径 希携带参数
	const miniProgramPath = 'payPages/pages/pay/payConfirm';
	const path =miniProgramPath +'?payList=' +payConfirmList
	 +'&flag=app'+'&selectShop='+JSON.stringify(uni.getStorageSync('presentShop')) 
	 + '&openid='+uni.getStorageSync('CUSTOMER_USER_INFO_DY').user.openid
	 + '&mer_id='+uni.getStorageSync('CUSTOMER_USER_INFO_DY').user.mer_id
	 + '&mer_name='+uni.getStorageSync('CUSTOMER_USER_INFO_DY').user.mer_name
	 + '&apptoken='+uni.getStorageSync('CUSTOMER_USER_INFO_DY').token;
	console.log('[goWX] launch', miniProgramId, path);
	this.showGoWXLoading();
	plus.share.getServices(
		(services) => {
			const weixin = (services || []).find(
				(item) => item.id === 'weixin'
			);
			if (!weixin) {
				uni.showModal({
					title: '微信 SDK 未就绪',
					content:
						'请使用「自定义调试基座」运行,并确认 manifest 已配置微信分享',
					showCancel: false
				});
				return;
			}
			if (typeof weixin.launchMiniProgram !== 'function') {
				uni.showModal({
					title: '不支持跳转小程序',
					content: '请重新制作自定义调试基座',
					showCancel: false
				});
				return;
			}
			weixin.launchMiniProgram(
				{
					id: miniProgramId,//需是原生gh_开头的原生微信appid
					path,
					type: 2 //小程序版本  0-正式版; 1-测试版; 2-体验版。
				},
				() => {
					this.hideGoWXLoading();
					console.log('[goWX] launchMiniProgram success');
				},
				(err) => {
					this.hideGoWXLoading();
					console.log('[goWX] launchMiniProgram fail', err);
					uni.showModal({
						title: '打开小程序失败',
						content:
							(err && (err.message || err.code)) ||
							JSON.stringify(err || {}),
						showCancel: false
					});
				}
			);
		},
		(err) => {
			this.hideGoWXLoading();
			console.log('[goWX] getServices fail', err);
			uni.showModal({
				title: '获取分享服务失败',
				content: JSON.stringify(err || {}),
				showCancel: false
			});
		}
	);
},

2.小程序获取APP参数,支付

onLoad获取参数

创建支付订单

bash 复制代码
createOrder() {
	uni.showLoading({
		title: '支付中',
		mask: true
	})

	let data = {
		openid: this.openid,
		// openid:'opP7A5b-k_ibUp0cr7RF853FCC6M',
		charge_list_ids: this.selectId, //物业缴费项id:(多个英文逗号隔开),
		pay_mode: '扫码支付',
		pay_channel: 'wx_lite',
		mer_id: this.mer_id,
		mer_name: this.mer_name,
		able_pay_amt: this.totalCount,
		goods_title: '市场管理费',
		goods_desc: '市场管理费',
		device_type: 1,
		unit_area: this.payList[0].unit_area_name,
		unit_location: this.payList[0].unit_location_name,
		unit_no: this.payList[0].unit_no,
		unit_id: this.payList[0].unit_id
	}

	this.Util.postRequest(
		'index/system/wycharge/order/create',
		data,
		this.isFromApp() ? this.apptoken : undefined
	).then(res => {
		uni.hideLoading()
		if (res.status === 200) {

			let payInfo = res.data;

			if (res.message == '您还有待支付的订单') {
				console.log(res)
				wx.requestPayment({
					'timeStamp': payInfo.pay_info.timeStamp, //时间戳
					'nonceStr': payInfo.pay_info.nonceStr, //随机字符串
					'package': payInfo.pay_info.package, //prepay_id 参数值
					'signType': payInfo.pay_info.signType,
					'paySign': payInfo.pay_info.paySign, //签名
					'success': (res) => {
						this.handlePaySuccess()
					},
				})
			} else {
				wx.requestPayment({
					'timeStamp': payInfo.timeStamp, //时间戳
					'nonceStr': payInfo.nonceStr, //随机字符串
					'package': payInfo.package, //prepay_id 参数值
					'signType': payInfo.signType,
					'paySign': payInfo.paySign, //签名
					'success': (res) => {
						this.handlePaySuccess()
					},
					'fail': function(res) {
						console.log(res)
					}
				})
			}
		} else {
			uni.showModal({
				title: '提示',
				content: res.message,
				showCancel: false
			})
		}
	})

3.支付成功,返回APP

bash 复制代码
handlePaySuccess() {
  //区分小程序支付,还是app跳转来支付
	if (this.isFromApp()) {
		//返回APP,通知APP支付成功参数
		this.appParameter = JSON.stringify({
			paySuccess: true
		})
		this.showAppPaySuccess = true
	} else {
		uni.showModal({
			title: '提示',
			content: '支付成功',
			showCancel: false,
			success: (res) => {
				if (res.confirm) {
					let pages = getCurrentPages()
					let beforePage = pages[pages.length - 2]
					uni.navigateBack({
						success: function() {
							beforePage.onLoad()
						}
					})
				}
			}
		})
	}
},

app跳转来的支付,不能使用uni.showModal返回APP,需要手动点击返回按钮才能正确返回APP

bash 复制代码
<!-- App支付成功弹框,用户主动点击返回App -->
<view class="pay-success-mask" v-if="showAppPaySuccess" @click.stop>
	<view class="pay-success-modal">
		<view class="pay-success-icon-wrap">
			<view class="pay-success-icon">✓</view>
		</view>
		<view class="pay-success-title">支付成功</view>
		<view class="pay-success-content">请点击下方按钮返回 App</view>
		<view class="pay-success-footer">
			<button class="launch-app-btn" open-type="launchApp" :app-parameter="appParameter"
				@launchapp="onLaunchApp" @error="onLaunchAppError">返回 App</button>
		</view>
	</view>
</view>
bash 复制代码
showAppPaySuccess: false,
appParameter: '',

onLaunchApp(e) {
	console.log('launchApp success', e)
	this.showAppPaySuccess = false
},
onLaunchAppError(e) {
	console.error('launchApp fail', e)
	uni.showModal({
		title: '提示',
		content: '返回App失败,请手动关闭小程序',
		showCancel: false
	})
},

4.APP接收小程序支付成功参数

miniProgramPayReturn.js

bash 复制代码
// #ifdef APP-PLUS
/**
 * 小程序 launchApp 回传 app-parameter,对应微信 SDK onResp.extMsg
 * Android: WXEntryActivity.onResp -> WXLaunchMiniProgram.Resp.extMsg
 * iOS: WXApiDelegate.onResp -> WXLaunchMiniProgramResp.extMsg
 * uni-app 云端打包会把 extMsg 桥接到 JS,Android/iOS 读取方式略有差异
 */

/** 解析 extMsg / extraData 字符串 */
export function parseMiniProgramExtMsg(extMsg) {
	if (extMsg == null || extMsg === '') return null
	if (typeof extMsg === 'object') return extMsg
	const raw = String(extMsg).trim()
	if (!raw) return null
	try {
		return JSON.parse(raw)
	} catch (e) {
		if (raw.includes('paySuccess')) {
			return {
				paySuccess: raw.includes('true') ||
					raw.includes('"paySuccess":true') ||
					raw.includes('paySuccess=1')
			}
		}
		return null
	}
}

/** Android onResp.extMsg:优先 arguments,后台唤醒时再从 Intent 补读 */
export function getAndroidMiniProgramExtMsg() {
	let extMsg = plus.runtime.arguments
	if (extMsg) return extMsg
	try {
		const main = plus.android.runtimeMainActivity()
		const intent = plus.android.invoke(main, 'getIntent')
		if (!intent) return ''
		const keys = [
			'_wxapi_launch_mini_program_resp_ext_msg',
			'wx_launch_mini_program_ext_msg',
			'extMsg',
			'APP_PARAMETER'
		]
		for (let i = 0; i < keys.length; i++) {
			const value = plus.android.invoke(intent, 'getStringExtra', keys[i])
			if (value) return value
		}
	} catch (e) {
		console.log('[miniProgramPayReturn] android extMsg read fail', e)
	}
	return ''
}

/** iOS onResp.extMsg:uni 桥接至 plus.runtime.arguments */
export function getIOSMiniProgramExtMsg() {
	return plus.runtime.arguments || ''
}

export function clearMiniProgramExtMsg() {
	plus.runtime.arguments = null
	plus.runtime.arguments = ''
}

/** 微信文档:从 App 打开的小程序返回时 launcher 为 miniProgram */
export function isFromMiniProgramReturn() {
	return plus.runtime.launcher === 'miniProgram'
}

export function getMiniProgramReturnDelayMs() {
	const osName = uni.getSystemInfoSync().osName
	return osName === 'ios' ? 500 : 200
}

/** 按平台读取 onResp.extMsg 并解析为 extraData */
export function readMiniProgramPayExtraData() {
	const osName = uni.getSystemInfoSync().osName
	let extMsg = ''
	if (osName === 'android') {
		extMsg = getAndroidMiniProgramExtMsg()
	} else if (osName === 'ios') {
		extMsg = getIOSMiniProgramExtMsg()
	} else {
		extMsg = plus.runtime.arguments || ''
	}
	if (!extMsg) return null
	// 有 extMsg 即视为小程序回传;launcher 仅作辅助日志
	if (!isFromMiniProgramReturn()) {
		console.log('[miniProgramPayReturn] extMsg without miniProgram launcher', plus.runtime.launcher)
	}
	return parseMiniProgramExtMsg(extMsg)
}
// #endif

app.vue

bash 复制代码
import {
	readMiniProgramPayExtraData,
	clearMiniProgramExtMsg,
	getMiniProgramReturnDelayMs
} from './common/miniProgramPayReturn.js'

onLaunch: function() {
	plus.globalEvent.addEventListener('newintent', () => {
		this.handleMiniProgramPayReturn();
	});
}
onShow: function(options) {
	/** App 从小程序返回:Android onResp.extMsg / iOS onResp.extMsg */
	handleMiniProgramPayReturn() {
		const delay = getMiniProgramReturnDelayMs()
		setTimeout(() => {
			const extraData = readMiniProgramPayExtraData()
			if (extraData && extraData.paySuccess) {
				uni.$emit('miniProgramPayResult', extraData)
			}
			clearMiniProgramExtMsg()
		}, delay)
	},
}

跳转小程序页处理

bash 复制代码
onLoad() {
	this.selectShop = uni.getStorageSync('presentShop')
	this.menusList.forEach((list) => {
		list.listData = []
	})
	uni.$on('miniProgramPayResult', this.onMiniProgramPayResult)
	this.payDatail()
},
onUnload() {
	uni.$off('miniProgramPayResult', this.onMiniProgramPayResult)
},
methods: {
	onMiniProgramPayResult(extraData) {
		if (extraData && extraData.paySuccess) {
			this.refreshAfterPaySuccess()
		}
	},
	refreshAfterPaySuccess() {
		uni.showToast({
			title: '支付成功',
			icon: 'success'
		})
		this.menusList.forEach((list) => {
			list.listData = []
		})
		if (this.companyList.length) {
			this.payListDetail()
		}
		this.hideModal()
	},
}