{"version":3,"file":"airwallex-apple-pay-adapter-CPz54c9e.mjs","names":["paymentRequest: ApplePayJS.ApplePayPaymentRequest","token: ApplePayEncryptedToken"],"sources":["../src/payment-methods/airwallex-apple-pay-adapter.ts"],"sourcesContent":["/**\n * Airwallex Apple Pay Adapter\n *\n * Uses native ApplePaySession API directly (not Stripe.js) to show the payment sheet\n * and extract the encrypted payment token for Airwallex processing.\n *\n * Flow:\n * 1. initialize() - Checks if ApplePaySession is available\n * 2. canMakePayment() - Checks if user can pay with Apple Pay\n * 3. showPaymentSheet() - Shows Apple Pay sheet with merchant validation callback\n *\n * The key difference from Stripe's approach is that we use the native\n * ApplePaySession API and handle merchant validation via Airwallex's\n * payment_session/start API which validates with Apple.\n *\n * Mock scenarios are supported for E2E testing without real Apple Pay.\n */\n\n/// <reference types=\"@types/applepayjs\" />\n\nexport enum AirwallexApplePayMockScenario {\n  None = \"none\",\n  Success = \"success\",\n  Cancelled = \"cancelled\",\n}\n\n// Apple Pay token structure from payment.token\nexport interface ApplePayEncryptedToken {\n  paymentData: {\n    version?: string;\n    data: string;\n    signature: string;\n    header: {\n      ephemeralPublicKey: string;\n      publicKeyHash: string;\n      transactionId: string;\n    };\n  };\n  paymentMethod: {\n    displayName: string;\n    network: string;\n    type: string;\n  };\n  transactionIdentifier: string;\n}\n\nexport interface AirwallexApplePayConfig {\n  amount: number; // Amount in atomic units (cents)\n  currency: string; // e.g., \"USD\"\n  country: string; // e.g., \"US\"\n  merchantName?: string; // Display name on Apple Pay sheet\n}\n\nexport type AirwallexApplePayShowResult =\n  | {\n      success: true;\n      token: ApplePayEncryptedToken;\n      payerEmail?: string;\n      payerFirstName?: string;\n      payerLastName?: string;\n      complete: (status: \"success\" | \"fail\") => void;\n    }\n  | { success: false; cancelled: true }\n  | { success: false; error: string };\n\n// Merchant validation callback type - called during onvalidatemerchant\nexport type MerchantValidationCallback = (\n  validationUrl: string,\n) => Promise<{ merchantSession?: object; error?: string }>;\n\nexport class AirwallexApplePayAdapter {\n  private config: AirwallexApplePayConfig | null = null;\n  private mockScenario: AirwallexApplePayMockScenario;\n\n  constructor(mockScenario?: AirwallexApplePayMockScenario) {\n    this.mockScenario = mockScenario ?? AirwallexApplePayMockScenario.None;\n  }\n\n  /**\n   * Initialize the adapter with payment configuration.\n   * Returns true if Apple Pay is available on this device.\n   */\n  initialize(config: AirwallexApplePayConfig): boolean {\n    // Mock scenarios bypass real Apple Pay initialization\n    if (this.mockScenario !== AirwallexApplePayMockScenario.None) {\n      console.log(\"[MockApplePay:Airwallex] initialize called (mock mode)\");\n      this.config = config;\n      return true;\n    }\n\n    // Check if ApplePaySession is available (Safari on iOS/macOS only)\n    if (typeof window === \"undefined\" || !window.ApplePaySession) {\n      console.error(\"[ApplePay:Airwallex] ApplePaySession not available (not Safari or unsupported device)\");\n      return false;\n    }\n\n    this.config = config;\n    return true;\n  }\n\n  /**\n   * Check if the user can make a payment with Apple Pay.\n   * Returns true if Apple Pay is set up on this device.\n   */\n  async canMakePayment(): Promise<boolean> {\n    // Mock scenarios always return true\n    if (this.mockScenario !== AirwallexApplePayMockScenario.None) {\n      console.log(\"[MockApplePay:Airwallex] canMakePayment: true (mock mode)\");\n      return true;\n    }\n\n    if (!window.ApplePaySession) {\n      return false;\n    }\n\n    try {\n      const canMake = window.ApplePaySession.canMakePayments();\n      console.log(\"[ApplePay:Airwallex] canMakePayments:\", canMake);\n      return canMake;\n    } catch (error) {\n      console.error(\"[ApplePay:Airwallex] canMakePayment error:\", error);\n      return false;\n    }\n  }\n\n  /**\n   * Convert atomic units (cents) to display format for Apple Pay.\n   * Apple Pay expects a string like \"10.00\" for $10.00.\n   */\n  private formatAmount(amountAtom: number, currency: string): string {\n    // Most currencies use 2 decimal places, some like JPY use 0\n    const zeroDecimalCurrencies = [\n      \"BIF\",\n      \"CLP\",\n      \"DJF\",\n      \"GNF\",\n      \"ISK\",\n      \"JPY\",\n      \"KMF\",\n      \"KRW\",\n      \"PYG\",\n      \"RWF\",\n      \"UGX\",\n      \"VND\",\n      \"VUV\",\n      \"XAF\",\n      \"XOF\",\n      \"XPF\",\n    ];\n    const isZeroDecimal = zeroDecimalCurrencies.includes(currency.toUpperCase());\n\n    if (isZeroDecimal) {\n      return amountAtom.toString();\n    }\n\n    return (amountAtom / 100).toFixed(2);\n  }\n\n  /**\n   * Show the Apple Pay payment sheet and return the encrypted token.\n   *\n   * This uses the native ApplePaySession API with a two-step flow:\n   * 1. onvalidatemerchant - Called by Apple to validate the merchant\n   *    The callback should call our backend which calls Airwallex's payment_session/start\n   * 2. onpaymentauthorized - Called when user authorizes with Face ID/Touch ID\n   *    Returns the encrypted token for processing\n   *\n   * @param onMerchantValidation - Callback to validate merchant with backend\n   * @returns Promise with token or error\n   */\n  async showPaymentSheet(onMerchantValidation: MerchantValidationCallback): Promise<AirwallexApplePayShowResult> {\n    // Handle mock scenarios\n    if (this.mockScenario === AirwallexApplePayMockScenario.Success) {\n      console.log(\"[MockApplePay:Airwallex] showPaymentSheet: returning mock success token\");\n      const mockToken: ApplePayEncryptedToken = {\n        paymentData: {\n          version: \"EC_v1\",\n          data: \"MOCK_ENCRYPTED_DATA\",\n          signature: \"MOCK_SIGNATURE\",\n          header: {\n            ephemeralPublicKey: \"MOCK_PUBLIC_KEY\",\n            publicKeyHash: \"MOCK_HASH\",\n            transactionId: \"mock_txn_123\",\n          },\n        },\n        paymentMethod: {\n          displayName: \"Visa 4242\",\n          network: \"Visa\",\n          type: \"debit\",\n        },\n        transactionIdentifier: \"MOCK_TXN_ID\",\n      };\n      return {\n        success: true,\n        token: mockToken,\n        payerEmail: \"mock@example.com\",\n        payerFirstName: \"Mock\",\n        payerLastName: \"User\",\n        complete: (status) => console.log(`[MockApplePay:Airwallex] complete: ${status}`),\n      };\n    }\n\n    if (this.mockScenario === AirwallexApplePayMockScenario.Cancelled) {\n      console.log(\"[MockApplePay:Airwallex] showPaymentSheet: returning mock cancelled\");\n      return { success: false, cancelled: true };\n    }\n\n    // Real Apple Pay flow\n    if (!this.config) {\n      return { success: false, error: \"Apple Pay not initialized\" };\n    }\n\n    if (!window.ApplePaySession) {\n      return { success: false, error: \"ApplePaySession not available\" };\n    }\n\n    const config = this.config;\n    const amount = this.formatAmount(config.amount, config.currency);\n\n    // Create the Apple Pay payment request\n    const paymentRequest: ApplePayJS.ApplePayPaymentRequest = {\n      countryCode: config.country.toUpperCase(),\n      currencyCode: config.currency.toUpperCase(),\n      supportedNetworks: [\"visa\", \"masterCard\", \"amex\", \"discover\"],\n      merchantCapabilities: [\"supports3DS\"],\n      total: {\n        label: config.merchantName || \"Total\",\n        amount: amount,\n        type: \"final\",\n      },\n      // Surface the payer's email and name so the backend can fill missing customer info.\n      requiredBillingContactFields: [\"email\", \"name\"],\n    };\n\n    return new Promise((resolve) => {\n      try {\n        // Create ApplePaySession (version 3 is widely supported)\n        // biome-ignore lint/style/noNonNullAssertion: ApplePaySession is guaranteed to exist (checked in canMakePayments)\n        const session = new window.ApplePaySession!(3, paymentRequest);\n\n        // Capture status constants at creation time (before optional chaining could yield undefined)\n        // biome-ignore lint/style/noNonNullAssertion: ApplePaySession existence verified above\n        const STATUS_SUCCESS = window.ApplePaySession!.STATUS_SUCCESS;\n        // biome-ignore lint/style/noNonNullAssertion: ApplePaySession existence verified above\n        const STATUS_FAILURE = window.ApplePaySession!.STATUS_FAILURE;\n\n        // Handle merchant validation\n        session.onvalidatemerchant = async (event: ApplePayJS.ApplePayValidateMerchantEvent) => {\n          console.log(\"[ApplePay:Airwallex] onvalidatemerchant called\", event.validationURL);\n\n          try {\n            const validationResult = await onMerchantValidation(event.validationURL);\n\n            if (validationResult.error || !validationResult.merchantSession) {\n              console.error(\"[ApplePay:Airwallex] Merchant validation failed:\", validationResult.error);\n              session.abort();\n              resolve({\n                success: false,\n                error: validationResult.error || \"Merchant validation failed\",\n              });\n              return;\n            }\n\n            // Complete merchant validation with the session from Apple\n            session.completeMerchantValidation(validationResult.merchantSession);\n            console.log(\"[ApplePay:Airwallex] Merchant validation completed\");\n          } catch (error) {\n            console.error(\"[ApplePay:Airwallex] Merchant validation error:\", error);\n            session.abort();\n            resolve({\n              success: false,\n              error: `Merchant validation error: ${error}`,\n            });\n          }\n        };\n\n        // Handle payment authorization (user approved with Face ID/Touch ID)\n        session.onpaymentauthorized = (event: ApplePayJS.ApplePayPaymentAuthorizedEvent) => {\n          console.log(\"[ApplePay:Airwallex] onpaymentauthorized called\");\n\n          // Extract the encrypted token\n          const payment = event.payment;\n          const token: ApplePayEncryptedToken = {\n            paymentData: payment.token.paymentData as ApplePayEncryptedToken[\"paymentData\"],\n            paymentMethod: {\n              displayName: payment.token.paymentMethod.displayName,\n              network: payment.token.paymentMethod.network,\n              type: payment.token.paymentMethod.type,\n            },\n            transactionIdentifier: payment.token.transactionIdentifier,\n          };\n\n          const payerEmail = payment.billingContact?.emailAddress ?? payment.shippingContact?.emailAddress ?? undefined;\n          const payerFirstName = payment.billingContact?.givenName ?? payment.shippingContact?.givenName ?? undefined;\n          const payerLastName = payment.billingContact?.familyName ?? payment.shippingContact?.familyName ?? undefined;\n\n          // Create the complete callback that the caller will use after confirming\n          const complete = (status: \"success\" | \"fail\") => {\n            console.log(\"[ApplePay:Airwallex] completing session with status:\", status);\n            const appleStatus = status === \"success\" ? STATUS_SUCCESS : STATUS_FAILURE;\n            session.completePayment({ status: appleStatus });\n          };\n\n          resolve({\n            success: true,\n            token,\n            payerEmail,\n            payerFirstName,\n            payerLastName,\n            complete,\n          });\n        };\n\n        // Handle cancellation\n        session.oncancel = () => {\n          console.log(\"[ApplePay:Airwallex] oncancel - user cancelled\");\n          resolve({ success: false, cancelled: true });\n        };\n\n        // Start the session - this shows the Apple Pay sheet\n        console.log(\"[ApplePay:Airwallex] Starting Apple Pay session\");\n        session.begin();\n      } catch (error) {\n        console.error(\"[ApplePay:Airwallex] Session creation error:\", error);\n        resolve({\n          success: false,\n          error: `Failed to create Apple Pay session: ${error}`,\n        });\n      }\n    });\n  }\n}\n"],"mappings":"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"}