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

已实现Xamarin.iOS应用订阅,如何检测用户取消订阅事件?

在Xamarin.iOS中监听订阅取消事件的实现方案

老哥,首先得明确一点:用户在App Store里直接取消订阅的操作不会触发你现有代码里的SKPaymentTransactionObserver回调——因为这种操作完全在App Store端完成,不会产生新的交易记录。所以我们得换两种思路来捕捉这个事件:


1. 定期主动检查订阅状态(最稳妥的基础方案)

因为取消订阅不会主动通知App,所以我们需要主动去查。通常可以在App启动时、用户进入订阅管理页面时,调用这个检查逻辑。结合你现有的BillingService和CustomPaymentObserver,可以这么改:

先完善你的CustomPaymentObserver实现

确保它能处理所有交易状态(比如购买、恢复、失败这些),代码如下:

internal class CustomPaymentObserver : SKPaymentTransactionObserver
{
    public override void UpdatedTransactions(SKPaymentQueue queue, SKPaymentTransaction[] transactions)
    {
        foreach (var transaction in transactions)
        {
            switch (transaction.TransactionState)
            {
                case SKPaymentTransactionState.Purchased:
                    CompleteTransaction(transaction);
                    break;
                case SKPaymentTransactionState.Failed:
                    FailedTransaction(transaction);
                    break;
                case SKPaymentTransactionState.Restored:
                    RestoreTransaction(transaction);
                    break;
                case SKPaymentTransactionState.Deferred:
                    // 处理延迟交易(比如需要家长审核的情况)
                    break;
                case SKPaymentTransactionState.Purchasing:
                    // 购买中,可显示加载状态
                    break;
            }
        }
    }

    // 复用你已有的交易处理方法
    private void CompleteTransaction(SKPaymentTransaction t)
    {
        // 你的现有逻辑:保存订阅凭证、标记用户订阅状态等
        SKPaymentQueue.DefaultQueue.FinishTransaction(t);
    }

    private void FailedTransaction(SKPaymentTransaction t)
    {
        // 你的现有失败处理逻辑
        SKPaymentQueue.DefaultQueue.FinishTransaction(t);
    }

    private void RestoreTransaction(SKPaymentTransaction t)
    {
        // 你的现有恢复购买逻辑
        SKPaymentQueue.DefaultQueue.FinishTransaction(t);
    }
}

添加订阅状态检查方法

接下来在你的BillingService里加一个方法,用来获取并验证订阅的真实状态:

internal class BillingService : SKProductsRequestDelegate
{
    private BillingService() { /* 你的现有构造逻辑 */ }

    // 新增:检查订阅状态的方法
    public async Task VerifySubscriptionStatusAsync()
    {
        var receiptUrl = NSBundle.MainBundle.AppStoreReceiptUrl;
        // 如果本地没有凭证,触发恢复购买流程
        if (!NSFileManager.DefaultManager.FileExists(receiptUrl.Path))
        {
            SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions();
            return;
        }

        // 把凭证转成Base64字符串,发给后端验证
        var receiptData = NSData.FromUrl(receiptUrl);
        var receiptBase64 = receiptData.Base64EncodedString(NSDataBase64EncodingOptions.EndLineWithLineFeed);

        // 这里一定要用后端服务器去和App Store验证!本地验证容易被篡改,不安全
        var httpClient = new HttpClient();
        var requestBody = new StringContent(
            JsonConvert.SerializeObject(new { Receipt = receiptBase64 }),
            Encoding.UTF8,
            "application/json"
        );

        try
        {
            var response = await httpClient.PostAsync("你的后端验证接口地址", requestBody);
            if (response.IsSuccessStatusCode)
            {
                var statusResponse = await response.Content.ReadAsStringAsync();
                var subscriptionStatus = JsonConvert.DeserializeObject<SubscriptionStatus>(statusResponse);

                // 判断订阅是否被取消或过期
                if (subscriptionStatus.IsCancelled || subscriptionStatus.ExpirationDate < DateTime.UtcNow)
                {
                    // 触发取消后的逻辑:比如关闭付费功能、通知用户等
                    HandleSubscriptionCancelled();
                }
            }
        }
        catch (Exception ex)
        {
            // 处理网络请求异常
            Console.WriteLine($"验证订阅状态失败: {ex.Message}");
        }
    }

    private void HandleSubscriptionCancelled()
    {
        // 这里写你订阅取消后的具体业务逻辑
        // 比如更新UI、移除付费功能权限、弹出提示等
    }

    // 你的现有方法:CompleteTransaction、FailedTransaction、RestoreTransaction...
    public void CompleteTransaction(SKPaymentTransaction t) { /* 你的逻辑 */ }
    public void FailedTransaction(SKPaymentTransaction t) { /* 你的逻辑 */ }
    public void RestoreTransaction(SKPaymentTransaction t) { /* 你的逻辑 */ }
}

// 用来接收后端返回的订阅状态模型
public class SubscriptionStatus
{
    public bool IsCancelled { get; set; }
    public DateTime ExpirationDate { get; set; }
    // 可以加其他字段:比如订阅类型、自动续期状态等
}

2. 配置App Store Server Notifications(实时通知方案)

如果想要实时获取取消事件,可以用苹果的Server Notifications功能:当用户取消订阅、订阅过期、续期失败等状态变化时,App Store会主动向你的后端服务器发送HTTP通知。

然后你的后端可以通过推送通知(比如APNs)把状态变化告知App,这样就能实时处理取消事件了。这种方式需要后端配置接收通知的端点,并且处理苹果的签名验证,适合对实时性要求高的场景。


关键注意点

  • 永远不要本地验证凭证:本地验证的receipt很容易被篡改,必须通过后端服务器调用苹果的验证接口来获取真实状态。
  • 定期检查的频率:不用太频繁,比如App启动时查一次,用户进入订阅页面时查一次就够了,避免不必要的网络请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:10:23