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-KEYon 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
- Enroll in the Apple Developer Program.
- Create an Apple Pay Merchant ID.
- Create a Payment Processing Certificate and configure it per VenPays / processor guidance.
- Enable the Apple Pay capability on the iOS App ID.
- 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).
- Signing & Capabilities → add Apple Pay.
- Select the Merchant ID that matches
apple_pay.merchant_identifierfrom VenPays initiation. - 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
- File → Add Package Dependencies…
- Paste the VenPays iOS package URL provided by VenPays
- Choose Up to Next Minor Version or Exact
- 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
| Status | Meaning | Typical app action |
|---|---|---|
succeeded | Payment completed | Show success; confirm with backend/webhook |
failed | Payment failed / declined | Show failure |
cancelled | Cancelled at payment layer | Show cancelled / allow retry |
processing | Accepted; may still be settling | Show pending; reconcile by trackID |
unknown | Recovery exhausted without a final status | Reconcile 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.
| Code | Meaning | Typical next step |
|---|---|---|
sessionExpired | Native token expired | Re-initiate on backend |
applePayUnsupported | Device cannot use Apple Pay | Hide Apple Pay |
noSupportedCard | No Wallet card for required networks | Prompt user to add a card |
invalidApplePayConfiguration | Merchant ID / networks / capabilities | Fix Apple / initiation config |
presentationFailed | Sheet failed to present | Retry / check device state |
paymentCancelled | User cancelled | No automatic retry |
invalidApplePayToken | Token / paymentData invalid | Investigate Apple Pay config |
unauthorized | Invalid native session | Re-initiate |
processorDeclined | Declined | Show decline |
processorUnavailable | Processor unavailable | Retry later |
requestTimeout | Timeout | Reconcile / recover by track ID |
networkUnavailable | Network / transport failure | Retry / recover |
paymentStatusUnknown | Recovery exhausted | Reconcile 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 data | Allowed location |
|---|---|
Merchant X-API-KEY | Merchant backend only |
| Native session token | App memory / SDK only — never logs |
| Amount and currency | Established server-side at initiation |
Apple payment_data | Device → VenPays authorize only |
| Payment status | VenPays + 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
.unknownand.processingas 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_identifierfrom initiation - [ ] Payment Processing certificate configured with VenPays / processor
- [ ] Initiation uses
integration_type: native_iosand returns a valid native session - [ ] Availability check returns
.availableon 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, andpaymentCancelled - [ ] 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