Accept payments
The SDK has four calls: isSupported, prepare, acceptPayment and presentEducation. The API is @MainActor. Call it from the main actor, for example from a SwiftUI view.
Example
import MoneiTapToPay
// On launch, and again after each token renewal.
func startTapToPay() async {
guard TapToPay.isSupported else { return } // Hide Tap to Pay on iPhone in your UI.
do {
let token = try await myServer.fetchPosToken() // The "token" field only.
try await TapToPay.prepare(token: token)
} catch {
// See "Handle errors and unclear outcomes".
}
}
// When the customer pays.
func pay(amountInCents: Int, orderId: String) async {
do {
let result = try await TapToPay.acceptPayment(
amount: amountInCents,
orderId: orderId,
callbackUrl: URL(string: "https://example.com/monei/webhook"))
switch result.status {
case .approved: showApproved(result)
case .declined: showDeclined(result)
@unknown default: showPending(orderId)
}
} catch TapToPayError.outcomeUnknown(let orderId) {
// Do not retry. Check this orderId in MONEI.
showPending(orderId)
} catch {
// See "Handle errors and unclear outcomes".
}
}
Check device support
TapToPay.isSupported is true when the device supports Tap to Pay on iPhone: an iPhone XS or newer with iOS 18.5 or later. When it is false, hide Tap to Pay on iPhone in your UI. On an unsupported device, all calls throw notSupported.
Prepare the reader
Call TapToPay.prepare(token:) on launch, and again after each token renewal.
preparestores the token and prepares the reader in the background, so that the first payment starts faster.- The token stays in memory only. Call
prepareagain after each launch. preparedoes not show Apple's terms.prepareasks for location permission if the user did not answer yet, and waits until the user answers. There is no time limit for the answer. If the user does not allow location access,preparethrowslocationDenied. IfInfo.plisthas noNSLocationWhenInUseUsageDescription,preparethrowslocationDeniedat once.- The SDK needs a location fix to prepare the reader. The first
prepareoracceptPaymentafter launch waits up to 15 seconds for it. If the device gets no location in this time, the call throwspaymentFailed(code: .locationTimeout).
When the app comes back to the foreground, the SDK prepares the reader again. You do not need to do this.
Take a payment
Call TapToPay.acceptPayment(amount:orderId:callbackUrl:) when the customer pays.
| Parameter | Type | Description |
|---|---|---|
amount | Int | The amount in euro cents, more than 0. For example, 1050 is 10.50 EUR. |
orderId | String | Your own reference for the order. It must not be empty. Use a new orderId for each order. MONEI stores it with the payment, and you use it to reconcile. |
callbackUrl | URL? | The URL where MONEI sends the signed webhook with the payment result. Always set it. See Confirm the payment with the webhook. |
What occurs during a payment:
- If the account is not linked yet, the first
acceptPaymenton a device shows Apple's Tap to Pay on iPhone terms. The user must accept them. - Apple shows the tap screen. The customer taps the card.
- MONEI processes the payment.
acceptPaymentreturns aPaymentResultor throws aTapToPayError.
Only one payment can run at a time. During a payment, acceptPayment throws busy, so disable your pay button.
If acceptPayment throws outcomeUnknown, the card can be charged. Do not retry, and do not charge again with a new orderId. See Unclear outcome.
Read the result
acceptPayment returns a PaymentResult:
| Property | Type | Description |
|---|---|---|
paymentId | String | The MONEI payment ID. |
status | Status | .approved or .declined. |
cardBrand | String? | The card network name in lowercase, for example visa or amex. unknown for a network that the SDK does not know. nil if the network is not available. |
last4 | String? | The last 4 digits of the card, or nil. |
orderId | String | The orderId of the payment. |
A result with the status .declined is not an error. The card was read and the issuer declined the payment. The payment is in MONEI with its paymentId.
Confirm the payment with the webhook
Always pass callbackUrl. MONEI sends a signed webhook with the payment result to this URL. The signed webhook is the only trusted payment result. Use the result in the app for the UI only.
- Verify the signature on your server before you fulfil the order. See Verify signature.
- The
callbackUrlmust usehttps, have 2048 characters or fewer, and its host must not be a private IP address. The SDK does not check the URL. MONEI checks it after the card read. If the URL is not valid, MONEI does not create the payment, andacceptPaymentcan throwoutcomeUnknown. Make sure that the URL is valid before you take payments. - For when MONEI sends this webhook, see Payment callback.
Show Apple's education screens
Call TapToPay.presentEducation(from:) to show Apple's screens that teach how to tap a card. Show them, for example, on first use and from your help menu.
try await TapToPay.presentEducation(from: viewController)
Switch statements
The SDK uses library evolution, and its public enums are not frozen. A switch over PaymentResult.Status, TapToPayError or TapToPayErrorCode needs an @unknown default case. Without it, your app does not compile.
API reference
| Symbol | Description |
|---|---|
TapToPay.isSupported: Bool | true when the device supports Tap to Pay on iPhone. |
TapToPay.prepare(token: String) async throws | Stores the token and prepares the reader in the background. It does not show Apple's terms. It asks for location permission if the user did not answer yet. |
TapToPay.acceptPayment(amount: Int, orderId: String, callbackUrl: URL?) async throws -> PaymentResult | Takes one card payment. amount is in euro cents and must be more than 0. orderId must not be empty. |
TapToPay.presentEducation(from: UIViewController) async throws | Shows Apple's screens that teach how to tap a card. |
PaymentResult | paymentId, status, cardBrand, last4 and orderId. See Read the result. |
TapToPayError | The error that all calls throw. See Handle errors and unclear outcomes. |
Common questions
Why does the first payment take longer?
On the first launch, the SDK asks for location permission and waits for the answer of the user. Then it needs a location fix before it can prepare the reader, and it waits up to 15 seconds for it. On the first payment on a device, Apple can also show its Tap to Pay on iPhone terms. Call prepare on launch, so that the reader is ready before the customer pays.
Must I call prepare again when the app comes back from the background?
No. The SDK prepares the reader again when the app comes back to the foreground. Call prepare again only after a launch and after a token renewal.
Does each order need a new orderId?
Yes. Use a new, non-empty orderId for each order. MONEI stores it with the payment, and you use it to reconcile. After outcomeUnknown, do not charge again with a new orderId until you know the result of the first payment. See Unclear outcome.