Payments

Integrate Native Apple Pay (iOS)

Public merchant guide for VenPaysApplePay — the native iOS Apple Pay SDK.

Use this document when publishing integration docs for merchants. It covers setup, backend initiation, app integration, results, security, testing, and regional availability.

Approved merchants

Native iOS Apple Pay is available to approved merchants. Package version: 0.1.0-rc.1. Payment Engine base URL: https://merchant.venpays.com. Contact VenPays to enable native Apple Pay for your account.

Related: Initiate payment · Webhooks · SDK overview


Overview

VenPaysApplePay presents Apple Pay with PassKit, sends the Apple payment token to VenPays for authorization, and returns a structured payment result to your app.

What the SDK does

  • Checks Apple Pay availability for the trusted session
  • Presents the Apple Pay sheet after a user tap
  • Authorizes with VenPays using a native session Bearer token
  • Recovers payment status when authorization is processing or uncertain

What the SDK does not do

  • Call POST /merchant/initiate-payment (that is server-to-server only)
  • Accept a merchant X-API-KEY on the device
  • Open browser redirect URLs (success_url / fail_url)
  • Override amount, currency, Merchant ID, or summary totals from the app
  • Create fake Apple Pay buttons or modify Apple’s sheet Cancel button
  • Call Apple web merchant-validation APIs or use initiativeContext

Requirements

  • iOS 15.0+
  • Xcode with an iOS SDK (this package is iOS-only; do not build for My Mac)
  • Apple Pay–capable physical device for real authorization
  • Apple Developer Merchant ID + Apple Pay capability on the app
  • Merchant backend that initiates payments with VenPays
  • VenPays native Apple Pay APIs enabled for your merchant

Architecture

Image placeholder

Architecture flow diagram — show the end-to-end path: Merchant iOS app → Merchant backend → VenPays POST /merchant/initiate-payment (X-API-KEY, server-to-server) → backend returns native session JSON to the app → VenPaysApplePay SDK → Apple Pay sheet (PassKit) → VenPays POST .../authorize → Processor → optional GET status recovery → app receives VenPaysPaymentResult or VenPaysError → merchant backend receives webhooks / reconciles by track_id.

Merchant iOS app
    → Merchant backend
    → VenPays POST /merchant/initiate-payment   (X-API-KEY, server-to-server only)
    → Merchant backend returns native session JSON to the app
    → VenPaysApplePay SDK
    → Apple Pay sheet (PassKit)
    → VenPays POST /v1/sdk/apple-pay/payments/{track_id}/authorize
    → Processor
    → Optional GET status recovery while the native session is valid
    → App receives VenPaysPaymentResult or VenPaysError
    → Merchant backend receives webhooks / reconciles by track_id

Return to the app: There is no redirect. The Apple Pay sheet is presented in-process. When the sheet finishes, presentApplePay completes with a result or error. Your app updates UI from that outcome. Order fulfillment should still rely on your backend and VenPays webhooks.

About success_url / fail_url

These optional initiation fields are for web/redirect flows. They are accepted by VenPays initiation but do not control native iOS navigation. The iOS SDK ignores them.


Regional availability

VenPaysApplePay is distributed as a reusable iOS SDK.

Apple Pay payment availability depends on:

  • Apple Pay availability in the merchant's country or region
  • Participating card issuers and networks
  • Merchant Apple Developer configuration
  • Apple Pay entitlement and Merchant ID configuration
  • Payment Processing certificate configuration
  • VenPays and acquiring-bank activation
  • Processor support for the transaction currency and card network

The current release has been designed and initially tested against Bahrain merchant configuration using BHD, Visa, Mastercard, and 3-D Secure. Other markets require validation before production use.


Apple Developer and app setup

Apple Developer

  1. Enroll in the Apple Developer Program.
  2. Create an Apple Pay Merchant ID.
  3. Create a Payment Processing Certificate and configure it per VenPays / processor guidance.
  4. Enable the Apple Pay capability on the iOS App ID.
  5. Regenerate provisioning profiles after enabling the capability.

Xcode project

Image placeholder

Xcode Signing & Capabilities — screenshot of an iOS target with the Apple Pay capability enabled and the Merchant ID selected (matching apple_pay.merchant_identifier from VenPays initiation).

  1. Signing & Capabilities → add Apple Pay.
  2. Select the Merchant ID that matches apple_pay.merchant_identifier from VenPays initiation.
  3. Ensure Bundle ID and Team match the App ID with Apple Pay enabled.

The host merchant app configures its own:

  • Apple Developer Team
  • Bundle ID
  • Apple Pay capability
  • Merchant ID
  • Provisioning profile

The SDK package does not change those settings.

Device requirements

  • Physical iPhone with Apple Pay support
  • Region and card support for your networks (for example Visa / Mastercard)
  • Face ID / Touch ID / passcode configured

Simulator

The iOS Simulator cannot fully validate Apple Pay authorization. Treat simulator checks as UI-only. Full payment flows require a physical device.


Installation

Add the VenPays private Swift package (organization access required).

Xcode

  1. File → Add Package Dependencies…
  2. Paste the VenPays iOS package URL provided by VenPays
  3. Choose Up to Next Minor Version or Exact
  4. Select 0.1.0-rc.1

Image placeholder

Add Package Dependencies — Xcode dialog showing the VenPays iOS package and version 0.1.0-rc.1 selected.

Package.swift

Install the VenPays iOS SDK using the distribution method and package URL provided by VenPays (organization access required). Pin version 0.1.0-rc.1 unless VenPays directs otherwise.

Then:

import VenPaysApplePay

Backend integration

Server-to-server initiation

Only your merchant backend may call:

POST /merchant/initiate-payment
X-API-KEY: <merchant-secret-key>
Content-Type: application/json

Never put the merchant secret on device

Never put the merchant secret X-API-KEY in the iOS application or the SDK.

Example request

{
  "amount": 10.000,
  "currency": "BHD",
  "payment_provider": "apple-pay",
  "integration_type": "native_ios",
  "merchant_reference": "optional-reference",
  "payment_method": "apple_pay",
  "additional_data": {}
}

Optional web-only fields such as success_url and fail_url may be accepted by VenPays but do not drive native iOS navigation.

Native initiation response (returned to the iOS app)

Your backend returns this payload (or an equivalent) to the app. The app never calls initiation directly.

{
  "track_id": "11111111-2222-3333-4444-555555555555",
  "native_session_token": "<opaque-token>",
  "expires_at": "2026-07-26T12:00:00.000Z",
  "amount": "10.000",
  "currency": "BHD",
  "merchant_reference": "optional-reference",
  "apple_pay": {
    "merchant_identifier": "merchant.com.example",
    "merchant_display_name": "Example Merchant",
    "country_code": "BH",
    "currency_code": "BHD",
    "supported_networks": ["visa", "masterCard"],
    "merchant_capabilities": ["threeDSecure"]
  },
  "success": true
}

Amount and currency source of truth

The backend session amount and currency are authoritative. The SDK builds exactly one final Apple Pay summary item:

  • label = apple_pay.merchant_display_name
  • amount = top-level amount
  • type = final

What the SDK calls after Apple Pay authorization

POST /v1/sdk/apple-pay/payments/{track_id}/authorize
Authorization: Bearer <native_session_token>
Idempotency-Key: <uuid>
X-Request-ID: <uuid>
Content-Type: application/json

payment_data is the decoded JSON object from PKPayment.token.paymentData (not a base64 blob of the whole token).

  • HTTP 200 — completed result body
  • HTTP 202 — processing; SDK recovers via status
GET /v1/sdk/apple-pay/payments/{track_id}
Authorization: Bearer <native_session_token>
X-Request-ID: <uuid>

Native session tokens typically expire after about 900 seconds. Status recovery only works while the token is valid.

Use the authenticated Payment Engine status APIs and webhooks from your backend — do not rely on the mobile app alone for fulfillment.

Environments

  • SDK base URL: https://merchant.venpays.com
  • Use a custom base URL only when VenPays provides one for approved testing

iOS integration

1. Create the client

let client = VenPaysApplePayClient(
    configuration: try VenPaysConfiguration(
        environment: .production
        // loggingEnabled: true  // optional diagnostics; never logs tokens
    )
)

No publishable key is required.

2. Obtain a native session from your backend

Your app calls your merchant backend. That backend calls VenPays initiation and returns the native initiation payload.

let session = try JSONDecoder().decode(
    VenPaysNativePaymentSession.self,
    from: responseData
)

Or construct the session from fields your backend already decoded.

3. Check availability

switch client.applePayAvailability(for: session) {
case .available:
    break
case .supportedButNoConfiguredCard:
    // Prompt the user to add a card in Wallet
case .unsupportedDevice:
    // Hide Apple Pay
case .invalidMerchantConfiguration:
    // Fix Merchant ID / networks / capabilities
case .sessionExpired:
    // Re-initiate on your backend
}

4. Present Apple Pay (UIKit)

Call presentation only as a direct result of a user tap on the Apple Pay button:

Image placeholder

In-app Apple Pay button — screenshot of a merchant checkout screen with the system-style Apple Pay button (.buy / black), ready for a user tap. Do not use a custom fake Apple Pay button.

let button = ApplePayButton(type: .buy, style: .black)
button.onTap = { [weak self] in
    guard let self else { return }
    button.isPaymentInProgress = true
    Task {
        defer { button.isPaymentInProgress = false }
        do {
            let result = try await client.presentApplePay(
                session: session,
                from: self
            )
            self.handle(result)
        } catch let error as VenPaysError where error.code == .paymentCancelled {
            // User dismissed the sheet before authorization
        } catch {
            // Handle other VenPaysError codes
        }
    }
}

Image placeholder

Apple Pay payment sheet — PassKit sheet on a physical iPhone showing merchant display name, final amount/currency (BHD example), and Face ID / Touch ID prompt. Caption that the sheet is presented in-process (no browser redirect).

5. SwiftUI

SwiftUIApplePayButton(
    type: .buy,
    style: .automatic,
    isPaymentInProgress: viewModel.isPaying
) {
    Task { await viewModel.pay(from: presenter) }
}

Obtain a UIViewController presenter from the SwiftUI hierarchy when calling presentApplePay.


Results and errors

Payment result statuses

StatusMeaningTypical app action
succeededPayment completedShow success; confirm with backend/webhook
failedPayment failed / declinedShow failure
cancelledCancelled at payment layerShow cancelled / allow retry
processingAccepted; may still be settlingShow pending; reconcile by trackID
unknownRecovery exhausted without a final statusReconcile with your backend using trackID

User cancellation before authorization usually throws VenPaysError with code paymentCancelled rather than returning a result.

Common error codes

Switch on VenPaysError.code, not message text.

CodeMeaningTypical next step
sessionExpiredNative token expiredRe-initiate on backend
applePayUnsupportedDevice cannot use Apple PayHide Apple Pay
noSupportedCardNo Wallet card for required networksPrompt user to add a card
invalidApplePayConfigurationMerchant ID / networks / capabilitiesFix Apple / initiation config
presentationFailedSheet failed to presentRetry / check device state
paymentCancelledUser cancelledNo automatic retry
invalidApplePayTokenToken / paymentData invalidInvestigate Apple Pay config
unauthorizedInvalid native sessionRe-initiate
processorDeclinedDeclinedShow decline
processorUnavailableProcessor unavailableRetry later
requestTimeoutTimeoutReconcile / recover by track ID
networkUnavailableNetwork / transport failureRetry / recover
paymentStatusUnknownRecovery exhaustedReconcile by track ID

VenPaysError.isRetryable indicates whether a transport-level retry may help. After uncertain authorize outcomes, prefer backend reconciliation over assuming failure.


Security rules

Credential or dataAllowed location
Merchant X-API-KEYMerchant backend only
Native session tokenApp memory / SDK only — never logs
Amount and currencyEstablished server-side at initiation
Apple payment_dataDevice → VenPays authorize only
Payment statusVenPays + merchant backend

Rules for merchants:

  • Do not embed merchant secrets in the app
  • Do not override session amount, currency, Merchant ID, or networks in the client
  • Do not log native session tokens, Authorization headers, Apple tokens, signatures, or full authorize bodies
  • Treat .unknown and .processing as reconcile-with-backend states
  • Fulfill orders from trusted backend/webhook confirmation, not from client UI alone

Security reports: security@venpays.com
Support: support@venpays.com


Testing checklist

Before Live traffic:

  • [ ] Apple Pay capability enabled and Merchant ID selected in Xcode
  • [ ] Merchant ID in the app matches apple_pay.merchant_identifier from initiation
  • [ ] Payment Processing certificate configured with VenPays / processor
  • [ ] Initiation uses integration_type: native_ios and returns a valid native session
  • [ ] Availability check returns .available on a physical device with a supported card
  • [ ] User tap presents the Apple Pay sheet
  • [ ] After Face ID / Touch ID, authorize reaches VenPays (POST .../authorize)
  • [ ] App handles succeeded, failed, processing, unknown, and paymentCancelled
  • [ ] Backend webhook / order status updates for the same track_id
  • [ ] No secrets or tokens appear in device logs

Go-live notes

Native iOS Apple Pay is available to approved merchants. Confirm enablement and certificates with VenPays before Live traffic.

When expanding beyond Bahrain (BHD, Visa, Mastercard, 3-D Secure), re-validate with VenPays:

  • Apple Pay regional eligibility
  • Card networks and 3-D Secure behavior
  • Currency and acquirer support
  • End-to-end authorize + webhook confirmation