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

生产环境未收到自动续订订阅状态通知及跨设备实现问询

Let's break down your two core issues and walk through practical solutions tailored to your SwiftyStoreKit setup:

1. Why You're Not Receiving RENEWAL Notifications After Free Trial Expiry

First, let's clarify a key distinction that often causes confusion:

  • In-app transaction callbacks (like your completeTransactions code) only trigger when the user has your app open (or running in the background) during a subscription event. For auto-renewals that happen while the app isn't active, Apple sends notifications directly to your configured Subscription Status URL, not to the app itself.

Here are the critical checks to fix missing RENEWAL notifications:

  • Validate your Subscription Status URL setup:
    • Double-check in App Store Connect that the URL uses HTTPS (Apple rejects HTTP endpoints entirely).
    • Verify your server accepts POST requests from Apple's IP ranges, and that firewalls/security tools aren't blocking these requests. Check your server logs for any failed incoming requests from Apple—even error responses can confirm if the URL is reachable.
    • Ensure your server returns a 200 OK response to Apple's notifications. If Apple gets non-200 responses repeatedly, it will stop sending updates.
  • Sandbox vs. Production behavior:
    • In sandbox, auto-renewals are accelerated (free trials last 5 minutes, renewals happen quickly), but there can be delays of 5-10 minutes before notifications hit your server. Wait a full 10 minutes after trial expiry before assuming notifications are missing.
    • Make sure your test Apple ID isn't marked as "invalid" (sandbox accounts can get flagged if you test too many subscription cycles).
  • Server-side notification parsing:
    • Confirm your server is handling all notification types correctly—RENEWAL notifications have a notification_type value of RENEWAL or INTERACTIVE_RENEWAL. Don't filter out any notification types unless you have a specific, documented reason.
2. Ensuring App Store Billing & Notifications When Using Android Only After iOS Purchase

Great news: App Store subscriptions are tied to the user's Apple ID, not their device. Here's how to keep everything running smoothly:

  • Auto-renewal doesn't require the iOS app to be open: Apple will charge the user's Apple ID automatically every billing cycle as long as the subscription is active and not canceled. The user doesn't need to launch the iOS app for this to happen.
  • Subscription Status URL notifications work cross-device: Apple will continue sending all subscription status updates (renewals, cancellations, refunds) to your server's URL regardless of whether the user is using iOS or Android. Your server should act as the single source of truth for subscription state.
  • Sync Android app state with your server: When the user logs into your Android app, it should fetch the latest subscription status directly from your server—Android can't access Apple's receipt data, so local validation isn't possible.

Code Optimizations for Your SwiftyStoreKit Implementation

Your current code has a couple of areas that can be improved to avoid edge cases:

1. Fix Transaction Completion Order

Don't finish the transaction before confirming your server has received and processed the receipt. If your app crashes before the server gets the data, the transaction will re-trigger when the app opens again:

// Updated AppDelegate completeTransactions logic
SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
    for purchase in purchases {
        print("Transaction update: \(purchase.transaction.transactionState) for product \(purchase.productId)")
        switch purchase.transaction.transactionState {
        case .purchased, .restored:
            if purchase.needsFinishTransaction {
                // First send receipt to server, then finish the transaction
                self.sendReceiptToServer(purchase: purchase) { success in
                    if success {
                        SwiftyStoreKit.finishTransaction(purchase.transaction)
                    }
                }
            }
        case .failed:
            print("Transaction failed: \(purchase.transaction.error?.localizedDescription ?? "Unknown error")")
            if purchase.needsFinishTransaction {
                SwiftyStoreKit.finishTransaction(purchase.transaction)
            }
        case .deferred, .purchasing:
            // Handle deferred transactions (e.g., Family Sharing Ask to Buy)
            break
        @unknown default:
            print("Unhandled transaction state")
            break
        }
    }
}

2. Add Receipt Refresh on App Launch

This ensures your app syncs with the latest subscription status if the user made changes on another device:

// Call this on app launch (e.g., in AppDelegate.applicationDidFinishLaunching)
private func refreshReceiptOnLaunch() {
    SwiftyStoreKit.refreshReceipt { result in
        switch result {
        case .success(let receiptData):
            let appleValidator = AppleReceiptValidator(service: self.env, sharedSecret: self.sharedSecret)
            SwiftyStoreKit.verifyReceipt(using: appleValidator) { receiptResult in
                if case .success(let receipt) = receiptResult {
                    let purchaseResult = SwiftyStoreKit.verifySubscription(
                        type: .autoRenewable, productId: self.productId, inReceipt: receipt)
                    switch purchaseResult {
                    case .purchased:
                        self.sendReceiptToServer(receiptData: receiptData)
                    case .expired, .notPurchased:
                        // Update local state to expired/not subscribed
                        break
                    }
                }
            }
        case .error(let error):
            print("Receipt refresh failed: \(error.localizedDescription)")
        }
    }
}

3. Server-Side Receipt Validation Backup

Even with notifications, periodically validate receipts on your server (e.g., once a week) to catch any missed updates. This acts as a safety net to ensure subscription status stays accurate.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:18:06