uni-app ios端取消订阅产品后,再次购买订阅产品不会重新拉起支付,而是直接进入支付成功回调
uni-app ios端取消订阅产品后,再次购买订阅产品不会重新拉起支付,而是直接进入支付成功回调
产品分类
uniapp/App
PC开发环境
- 操作系统:Windows
- 操作系统版本号:windows10 v2009
手机系统
- 系统:iOS
- 系统版本号:iOS 15
- 手机厂商:苹果
- 手机机型:iPhone6s Plus
开发工具
- HBuilderX类型:正式
- HBuilderX版本号:3.99
项目信息
- 页面类型:vue
- vue版本:vue2
- 打包方式:云端
- 项目创建方式:HBuilderX
示例代码
import {
Iap,
IapTransactionState,
} from "@/utils/iap.js"
export default {
data() {
return {
// 获取苹果真实的产品列表
iapProductList: [],
loading: false,
disabled: true,
personId: uni.getStorageSync("personId"),
quantity: 1,
}
},
methods: {
// 初始化支付环境
initPayEnv(products) {
this._iap = new Iap({
products
})
this.init();
},
async init() {
uni.showLoading({
title: '正在检测支付环境...'
});
try {
await this._iap.init();
this.iapProductList = await this._iap.getProduct();
this.disabled = false;
} catch (e) {
uni.showToast({
icon: 'none',
title: '支付环境检测失败',
duration: 2000
});
} finally {
uni.hideLoading();
}
if (this._iap.ready) {
this.restore();
}
},
async restore() {
this.loading = true;
this.disabled = true
// 检查上次用户已支付且未关闭的订单,可能出现原因:首次绑卡,网络中断等异常
// 提示是正在检测支付环境,实际是正在检测未关闭的订单
uni.showLoading({
title: '正在检测支付环境...'
});
try {
// 从苹果服务器检查未关闭的订单,可选根据 username 过滤,和调用支付时透传的值一致
const transactions = await this._iap.restoreCompletedTransactions({
username: ''
});
console.log(transactions)
if (!transactions.length) {
return;
}
for (var i = 0; i < transactions.length; i++) {
let transaction = transactions[i]
switch (transaction.transactionState) {
case IapTransactionState.purchased:
// 用户已付款,在此处请求开发者服务器,在服务器端请求苹果服务器验证票据
const username = transaction.payment.username || uni.getStorageSync(
'IAP_ORDER_USERNAME');
console.log('username', username)
if (!username) {
return await this._iap.finishTransaction(transaction);
}
this.validatePaymentResult(username, transaction);
break;
case IapTransactionState.failed:
// 关闭未支付的订单
await this._iap.finishTransaction(transaction);
break;
// case IapTransactionState.restored:
// // 关闭未支付的订单
// this.validatePaymentResult(username, transaction);
// break;
default:
break;
}
}
} catch (e) {
uni.showToast({
icon: 'none',
title: e.message,
duration: 2000
});
} finally {
this.loading = false
this.disabled = false
uni.hideLoading();
}
},
async onPayment() {
if (this.loading === true) {
return;
}
this.loading = true;
this.disabled = true
try {
// 从开发者服务器创建订单
const orderInfo = await this.$u.post('/api/finance/CreateIAPOrder', {
"PersonId": this.personId,
"CoinCount": this.currProductInfo.Price,
"ProductId": this.currProductId
})
console.log(orderInfo)
if (orderInfo && orderInfo.Status) {
// 本地缓存username
uni.setStorageSync('IAP_ORDER_USERNAME', orderInfo.UserName);
} else {
uni.showToast({
icon: 'none',
title: '订单创建失败,请联系管理员',
duration: 3000
});
uni.hideLoading()
return
}
// 请求苹果支付
const transaction = await this._iap.requestPayment({
productid: this.currProductId,
username: orderInfo.UserName,
quantity: this.quantity,
manualFinishTransaction: true,
});
console.log('transaction', transaction)
if (transaction.transactionState === IapTransactionState.purchased) {
// 在此处请求开发者服务器,在服务器端请求苹果服务器验证票据
const res = await this.validatePaymentResult(transaction.payment.username, transaction)
if (res.ReturnCode === 1001) {
// 支付成功,重新获取用户余额
uni.showToast({
icon: 'success',
title: '支付成功',
duration: 2000
});
// 触发统一的支付成功函数,由各个业务页面自行处理成功逻辑
this.paySuccess()
} else {
uni.showToast({
icon: 'none',
title: res.MessageChinese,
duration: 3000
});
}
uni.hideLoading()
this.onPaySuccessReset()
}
} catch (e) {
console.log(e)
uni.setStorageSync('IAP_ORDER_USERNAME', '');
if (e.errCode === 2 && e.code === 2) {
uni.showToast({
icon: 'none',
title: '支付已取消',
duration: 2000
});
} else {
uni.showToast({
icon: 'none',
title: e.message,
duration: 2000
});
}
uni.hideLoading()
this.onPaySuccessReset()
} finally {
// this.onPaySuccessReset()
}
},
// 向本地服务器验证支付结果
async validatePaymentResult(userName, transaction) {
const serverRes = await this.$u.post('/api/finance/IAPOrderVerify', {
"UserName": userName,
"TransactionReceipt": transaction.transactionReceipt,
})
console.log(serverRes)
if (serverRes.ReturnCode === 1001) {
uni.setStorageSync('IAP_ORDER_USERNAME', '');
// 验证成功后关闭订单
await this._iap.finishTransaction(transaction);
}
return serverRes
}
}
}
操作步骤
- ios测试环境:沙盒环境
- 场景一:在每次支付前不调用
restoreCompletedTransactionsapi的情况下,订阅产品在订阅状态下,再次购买苹果系统会提示“你目前已订阅此项目” - 场景二:在每次支付前调用
restoreCompletedTransactionsapi的情况下,订阅产品在订阅状态下,再次购买会直接进入支付成功回调。 - 场景三:在产品订阅情况下,进入设置->App Store->沙盒账户->取消产品订阅,再进入app,在未调用
restoreCompletedTransactionsapi后购买该订阅产品,会先执行支付成功回调,再弹出系统支付弹窗。 - 场景四:在产品订阅情况下,进入设置->App Store->沙盒账户->取消产品订阅,再进入app,在调用
restoreCompletedTransactionsapi后购买该订阅产品,会直接进入支付成功回调,支付弹窗未弹出。同时,检查设置->App Store->沙盒账户->产品订阅状态还是未订阅状态
预期结果
- 产品在未订阅状态下:可以订阅成功。
- 产品在已订阅状态下:有系统提示用户。
实际结果
- 无法形成订阅产品支付闭环
更多关于uni-app ios端取消订阅产品后,再次购买订阅产品不会重新拉起支付,而是直接进入支付成功回调的实战教程也可以访问 https://www.itying.com/category-93-b0.html
有官方同学解答下吗?
+1 我也遇到了
顶,好多问题啊
建议用原生插件来解决这个问题
不会原生的
回复 y***@163.com: 我写了个,https://ext.dcloud.net.cn/plugin?id=17130
生产环境也这样,会返回初次的订单记录,而不是拉起支付弹窗,官方能不能看看问题啊,服了
根据DCloud_iOS_WZT大佬的指导已经解决了问题,我没有使用官方的iap.js,所以在调用iapChannel.finishTransaction(Transaction, <Function> success, <Function> fail)原生函数的时候我没有传递success、fail这两个回调函数导致关闭交易失败了。
回复 y***@163.com: 我使用官方的iap.js,但是也碰到了同样的问题
有没官方的人出来解释下哇
解决了,刚开始是因为没有关闭交易,后面沙盒环境这样是因为有延迟问题,取消订阅后需要等待20分钟后再次发起支付就可以拉起弹窗了
回复 y***@163.com: 每次支付前都会restore,然后关闭订单吗
回复 y***@163.com: restore后无法重新订阅(拉起订阅弹窗)
回复 PenGuin1: 你取消订阅之后过一段时间再试试呢
在 uni-app 中,iOS 端的订阅产品在取消订阅后再次购买时,可能会出现直接进入支付成功回调的情况,而不是重新拉起支付。这通常是由于 Apple 的订阅机制导致的。以下是一些可能的原因和解决方案:
1. Apple 的订阅机制
- 自动续订:Apple 的订阅产品在用户取消订阅后,如果用户再次购买相同的订阅产品,系统会自动续订,而不会重新拉起支付流程。这是因为 Apple 会认为用户已经同意继续订阅。
- 支付成功回调:由于系统自动续订,支付流程不会再次触发,因此会直接进入支付成功回调。
2. 解决方案
- 检查订阅状态:在用户尝试再次购买订阅产品之前,可以通过
uni-app提供的 API 或 Apple 的服务器验证用户的订阅状态。如果用户已经订阅,可以提示用户不需要再次购买。 - 引导用户重新订阅:如果用户确实需要重新订阅,可以引导用户到 Apple 的订阅管理页面,手动重新订阅。
- 处理自动续订:在支付成功回调中,处理自动续订的逻辑,确保用户的订阅状态更新正确。
3. 代码示例
-
检查订阅状态:
uni.request({ url: 'https://api.apple.com/check_subscription_status', // 替换为实际的 Apple 服务器接口 method: 'GET', success(res) { if (res.data.subscribed) { console.log('用户已经订阅,无需再次购买'); } else { console.log('用户未订阅,可以继续购买'); } }, fail(err) { console.error('检查订阅状态失败', err); } }); -
处理自动续订:
uni.requestPayment({ provider: 'appleiap', orderInfo: { productId: 'your_subscription_product_id' }, success(res) { console.log('支付成功', res); // 处理自动续订逻辑 updateSubscriptionStatus(res); }, fail(err) { console.error('支付失败', err); } }); function updateSubscriptionStatus(res) { // 更新用户的订阅状态 uni.request({ url: 'https://your_server/update_subscription_status', method: 'POST', data: { transactionId: res.transactionId, subscriptionStatus: 'active' }, success(response) { console.log('订阅状态更新成功', response); }, fail(error) { console.error('订阅状态更新失败', error); } }); }


