Skip to content

Troubleshooting

Learn how to diagnose and fix common issues with the Apple Pay iOS SDK component.

Exception types and error codes

SDK exceptions

The Apple Pay iOS SDK throws specific exceptions for different error scenarios:

ExceptionError codeDescriptionPrevention
ApplePayPaymentRequestApiNotSupportedExceptionSDK0602Apple Pay is not supported on this device or iOS version.Check device compatibility and iOS version before initialisation.
ApplePaySessionCancelledExceptionSDK0615The Apple Pay session was cancelled by the user.Handle gracefully as this is expected user behaviour.
ApplePayMerchantValidationFailedExceptionSDK0616The Apple Pay merchant validation failed.Verify your merchant configuration and certificates.
ApplePayCustomApiProcessingFailedExceptionSDK0617Custom API processing failed during Apple Pay payment.Check network connectivity and API configuration.
ApplePayValidationExceptionSDK0501The payment request validation failed.Validate all required fields before submission.
ApplePayPaymentFailedExceptionSDK0303The Apple Pay payment processing failed.Implement retry logic and proper error handling.
ApplePayMerchantIdentifierRequiredExceptionCustomThe merchant identifier is missing from the configuration.Ensure merchant identifier is properly configured.
ApplePayPaymentRequestNotInitializedExceptionCustomThe payment request was not properly initialised.Validate payment request before starting payment flow.
ApplePayHttpRequestFailedExceptionCustomHTTP request failed during payment processing.Check network connectivity and server availability.
ApplePayBlobDecryptionFailedExceptionCustomFailed to decrypt Apple Pay payment token.Verify merchant certificates and configuration.
ApplePayDisbursementRequestNotSupportedExceptionCustomDisbursement requests are not supported on this iOS version.Check iOS version (requires iOS 17.0+) before using disbursements.

Error handling best practices

Comprehensive error handler

let config = ApplePayButtonComponentConfig()

config.onError = { error in
    // Log error for debugging
    print("Apple Pay Error: \(error)")
    print("Error Code: \(error.userInfo["errorCode"] ?? "Unknown")")
    print("Error Message: \(error.localizedDescription)")
    
    // Handle specific error types
    switch type(of: error) {
    case is ApplePayPaymentRequestApiNotSupportedException.Type:
        showUserMessage("Apple Pay is not available on this device. Please use an alternative payment method.")
        showAlternativePaymentMethods()
        
    case is ApplePayMerchantValidationFailedException.Type:
        showUserMessage("Payment system temporarily unavailable. Please try again in a few moments.")
        logCriticalError("Merchant validation failed", error: error)
        
    case is ApplePaySessionCancelledException.Type:
        // User cancelled - don't show error
        trackEvent("apple_pay_cancelled")
        
    case is ApplePayPaymentFailedException.Type:
        showUserMessage("Payment failed. Please check your payment information and try again.")
        offerRetryOption()
        
    case is ApplePayValidationException.Type:
        showUserMessage("Please check your payment details and try again.")
        highlightValidationErrors()
        
    default:
        showUserMessage("An unexpected error occurred. Please try again or contact support.")
        logUnknownError(error)
    }
}

config.onCancel = { error in
    print("Apple Pay cancelled by user")
    trackEvent("apple_pay_cancelled")
    // Handle graceful cancellation
}

func showUserMessage(_ message: String) {
    DispatchQueue.main.async {
        // Show user-friendly error message in your UI
        let alert = UIAlertController(title: "Payment Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        // Present alert to user
    }
}

func logCriticalError(_ context: String, error: Error) {
    // Send to error monitoring service
    // Example: Crashlytics.record(error: error)
}

Troubleshooting common issues

Apple Pay button isn't showing

The symptoms of this are:

  • The button element is hidden or not rendered.
  • There's an empty container where the button should appear.
  • No Apple Pay option is visible to customers.

Diagnostic steps

// Step 1: Check Apple Pay availability
print("Checking Apple Pay availability...")

// Check if PassKit is available
import PassKit

if !PKPaymentAuthorizationController.canMakePayments() {
    print("Error: Apple Pay cannot make payments - no setup or unsupported device")
    return
}

// Step 2: Check specific networks
let supportedNetworks: [PaymentNetwork] = [.visa, .masterCard, .amex, .discover]
let paymentService = ApplePayPaymentService()

if !paymentService.canMakePayments(usingNetworks: supportedNetworks) {
    print("Warning: Apple Pay available but no supported cards configured")
}

// Step 3: Verify merchant configuration
guard let merchantId = checkoutConfig.session.allowedFundingTypes.wallets?.applePay?.merchantId else {
    print("Error: Merchant ID is missing")
    return
}

if !merchantId.hasPrefix("merchant.") {
    print("Error: Invalid merchant ID format: \(merchantId)")
    return
}

// Step 4: Check iOS version for advanced features
if #available(iOS 17.0, *) {
    print("iOS 17.0+ features available (disbursements)")
} else {
    print("Limited to basic Apple Pay features")
}

print("All Apple Pay requirements met")

Solutions

// Solution: Graceful fallback
func initializeApplePay() {
    if checkApplePayAvailability() {
        renderApplePayButton()
    } else {
        renderAlternativePaymentMethods()
    }
}

func checkApplePayAvailability() -> Bool {
    return PKPaymentAuthorizationController.canMakePayments() &&
           hasValidMerchantId() &&
           isIOSVersionSupported()
}

func renderApplePayButton() {
    do {
        let applePayComponent = try ApplePayButtonComponent(
            config: checkoutConfig,
            componentConfig: applePayConfig
        )
        
        // Mount the component to your view
        let buttonView = try applePayComponent.render()
        containerView.addSubview(buttonView)
        
        // Setup constraints
        buttonView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            buttonView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            buttonView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            buttonView.topAnchor.constraint(equalTo: containerView.topAnchor),
            buttonView.heightAnchor.constraint(equalToConstant: 44)
        ])
        
    } catch {
        print("Failed to initialize Apple Pay: \(error)")
        renderAlternativePaymentMethods()
    }
}

func renderAlternativePaymentMethods() {
    // Show alternative payment options
    let creditCardButton = UIButton(type: .system)
    creditCardButton.setTitle("Pay with Credit Card", for: .normal)
    // Add to your view hierarchy
}

Merchant validation failures

The symptoms of this are:

  • An error occurs during the Apple Pay session initialisation.
  • "Merchant validation failed" error messages are displayed.
  • The payment sheet doesn't appear after button tap.

Diagnostic steps

// Check merchant ID configuration
struct MerchantDiagnostics {
    let merchantId: String?
    let bundleId: String
    let deviceModel: String
    let iOSVersion: String
}

let diagnostics = MerchantDiagnostics(
    merchantId: checkoutConfig.session.allowedFundingTypes.wallets?.applePay?.merchantId,
    bundleId: Bundle.main.bundleIdentifier ?? "Unknown",
    deviceModel: UIDevice.current.model,
    iOSVersion: UIDevice.current.systemVersion
)

print("Merchant Validation Diagnostics: \(diagnostics)")

// Test payment request creation
func testPaymentRequestCreation() {
    guard let merchantId = diagnostics.merchantId else {
        print("Error: Merchant ID is nil")
        return
    }
    
    let paymentRequest = ApplePayPaymentRequest(
        merchantIdentifier: merchantId,
        countryCode: "US",
        currencyCode: "USD",
        supportedNetworks: [.visa, .masterCard],
        merchantCapabilities: [.supports3DS, .supportsEMV],
        total: ApplePayPaymentSummaryItem(
            amount: Decimal(string: "10.00") ?? 0,
            type: .final,
            label: "Test Payment"
        )
    )
    
    // Validate the payment request
    if ApplePayValidationService.validatePaymentRequest(paymentRequest) {
        print("Payment request validation passed")
    } else {
        print("Payment request validation failed")
    }
}

Solutions

Verify the Apple Developer Console setup
// Checklist for Apple Developer Console
struct MerchantSetupChecklist {
    let merchantIdCreated: Bool = true           // Created in Apple Developer Console
    let merchantIdFormat: String = "merchant.com.yourcompany.store" // Correct format
    let certificateGenerated: Bool = true        // Payment processing certificate created
    let certificateInstalled: Bool = true        // Certificate installed on server
    let bundleIdRegistered: Bool = true         // Bundle ID registered for merchant ID
    let entitlementsConfigured: Bool = true     // Apple Pay entitlements in app
}

func validateMerchantSetup() -> Bool {
    // Check entitlements
    guard let entitlements = Bundle.main.entitlements else {
        print("Error: Cannot read app entitlements")
        return false
    }
    
    if let applePayMerchantIds = entitlements["com.apple.developer.in-app-payments"] as? [String] {
        print("Apple Pay merchant IDs configured: \(applePayMerchantIds)")
        return !applePayMerchantIds.isEmpty
    } else {
        print("Error: Apple Pay entitlements not configured")
        return false
    }
}
Enhanced validation before payment
func validateConfigurationBeforePayment() -> Bool {
    // Validate merchant ID
    guard let merchantId = checkoutConfig.session.allowedFundingTypes.wallets?.applePay?.merchantId,
          !merchantId.isEmpty else {
        print("Error: Merchant ID is missing or empty")
        return false
    }
    
    guard merchantId.hasPrefix("merchant.") else {
        print("Error: Invalid merchant ID format: \(merchantId)")
        return false
    }
    
    // Validate entitlements
    if !validateMerchantSetup() {
        return false
    }
    
    // Check Apple Pay availability
    if !PKPaymentAuthorizationController.canMakePayments() {
        print("Error: Apple Pay not available on device")
        return false
    }
    
    return true
}

// Use before starting payment
func startApplePayPayment() {
    guard validateConfigurationBeforePayment() else {
        showConfigurationError()
        return
    }
    
    // Proceed with payment
    let paymentRequest = createPaymentRequest()
    applePayService.startPayment(with: paymentRequest)
}
Retry logic for transient failures
func startPaymentWithRetry(maxRetries: Int = 3) {
    Task {
        for attempt in 1...maxRetries {
            do {
                let result = try await processPayment()
                // Success - exit retry loop
                handlePaymentSuccess(result)
                return
            } catch let error as ApplePayMerchantValidationFailedException {
                print("Merchant validation attempt \(attempt) failed: \(error)")
                
                if attempt == maxRetries {
                    handlePaymentFailure(error)
                    return
                }
                
                // Wait before retrying (exponential backoff)
                let delay = TimeInterval(pow(2.0, Double(attempt)))
                try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
            }
        }
    }
}

Payment authorisation failures

The symptoms of this are:

  • The Apple Pay sheet appears but payment fails.
  • The customer sees a generic error message.
  • The transaction doesn't complete successfully.

Diagnostic steps

// Enhanced payment authorisation with detailed logging
config.onPreAuthorisation = { result in
    print("Payment Authorisation Started:")
    print("- Payment Token: \(result.data.token.paymentData != nil ? "Present" : "Missing")")
    print("- Billing Contact: \(result.data.billingContact != nil ? "Present" : "Missing")")
    print("- Shipping Contact: \(result.data.shippingContact != nil ? "Present" : "Missing")")
    print("- Payment Method: \(result.data.paymentMethod)")
    
    // Return transaction initialisation data
    return BaseTransactionResponse() // Your transaction init logic here
}

config.onPostAuthorisation = { result in
    print("Payment Authorisation Completed:")
    print("- Transaction State: \(result.state)")
    print("- Transaction ID: \(result.transactionId ?? "None")")
    
    if let errorReason = result.errorReason {
        print("- Error Reason: \(errorReason)")
    }
    
    // Handle success/failure
    switch result.state {
    case "Authorised", "Captured":
        handlePaymentSuccess(result)
    default:
        handlePaymentFailure(result)
    }
}

Solutions

Enhanced validation
func validatePaymentData(_ payment: PKPayment) throws {
    // Validate payment token
    guard payment.token.paymentData.count > 0 else {
        throw ApplePayValidationException("Payment token is missing or invalid")
    }
    
    guard !payment.token.transactionIdentifier.isEmpty else {
        throw ApplePayValidationException("Transaction identifier is missing")
    }
    
    // Validate contact information if required
    if let billingContact = payment.billingContact {
        guard billingContact.postalAddress != nil else {
            throw ApplePayValidationException("Billing address is required but missing")
        }
    }
    
    // Validate payment method
    guard payment.paymentMethod.network != nil else {
        throw ApplePayValidationException("Payment network information is missing")
    }
}

func processPaymentWithValidation(_ payment: PKPayment) async throws -> BaseTransactionResponse {
    // Validate payment data
    try validatePaymentData(payment)
    
    // Create transaction request
    let transactionRequest = createTransactionRequest(from: payment)
    
    // Process with Unity Service
    return try await unityService.transactionAsync(transactionRequest)
}
Network error handling
func processPaymentWithNetworkRetry(_ payment: PKPayment) async throws -> BaseTransactionResponse {
    let maxRetries = 2
    var lastError: Error?
    
    for attempt in 1...maxRetries {
        do {
            return try await processPayment(payment)
        } catch let error as ApplePayHttpRequestFailedException {
            print("Payment attempt \(attempt) failed with HTTP error: \(error)")
            lastError = error
            
            // Check if retryable
            if isRetryableHttpError(error) && attempt < maxRetries {
                let delay = TimeInterval(attempt) // Progressive delay
                try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
                continue
            }
            
            throw error
        } catch {
            print("Payment attempt \(attempt) failed: \(error)")
            throw error
        }
    }
    
    throw lastError ?? ApplePayPaymentFailedException()
}

func isRetryableHttpError(_ error: ApplePayHttpRequestFailedException) -> Bool {
    let retryableStatusCodes = [408, 429, 500, 502, 503, 504]
    return retryableStatusCodes.contains(error.statusCode ?? 0)
}

Validation errors

The symptoms of this are:

  • Payment requests fail before reaching Apple Pay.
  • Validation exception messages are displayed.
  • The payment flow doesn't start.

Diagnostic steps

func diagnoseValidationIssues() {
    let paymentRequest = ApplePayPaymentRequest(
        merchantIdentifier: "merchant.com.example.store",
        countryCode: "US",
        currencyCode: "USD",
        supportedNetworks: [.visa, .masterCard],
        merchantCapabilities: [.supports3DS],
        total: ApplePayPaymentSummaryItem(
            amount: Decimal(string: "10.00") ?? 0,
            type: .final,
            label: "Test Payment"
        )
    )
    
    // Test individual validation steps
    print("=== Payment Request Validation ===")
    
    // Basic fields validation
    print("Merchant ID: \(paymentRequest.merchantIdentifier.isEmpty ? "❌ Empty" : "✅ Valid")")
    print("Country Code: \(ApplePayValidator.isValidCountryCode(paymentRequest.countryCode) ? "✅ Valid" : "❌ Invalid")")
    print("Currency Code: \(ApplePayValidator.isValidCurrencyCode(paymentRequest.currencyCode) ? "✅ Valid" : "❌ Invalid")")
    print("Total Amount: \(paymentRequest.total?.amount.isNegative == false ? "✅ Valid" : "❌ Negative or nil")")
    
    // Networks and capabilities
    print("Supported Networks: \(paymentRequest.supportedNetworks.isEmpty ? "❌ Empty" : "✅ Valid")")
    print("Merchant Capabilities: \(paymentRequest.merchantCapabilities.isEmpty ? "❌ Empty" : "✅ Valid")")
    
    // Run full validation
    if ApplePayValidationService.validatePaymentRequest(paymentRequest) {
        print("✅ Overall validation: PASSED")
    } else {
        print("❌ Overall validation: FAILED")
    }
}

Solutions

Comprehensive validation helper
struct PaymentRequestValidator {
    static func createValidatedPaymentRequest(
        merchantId: String,
        amount: String,
        currency: String = "USD",
        country: String = "US",
        description: String = "Purchase"
    ) throws -> ApplePayPaymentRequest {
        
        // Validate merchant ID
        guard !merchantId.isEmpty && merchantId.hasPrefix("merchant.") else {
            throw ApplePayValidationException("Invalid merchant ID format")
        }
        
        // Validate amount
        guard let decimalAmount = Decimal(string: amount), !decimalAmount.isNegative else {
            throw ApplePayValidationException("Invalid payment amount")
        }
        
        // Validate currency
        guard ApplePayValidator.isValidCurrencyCode(currency) else {
            throw ApplePayValidationException("Invalid currency code: \(currency)")
        }
        
        // Validate country
        guard ApplePayValidator.isValidCountryCode(country) else {
            throw ApplePayValidationException("Invalid country code: \(country)")
        }
        
        return ApplePayPaymentRequest(
            merchantIdentifier: merchantId,
            countryCode: country,
            currencyCode: currency,
            supportedNetworks: [.visa, .masterCard, .amex],
            merchantCapabilities: [.supports3DS, .supportsEMV],
            total: ApplePayPaymentSummaryItem(
                amount: decimalAmount,
                type: .final,
                label: description
            )
        )
    }
}

// Usage
do {
    let paymentRequest = try PaymentRequestValidator.createValidatedPaymentRequest(
        merchantId: "merchant.com.example.store",
        amount: "29.99",
        description: "Product Purchase"
    )
    applePayService.startPayment(with: paymentRequest)
} catch {
    handleValidationError(error)
}
Real-time validation feedback
func validatePaymentRequestFields(
    merchantId: String?,
    amount: String?,
    currency: String?,
    country: String?
) -> [ValidationError] {
    var errors: [ValidationError] = []
    
    // Merchant ID validation
    if let merchantId = merchantId {
        if merchantId.isEmpty {
            errors.append(ValidationError(field: "merchantId", message: "Merchant ID cannot be empty"))
        } else if !merchantId.hasPrefix("merchant.") {
            errors.append(ValidationError(field: "merchantId", message: "Merchant ID must start with 'merchant.'"))
        }
    } else {
        errors.append(ValidationError(field: "merchantId", message: "Merchant ID is required"))
    }
    
    // Amount validation
    if let amount = amount {
        if amount.isEmpty {
            errors.append(ValidationError(field: "amount", message: "Amount cannot be empty"))
        } else if Decimal(string: amount) == nil {
            errors.append(ValidationError(field: "amount", message: "Invalid amount format"))
        } else if let decimal = Decimal(string: amount), decimal.isNegative {
            errors.append(ValidationError(field: "amount", message: "Amount cannot be negative"))
        }
    } else {
        errors.append(ValidationError(field: "amount", message: "Amount is required"))
    }
    
    // Currency validation
    if let currency = currency {
        if !ApplePayValidator.isValidCurrencyCode(currency) {
            errors.append(ValidationError(field: "currency", message: "Invalid currency code"))
        }
    } else {
        errors.append(ValidationError(field: "currency", message: "Currency is required"))
    }
    
    // Country validation
    if let country = country {
        if !ApplePayValidator.isValidCountryCode(country) {
            errors.append(ValidationError(field: "country", message: "Invalid country code"))
        }
    } else {
        errors.append(ValidationError(field: "country", message: "Country is required"))
    }
    
    return errors
}

struct ValidationError {
    let field: String
    let message: String
}

Debugging tools and techniques

To get more detailed information about errors, use this debugging setup:

// Enable comprehensive logging
extension ApplePayButtonComponent {
    func enableDebugMode() {
        print("Apple Pay Debug Mode Enabled")
        logEnvironmentInfo()
    }
    
    private func logEnvironmentInfo() {
        print("=== Apple Pay Environment ===")
        print("Device Model: \(UIDevice.current.model)")
        print("iOS Version: \(UIDevice.current.systemVersion)")
        print("Bundle ID: \(Bundle.main.bundleIdentifier ?? "Unknown")")
        print("Apple Pay Available: \(PKPaymentAuthorizationController.canMakePayments())")
        
        // Check supported networks
        let networks: [PKPaymentNetwork] = [.visa, .masterCard, .amex, .discover]
        print("Supported Networks Available: \(PKPaymentAuthorizationController.canMakePayments(usingNetworks: networks))")
        
        // Check entitlements
        if let entitlements = Bundle.main.entitlements,
           let merchantIds = entitlements["com.apple.developer.in-app-payments"] as? [String] {
            print("Merchant IDs in Entitlements: \(merchantIds)")
        } else {
            print("Apple Pay entitlements not found")
        }
    }
}

// Debug helper class
class ApplePayDebugger {
    static func logPaymentRequest(_ request: ApplePayPaymentRequest) {
        print("=== Payment Request Debug ===")
        print("Merchant ID: \(request.merchantIdentifier)")
        print("Country: \(request.countryCode)")
        print("Currency: \(request.currencyCode)")
        print("Total: \(request.total?.amount ?? 0) \(request.currencyCode)")
        print("Networks: \(request.supportedNetworks)")
        print("Capabilities: \(request.merchantCapabilities)")
        
        if let lineItems = request.lineItems {
            print("Line Items:")
            for item in lineItems {
                print("  - \(item.label): \(item.amount)")
            }
        }
    }
    
    static func logPaymentResult(_ result: ApplePayPaymentResult) {
        print("=== Payment Result Debug ===")
        print("Payment Method: \(result.data.paymentMethod)")
        print("Token Present: \(result.data.token.paymentData.count > 0)")
        print("Transaction ID: \(result.data.token.transactionIdentifier)")
        
        if let billingContact = result.data.billingContact {
            print("Billing Contact Present: Yes")
            print("Billing Country: \(billingContact.postalAddress?.country ?? "Unknown")")
        } else {
            print("Billing Contact Present: No")
        }
        
        if let shippingContact = result.data.shippingContact {
            print("Shipping Contact Present: Yes")
            print("Shipping Country: \(shippingContact.postalAddress?.country ?? "Unknown")")
        } else {
            print("Shipping Contact Present: No")
        }
    }
}

// Usage in your implementation
let applePayComponent = try ApplePayButtonComponent(config: checkoutConfig, componentConfig: applePayConfig)
applePayComponent.enableDebugMode()

// In your payment flow
ApplePayDebugger.logPaymentRequest(paymentRequest)

Prevention and best practices

Proactive error prevention

// Pre-flight checks before initialising Apple Pay
func performPreflightChecks() async -> Bool {
    let checks: [(String, () async -> Bool, String)] = [
        ("Apple Pay Support", {
            PKPaymentAuthorizationController.canMakePayments()
        }, "Apple Pay is not supported on this device"),
        
        ("Payment Networks", {
            let networks: [PKPaymentNetwork] = [.visa, .masterCard, .amex]
            return PKPaymentAuthorizationController.canMakePayments(usingNetworks: networks)
        }, "No supported payment networks available"),
        
        ("Merchant Configuration", {
            return self.validateMerchantConfiguration()
        }, "Merchant configuration is invalid"),
        
        ("iOS Version", {
            if #available(iOS 11.0, *) {
                return true
            } else {
                return false
            }
        }, "iOS 11.0 or later required"),
        
        ("Network Connectivity", {
            return await self.checkNetworkConnectivity()
        }, "Network connection required")
    ]
    
    var allPassed = true
    print("=== Apple Pay Pre-flight Checks ===")
    
    for (name, test, failureMessage) in checks {
        let passed = await test()
        print("\(passed ? "✅" : "❌") \(name): \(passed ? "PASSED" : failureMessage)")
        if !passed {
            allPassed = false
        }
    }
    
    return allPassed
}

private func validateMerchantConfiguration() -> Bool {
    guard let merchantId = checkoutConfig.session.allowedFundingTypes.wallets?.applePay?.merchantId,
          !merchantId.isEmpty,
          merchantId.hasPrefix("merchant.") else {
        return false
    }
    
    // Check entitlements
    guard let entitlements = Bundle.main.entitlements,
          let merchantIds = entitlements["com.apple.developer.in-app-payments"] as? [String],
          merchantIds.contains(merchantId) else {
        return false
    }
    
    return true
}

private func checkNetworkConnectivity() async -> Bool {
    // Simple connectivity check
    guard let url = URL(string: "https://apple.com") else { return false }
    
    do {
        let (_, response) = try await URLSession.shared.data(from: url)
        return (response as? HTTPURLResponse)?.statusCode == 200
    } catch {
        return false
    }
}

// Use before initialisation
func initializeApplePay() async {
    let preflightPassed = await performPreflightChecks()
    
    if preflightPassed {
        // Safe to initialize Apple Pay
        do {
            let applePayComponent = try ApplePayButtonComponent(
                config: checkoutConfig,
                componentConfig: applePayConfig
            )
            showApplePayButton(applePayComponent)
        } catch {
            showFallbackPaymentOptions()
        }
    } else {
        // Show alternative payment methods
        showFallbackPaymentOptions()
    }
}

Monitoring and alerting

// Error monitoring setup
class ApplePayErrorMonitor {
    static func captureError(_ error: Error, context: [String: Any] = [:]) {
        var errorData: [String: Any] = [
            "error_message": error.localizedDescription,
            "error_type": String(describing: type(of: error)),
            "device_model": UIDevice.current.model,
            "ios_version": UIDevice.current.systemVersion,
            "bundle_id": Bundle.main.bundleIdentifier ?? "Unknown",
            "timestamp": ISO8601DateFormatter().string(from: Date())
        ]
        
        // Add context
        errorData.merge(context) { _, new in new }
        
        // Add error-specific data
        if let sdkError = error as? BaseSdkException {
            errorData["error_code"] = sdkError.userInfo["errorCode"]
            errorData["sdk_error"] = true
        }
        
        // Send to your error tracking service
        print("📊 Error captured: \(errorData)")
        // Example: Crashlytics.record(error: error, userInfo: errorData)
    }
    
    static func trackApplePayEvent(_ eventType: String, data: [String: Any] = [:]) {
        var eventData = data
        eventData["event_type"] = eventType
        eventData["timestamp"] = ISO8601DateFormatter().string(from: Date())
        eventData["component"] = "ApplePay"
        
        print("📈 Event tracked: \(eventData)")
        // Send to your analytics service
    }
}

// Integration with Apple Pay component
let config = ApplePayButtonComponentConfig()

config.onError = { error in
    ApplePayErrorMonitor.captureError(error, context: [
        "flow_step": "payment_authorization",
        "merchant_id": merchantId
    ])
}

config.onPostAuthorisation = { result in
    ApplePayErrorMonitor.trackApplePayEvent("payment_completed", data: [
        "transaction_state": result.state,
        "transaction_id": result.transactionId ?? "none"
    ])
}

config.onCancel = { error in
    ApplePayErrorMonitor.trackApplePayEvent("payment_cancelled", data: [
        "cancellation_reason": error.localizedDescription
    ])
}

iOS-specific considerations

iOS version compatibility

// Check iOS version for feature availability
func checkiOSFeatureAvailability() {
    print("=== iOS Feature Availability ===")
    
    if #available(iOS 11.0, *) {
        print("✅ Basic Apple Pay (iOS 11.0+)")
    } else {
        print("❌ Apple Pay requires iOS 11.0 or later")
        return
    }
    
    if #available(iOS 15.0, *) {
        print("✅ Coupon codes (iOS 15.0+)")
    } else {
        print("⚠️ Coupon codes not available (requires iOS 15.0+)")
    }
    
    if #available(iOS 16.0, *) {
        print("✅ Multi-merchant payments (iOS 16.0+)")
    } else {
        print("⚠️ Multi-merchant payments not available (requires iOS 16.0+)")
    }
    
    if #available(iOS 17.0, *) {
        print("✅ Disbursements (iOS 17.0+)")
    } else {
        print("⚠️ Disbursements not available (requires iOS 17.0+)")
    }
}

Device-specific limitations

func checkDeviceLimitations() {
    print("=== Device limitations ===")
    
    // Check if device supports Apple Pay
    if PKPaymentAuthorizationController.canMakePayments() {
        print("✅ Device supports Apple Pay")
    } else {
        print("❌ Device does not support Apple Pay")
        return
    }
    
    // Check available payment networks
    let allNetworks: [PKPaymentNetwork] = [.visa, .masterCard, .amex, .discover, .maestro, .chinaUnionPay]
    let availableNetworks = allNetworks.filter { network in
        PKPaymentAuthorizationController.canMakePayments(usingNetworks: [network])
    }
    
    print("Available payment networks: \(availableNetworks)")
    
    if availableNetworks.isEmpty {
        print("⚠️ No payment cards configured in Wallet")
    }
}