# 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:

| Exception | Error code | Description | Prevention |
|  --- | --- | --- | --- |
| `ApplePayPaymentRequestApiNotSupportedException` | `SDK0602` | Apple Pay is not supported on this device or iOS version. | Check device compatibility and iOS version before initialisation. |
| `ApplePaySessionCancelledException` | `SDK0615` | The Apple Pay session was cancelled by the user. | Handle gracefully as this is expected user behaviour. |
| `ApplePayMerchantValidationFailedException` | `SDK0616` | The Apple Pay merchant validation failed. | Verify your merchant configuration and certificates. |
| `ApplePayCustomApiProcessingFailedException` | `SDK0617` | Custom API processing failed during Apple Pay payment. | Check network connectivity and API configuration. |
| `ApplePayValidationException` | `SDK0501` | The payment request validation failed. | Validate all required fields before submission. |
| `ApplePayPaymentFailedException` | `SDK0303` | The Apple Pay payment processing failed. | Implement retry logic and proper error handling. |
| `ApplePayMerchantIdentifierRequiredException` | Custom | The merchant identifier is missing from the configuration. | Ensure merchant identifier is properly configured. |
| `ApplePayPaymentRequestNotInitializedException` | Custom | The payment request was not properly initialised. | Validate payment request before starting payment flow. |
| `ApplePayHttpRequestFailedException` | Custom | HTTP request failed during payment processing. | Check network connectivity and server availability. |
| `ApplePayBlobDecryptionFailedException` | Custom | Failed to decrypt Apple Pay payment token. | Verify merchant certificates and configuration. |
| `ApplePayDisbursementRequestNotSupportedException` | Custom | Disbursement 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

```swift
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

```swift
// 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

```swift
// 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 initialise 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

```swift
// 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

```swift
// 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

```swift
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

```swift
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

```swift
// 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

```swift
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

```swift
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

```swift
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

```swift
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

```swift
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:

```swift
// 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

```swift
// 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 initialise Apple Pay
        do {
            let applePayComponent = try ApplePayButtonComponent(
                config: checkoutConfig,
                componentConfig: applePayConfig
            )
            showApplePayButton(applePayComponent)
        } catch {
            showFallbackPaymentOptions()
        }
    } else {
        // Show alternative payment methods
        showFallbackPaymentOptions()
    }
}
```

### Monitoring and alerting

```swift
// 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

```swift
// 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

```swift
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")
    }
}
```