Skip to content

iOS SDK Integration

The xMoney iOS SDK provides a drop-in Payment Sheet, an embeddable Payment Element, and standalone Apple Pay. All surfaces use PaymentConfig, PaymentIntent, and PaymentResult.

Current release: 1.0.0.

Requirements

  • iOS 15 or later
  • Swift 5.9 or later
  • Xcode 15 or later
  • A publishable xMoney key
  • A backend that creates and signs orders
Do not ship a secret key

Store the xMoney private key on your backend. The app should receive only the publishable key, order payload, and checksum.

1. Install the SDK

Swift Package Manager

Add the package dependency:

.package(
    url: "https://github.com/xMoney-Payments/xmoney-ios.git",
    from: "1.0.0"
)

Link XMoneyPaymentSheet for Payment Sheet. It includes Payment Element and Apple Pay. For a smaller embedded integration, link XMoneyPaymentElement and add XMoneyApplePay only when you need Apple Pay.

CocoaPods

pod 'XMoneyPaymentSheet', '1.0.0'

# Or select individual surfaces:
# pod 'XMoneyPaymentSheet', :subspecs => ['PaymentElement']
# pod 'XMoneyPaymentSheet', :subspecs => ['ApplePay']

CocoaPods exposes all subspecs through import XMoneyPaymentSheet.

2. Create a PaymentIntent

Your backend creates and signs the order. Build an intent from the returned values:

let intent = PaymentIntent(
    orderPayload: OrderPayload(payloadFromYourServer),
    orderChecksum: OrderChecksum(checksumFromYourServer)
)

See Create the order on your backend for the shared server responsibilities.

3. Choose a payment surface

Payment Sheet

Payment Sheet owns the complete checkout UI and Pay button.

import XMoneyPaymentSheet

let configuration = PaymentConfig(
    publicKey: "pk_test_...",
    paymentMethods: .init(
        applePay: .init(enabled: true)
    ),
    card: .init(
        savedCards: .init(enabled: true)
    )
)

let sheet = PaymentSheet(configuration: configuration)

sheet.present(from: self, intent: intent) { result in
    handlePaymentResult(result)
}

Idle close, drag, or scrim dismissal returns .canceled without consuming the order. Once payment or 3DS starts, terminal results consume the signed order.

Payment Element

Payment Element places the payment form directly in your layout. It does not link Apple Pay automatically.

import XMoneyPaymentElement
import XMoneyApplePay

ApplePay.register()

let embedded = EmbeddedPayment(configuration: configuration) { result in
    handlePaymentResult(result)
}

let element = PaymentElement(payment: embedded) { event in
    if case .ready = event {
        hideCheckoutLoader()
    }
}

view.addSubview(element)
try await element.prepare(intent: intent)

Call ApplePay.register() before prepare when the embedded configuration enables Apple Pay. Keep the element mounted while it prepares and while the order changes.

To use your own Pay button, hide the SDK button and call confirm() after the element reports .ready:

let configuration = PaymentConfig(
    publicKey: "pk_test_...",
    card: .init(
        submitButton: .init(visible: false)
    )
)

embedded.confirm()

Gate the merchant button with embedded.isInteractionEnabled.

Standalone Apple Pay

import XMoneyApplePay

let applePay = ApplePay(configuration: configuration) { result in
    handlePaymentResult(result)
}

applePay.present(from: self, intent: intent)

You can also attach present to ApplePayButton or ApplePayButtonView. A dismissal before wallet authorization returns .canceled and does not consume the intent.

Apple Pay setup

  1. Enable Apple Pay in paymentMethods.applePay.
  2. Create a Merchant ID in the Apple Developer portal.
  3. In Xcode, add the Apple Pay capability to your app target and select the Merchant ID.
  4. Make sure the Merchant ID matches the merchantId configured for your xMoney site.
  5. Test on a physical device with an eligible card in Wallet.

See Apple Pay for activation and integration options.

Handle PaymentResult

func handlePaymentResult(_ result: PaymentResult) {
    switch result {
    case .complete(let transaction):
        confirmOrderOnBackend(transaction)
    case .failed(let error):
        showPaymentError(error.message)
    case .canceled:
        showCheckout()
    }
}

Use the result to update the app, then confirm final payment status on your backend before fulfillment.

Update a mounted Payment Element

When the amount, currency, or cart changes, request a new signed order and rebind the mounted element:

try await embedded.updateOrder(intent: nextIntent)

Do not replace the element with a loader. Interaction is disabled until the new intent is ready. A complete, failed, or post-submit canceled result consumes the current intent; prepare a new one before another attempt.

Configuration

PaymentConfig controls saved cards, card validation, field grouping, submit-button intent, locale, light/dark style, colors, and radii.

let configuration = PaymentConfig(
    publicKey: "pk_test_...",
    card: .init(
        savedCards: .init(enabled: true, optInVisible: true),
        validationMode: .onTouched,
        inputs: .init(grouping: .condensed)
    ),
    options: .init(
        locale: "en-US",
        style: .automatic
    )
)

Supported UI languages are English (en), Greek (el), Romanian (ro), Bulgarian (bg), Hungarian (hu), and Polish (pl). Region tags such as pl-PL are supported.

For cardholder name verification, provide CardHolderVerification in the card configuration and return true to continue:

card: .init(
    cardHolderVerification: CardHolderVerification(
        name: CardHolderName(firstName: "John", lastName: "Doe"),
        onCardHolderVerification: { result in
            result.status == .matched
        }
    )
)

The site must have cardholder name verification enabled.

Next steps