人物略有不同
人物略有不同
  • 发布:2026-05-27 10:52
  • 更新:2026-05-27 10:56
  • 阅读:18

我是怎么实现ios内购后恢复购买的

分类:uni-app
// 在客户端app,定义一个获取历史苹果收据的方法如下:  
function getIapOrders() {  
    return new Promise(async (resolve, reject) => {  
        try {  
            // 1. 导入 iOS 原生类  
            const NSBundle = plus.ios.importClass("NSBundle");  
            const NSData = plus.ios.importClass("NSData");  
            // 2. 获取收据 URL  
            const url = NSBundle.mainBundle().appStoreReceiptURL();  
            if (!url) {  
                uni.hideLoading();  
                uni.showToast({  
                    title: '未找到收据路径',  
                    icon: 'none'  
                });  
                reject()  
                return;  
            }  
            // 3. 读取收据数据  
            const receiptData = NSData.dataWithContentsOfURL(url);  
            if (receiptData) {  
                // 4. 【核心修改】使用 plus.ios.invoke 显式调用 base64EncodedStringWithOptions: 方法  
                // 注意:方法名后面的冒号 ":" 必须保留,代表这是一个带参数的方法  
                const base64Receipt = plus.ios.invoke(receiptData, "base64EncodedStringWithOptions:", 0);  
                if (base64Receipt) {  
                    //加密过的,它是苹果官方为你在这个 App 里发生的所有成功交易出具的“电子发票”和“资产清单”。  
                    console.log("成功获取到本地收据 Base64 数据", base64Receipt);  
                    // 5. 发送给您的服务器,解密取出数据  
                    uni.vk.callFunction({  
                        url: 'client/order/pub/verifyReceipt',  
                        data: {  
                            transaction_receipt: base64Receipt  
                        },  
                        success(res) {  
                            // 返回苹果给的交易记录  
                            resolve(res.rows)  
                        },  
                        complete(res) {  

                        }  
                    });  

                } else {                      
                    uni.hideLoading();  
                    uni.showToast({  
                        title: '收据转换 Base64 失败',  
                        icon: 'none'  
                    });  
                    reject();  
                }  
            } else {  
                uni.hideLoading();  
                uni.showToast({  
                    title: '收据内容为空,请重试',  
                    icon: 'none'  
                });  
                reject()  
            }  
        } catch (e) {  
            uni.hideLoading();  
            console.error("读取收据失败: ", e);  
            uni.showToast({  
                title: '读取凭证失败: ' + e.message,  
                icon: 'none'  
            });  
            reject()  
        }  

    })  

}
'use strict';  
const uniPay = require("uni-pay");  
module.exports = {  
    /**  
     * 加密数据,它是苹果官方为你在这个 App 里发生的所有成功交易出具的“电子发票”和“资产清单”。  
     * 通过verifyReceipt数据校验后返回json交易数据  
     * @url client/order/pub/verifyReceipt 前端调用的url参数地址  
     * data 请求参数  
     * @param {String} params1  参数1  
     */  
    main: async (event) => {  
        let { data = {}, userInfo, util, filterResponse, originalParam } = event;  
        let { customUtil, uniID, config, pubFun, vk, db, _, $ } = util;  
        let { uid } = data;  
        let res = { code: 0, msg: "" };  
        // 业务逻辑开始-----------------------------------------------------------  
        // 测式时的沙箱模式下  
        let  uniPayInstance =  uniPay.initAppleIapPayment({ provider: "appleiap", provider_pay_type: "app" ,sandbox:true});  
        let tradeRes = await uniPayInstance.verifyReceipt({  
            receiptData: data.transaction_receipt  
        });  
        // console.log("************ uniPayInstance **************",JSON.stringify(tradeRes))  
        res.rows=[];  
        if(tradeRes.tradeState == "SUCCESS"){  
            /**  
             * original_purchase_date、   original_purchase_date_ms、 original_transaction_id  
             * product_id、purchase_date、purchase_date_ms  
             * quantity  
             * transaction_id  
             */  
            res.rows = tradeRes.receipt.in_app  
        }  
        debugger  
        // 业务逻辑结束-----------------------------------------------------------  
        return res;  
    }  
}

0 关注 分享

要回复文章请先登录注册

人物略有不同

人物略有不同 (作者)

1. 凭证(Receipt)具体包含哪些成功交易?
只要用户在这个 App 里付过钱,苹果就会把交易记录加密写进这个凭证文件中。它主要包含以下两大类成功交易:

App 本身的购买记录:如果你的 App 是付费下载的,这里会记录用户购买下载该 App 的时间和订单号(即使是免费下载,也会记录一条免费下载的凭证)。

应用内购买(In-App Purchase, 简称 IAP):

消耗型项目:比如游戏里的金币、皮肤、直播的打赏道具(一旦使用就消耗掉了)。

非消耗型项目:比如解锁关卡、购买永久去广告功能。

自动续期/非自动续期订阅:比如购买了 App 的月度会员、季度会员。凭证里会包含他们每一次续费成功的记录。

2. 凭证里长什么样?(数据结构)
这段代码直接读取出来的 receiptData 是一堆加了密、无法直接读取的二进制文件(PKCS #7 格式)。

当你的服务器把这段数据发送给苹果服务器进行验证后,苹果会返回一段结构化的明细数据。里面长这样(简化版):

JSON


{
"bundle_id": "com.yourcompany.app", // 你的App包名
"application_version": "1.0.0", // App版本
"in_app": [ // 重点:用户在这个App里的成功交易历史列表
{
"quantity": "1",
"product_id": "com.yourcompany.app.vip1month", // 购买的产品ID(比如月度会员)
"transaction_id": "1000000123456789", // 苹果的官方交易流水号
"purchase_date": "2026-05-27 10:00:00 UTC", // 成功交易的时间
"expires_date": "2026-06-27 10:00:00 UTC" // 如果是订阅,这是过期时间
},
{
"quantity": "10",
"product_id": "com.yourcompany.app.coin100", // 另一笔交易(比如买了100金币)
"transaction_id": "1000000987654321",
"purchase_date": "2026-05-20 15:30:00 UTC"
}
]
}
3. 特别注意:它不包含“失败”或“未完成”的交易
没有银行卡信息:里面绝对不包含用户的银行卡号、微信/支付宝账号或者苹果账号密码。

没有失败的记录:如果用户点开支付弹窗,最后取消了付款,或者因为银行卡余额不足导致扣款失败,这些信息是绝对不会出现在凭证里的。凭证只记录“板上钉钉”已经扣款成功的交易。

退款会被标记:如果用户事后向苹果申请了退款并且成功了,苹果在验证这个凭证时,会带有一个 cancellation_date(取消/退款时间)的字段,告诉你的服务器这笔钱已经退了。

所以,开发者拿到这个凭证,就是为了在服务器端一行行检查 in_app 数组里的交易记录,确认用户真的花钱买了某个商品,然后放心地在游戏或软件里给用户发货。
2026-05-27 10:56