uni-app怎么实现App端内购功能 uni-app接入iOS应用内付费教程实战案例|Duuu笔记
iOS内购必须通过原生插件(如uni-app-iap)桥接StoreKit,uni.pay和uni.requestPayment对IAP无效;商品查询、购买、验签等全流程需原生支持,服务端必须向Apple接口验证receipt,沙盒与正式环境验签地址及逻辑须严格区分。
iOS内购必须用原生模块,uni-app不能直接调用StoreKit
uni-app 的
uni.pay
只支持微信/支付宝,对 iOS 应用内购买(IAP)完全不生效。想做 App 端内购,必须通过原生插件桥接,否则打包后点击购买会静默失败或报错
undefined is not an object (evaluating 'uni.requestPayment')
。
常见错误现象:在 HBuilderX 里点“运行到手机”后调用
uni.requestPayment
,控制台没报错但什么也不弹——因为该 API 在 iOS 真机上对 IAP 返回空实现,连回调都不会触发。
所有商品查询、购买、验证、恢复购买,都得走原生层,JS 层只负责发指令和收结果
不能依赖
uni.getProvider
检测 IAP 支持,它返回的
appleiap
是假的,实际不可用
HBuilderX 自带的“苹果应用内支付”插件(
uni-pay
)已多年未更新,iOS 16+ 上大概率闪退,别碰
必须自己封装或选用可靠原生插件,推荐 DCloud 官方
uni-app-iap
DCloud 社区维护的
uni-app-iap
插件(非官方但被广泛验证)目前是少数能在 iOS 17 下稳定工作的方案。它把 StoreKit 封装成 JS 可调用的 Promise 接口,关键能力都覆盖了。
使用场景:你需要展示商品列表、发起购买、监听交易完成、处理续订/降级、恢复已购项目——这些都不能靠 JS 模拟,必须和原生 transaction 状态严格同步。
安装方式:通过 HBuilderX 的“原生插件管理”添加,插件 ID 是
uni-app-iap
,不是 npm install
初始化必须在
onLaunch
里调用
iap.init()
,否则后续所有方法都返回
not initialized
商品 ID 必须和 App Store Connect 后台完全一致,大小写、下划线都不能错,否则
iap.getProducts
返回空数组
调试时开启 Xcode 控制台,看是否出现
SKErrorDomain Code=0
(沙盒环境未配置)或
SKErrorDomain Code=2
(用户未登录测试账号)
服务端验签不能省,客户端验签等于没验
iOS 返回的 receipt data 是 base64 字符串,但直接在前端解码校验毫无意义——攻击者可以伪造任意 receipt 并绕过。真正有效的验签必须由你自己的服务器向 Apple 的
https://buy.itunes.apple.com/verifyReceipt
或沙盒地址发起 POST 请求。
白瓜AI
白瓜AI,一个免费图文AI创作工具,支持 AI 仿写,图文生成,敏感词检测,图片去水印等等。
下载
参数差异:沙盒验签用
https://sandbox.itunes.apple.com/verifyReceipt
,上线后要切回正式地址;两者返回结构一致,但沙盒环境不接受正式 receipt,反之亦然。
receipt data 必须原样透传,不要 base64 decode 再 encode,不要 trim 空格,否则 Apple 返回
status: 21002
服务器响应必须包含
status: 0
和有效
latest_receipt_info
,前端才应解锁功能;仅检查 status 不为 0 不够,得确认对应 product_id 和 expires_date_ms(订阅类)
Apple 的验签接口可能返回 503,需加重试逻辑;单次请求超时建议设为 10s,避免前端卡死
订阅类产品必须处理续订、降级、跨档迁移,别只写一次购买逻辑
用户买的是“订阅”,不是“一次付款”。如果只按普通商品处理,用户续订失败、手动取消、换档位(比如月付切年付)时,你的服务端和客户端状态就会彻底脱节。
性能影响:每次打开 App 都该调用
iap.refreshReceipt()
+ 服务端验签,但别在首页 onLoad 里同步阻塞执行——应该用 background fetch 或静默触发,避免白屏等待。
用户在 App Store 手动取消订阅后,Apple 不会主动通知你;你只能靠下次验签时发现
latest_receipt_info
中的
expires_date_ms
已过期
降级(如年付→月付)会在当前周期结束前生效,
pending_renewal_info
字段会体现新计划,但旧订单仍有效直到到期
客户端恢复购买(restore)不是“重新扣款”,而是拉取历史 receipt 并交服务端验签,必须和正常购买走同一套验签流程
最易被忽略的是沙盒测试账号的生命周期:每个测试账号有 3 次订阅试用机会,用完就得换号,否则所有 purchase 请求返回
SKErrorDomain Code=2
却不提示原因。
