You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Forms iOS自动续订订阅:收据解密与本地验证咨询

Xamarin Forms iOS自动续订订阅问题解答

1. 购买验证是否为强制要求?

Apple规则明确:仅解锁应用内功能、不提供外部内容的自动续订订阅,并非强制要求服务器端验证,客户端验证是被允许的。你遇到的审核被拒大概率是审核人员的误解,申诉时可明确说明订阅仅用于解锁应用内功能、无外部内容交付,同时引用Apple官方客户端验证的文档依据。

2. 如何解密App Store收据?

App Store收据是PKCS #7格式的签名容器,在Xamarin Forms iOS中可借助BouncyCastle库(NuGet可直接安装)完成解密与解析,核心实现如下:

步骤1:获取设备收据数据

using Foundation;
using UIKit;

public byte[] GetReceiptData()
{
    var receiptUrl = NSBundle.MainBundle.AppStoreReceiptUrl;
    if (receiptUrl == null || !NSFileManager.DefaultManager.FileExists(receiptUrl.Path))
    {
        // 触发App Store刷新收据
        var request = new SKReceiptRefreshRequest();
        request.Start();
        return null;
    }
    return NSData.FromUrl(receiptUrl).ToArray();
}

步骤2:解析PKCS #7收据并验证签名

using Org.BouncyCastle.Cms;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Security;

public string ParseReceipt(byte[] receiptData)
{
    var cms = new CmsSignedData(receiptData);
    
    // 用Apple根证书验证签名(需提前将AppleRootCA-G3.cer嵌入项目)
    var signerInfos = cms.GetSignerInfos();
    var certs = cms.GetCertificates("Collection");
    foreach (var signerInfo in signerInfos.GetSigners())
    {
        var cert = certs.GetCertificate(signerInfo.SignerID);
        var validator = new SignerInformationVerifier(signerInfo.SignerID, cert);
        if (!signerInfo.Verify(validator))
        {
            throw new SecurityException("收据签名验证失败");
        }
    }
    
    // 提取收据JSON内容
    var content = (CmsProcessableByteArray)cms.SignedContent;
    return System.Text.Encoding.UTF8.GetString((byte[])content.Content);
}

3. 如何不使用服务器进行购买验证?

客户端验证需完成核心流程:获取并解析收据、检查订阅状态(过期时间、自动续订状态等),示例代码如下:

using Newtonsoft.Json.Linq;

public bool ValidateSubscriptionLocally()
{
    var receiptData = GetReceiptData();
    if (receiptData == null) return false;
    
    try
    {
        var receiptJson = ParseReceipt(receiptData);
        var receiptObj = JObject.Parse(receiptJson);
        var latestReceiptInfos = receiptObj["latest_receipt_info"] as JArray;
        
        if (latestReceiptInfos == null || latestReceiptInfos.Count == 0)
            return false;
        
        // 获取最新的订阅记录
        var latestSubscription = latestReceiptInfos.OrderByDescending(x => x["expires_date_ms"]).First();
        var expiresDateMs = long.Parse(latestSubscription["expires_date_ms"].ToString());
        var currentTimeMs = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
        
        // 验证订阅是否未过期
        return currentTimeMs < expiresDateMs;
    }
    catch
    {
        return false;
    }
}

提示:客户端验证存在被篡改的风险,若应用对安全性要求较高,仍建议结合服务器端验证;但对于仅解锁基础内功能的场景,客户端验证完全满足合规要求。

内容的提问来源于stack exchange,提问作者jho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 02:46:24