Accept NFC tap-to-pay payments in your iOS app via MONEI Pay.
No special entitlements, certificates, or Apple approval needed — your app opens MONEI Pay, which handles the NFC payment, then calls back with the result.
- iOS 15.0+
- MONEI Pay installed on device
- POS auth token from your backend (
POST /v1/pos/auth-token)
Use the latest release. Git tags look like v1.2.3; SwiftPM and CocoaPods expect semver without the v. CocoaPods also lists the pod here.
In Xcode: File → Add Package Dependencies → https://github.com/MONEI/monei-pay-ios-sdk.git → pick a rule tied to the version on the latest release (e.g. Up to Next Major from that version).
In Package.swift, set from: to the semver from the latest release page:
dependencies: [
.package(url: "https://github.com/MONEI/monei-pay-ios-sdk.git", from: "X.Y.Z")
]In your Podfile:
pod 'MoneiPaySDK'Then run pod install (resolves the current release on trunk).
In your app's Info.plist, register a custom URL scheme (e.g. your bundle ID):
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>my-merchant-app</string>
</array>
</dict>
</array><key>LSApplicationQueriesSchemes</key>
<array>
<string>monei-pay</string>
</array>The SDK launches MONEI Pay via a Universal Link (https://pay.monei.com/accept-payment), which opens
the app directly with no "Open in MONEI Pay?" prompt. On MONEI Pay installs that predate Universal
Link support it transparently falls back to the monei-pay:// custom scheme (hence the queried scheme
above is still required). No merchant configuration is needed beyond the steps here.
SwiftUI:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
MoneiPay.handleCompleteRedirect(url: url)
}
}
}
}UIKit:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
return MoneiPay.handleCompleteRedirect(url: url)
}Two complementary result channels:
completeScheme— your app's URL scheme. MONEI Pay opens<completeScheme>://payment-result?...on completion. UX-only, untrusted; use for navigation/UI.callbackUrl(optional) — HTTPS endpoint on your backend. MONEI fires a signed webhook (MONEI-SignatureHMAC). Use for fulfillment, accounting, anything irreversible.
import MoneiPaySDK
do {
let result = try await MoneiPay.acceptPayment(
token: "eyJ...", // Raw JWT from your backend (no "Bearer " prefix)
amount: 1500, // Amount in cents (1500 = 15.00 EUR)
description: "Order #123", // Optional
customerName: "John Doe", // Optional
customerEmail: "john@ex.com", // Optional
customerPhone: "+34600000000",// Optional
callbackUrl: "https://merchant.example.com/webhook/monei", // Optional, signed webhook
completeScheme: "my-merchant-app" // Your registered URL scheme
)
print("Payment approved: \(result.transactionId)")
print("Card: \(result.cardBrand ?? "") \(result.maskedCardNumber ?? "")")
} catch MoneiPayError.moneiPayNotInstalled {
// Prompt user to install MONEI Pay
} catch MoneiPayError.paymentCancelled {
// User cancelled
} catch MoneiPayError.paymentTimeout {
// MONEI Pay didn't respond in time
} catch {
print("Payment failed: \(error.localizedDescription)")
}Accepts an NFC payment via MONEI Pay.
| Parameter | Type | Required | Description |
|---|---|---|---|
token |
String |
Yes | Raw JWT auth token (no "Bearer " prefix) |
amount |
Int |
Yes | Amount in cents |
description |
String? |
No | Payment description |
customerName |
String? |
No | Customer name |
customerEmail |
String? |
No | Customer email |
customerPhone |
String? |
No | Customer phone |
callbackUrl |
String? |
No | HTTPS endpoint for the signed webhook. Trusted channel — use for fulfillment. |
orderId |
String? |
No | Merchant order reference. Surfaced in the webhook callback for reconciliation. Max 2048 chars. If omitted, the SDK generates one. |
transactionType |
String? |
No | Optional: SALE (default), AUTH, REFUND, CAPTURE, CANCEL, PAYOUT, VERIF. Server-validated. |
completeScheme |
String |
Yes | Your app's registered URL scheme. MONEI Pay opens <completeScheme>://payment-result on end |
timeout |
TimeInterval? |
No | Timeout in seconds (default: 60) |
Returns PaymentResult. Throws MoneiPayError.
Handle incoming complete-redirect URL from MONEI Pay. Returns true if the URL was handled.
| Property | Type | Description |
|---|---|---|
transactionId |
String |
Unique transaction ID |
success |
Bool |
Whether payment was approved |
amount |
Int? |
Amount in cents |
cardBrand |
String? |
Card brand (visa, mastercard, etc.) |
maskedCardNumber |
String? |
Masked card number (****1234) |
| Case | Description |
|---|---|
.moneiPayNotInstalled |
MONEI Pay not on device |
.paymentInProgress |
Another payment is active |
.paymentTimeout |
Callback not received in time |
.paymentCancelled |
User cancelled |
.paymentFailed(reason:) |
Payment declined or failed |
.invalidParameters(_) |
Invalid input parameters (also INVALID_AMOUNT, INVALID_CALLBACK_URL, INVALID_COMPLETE_URL) |
.failedToOpen |
Could not open MONEI Pay |
.tokenExpired |
Auth token expired — fetch fresh token and retry |
.invalidToken |
Auth token rejected |
.notAuthenticated |
No signed-in session and no auth_token supplied |
.accountNotConfigured |
Account is not set up for card-present payments |
The examples/MerchantDemo directory contains a minimal SwiftUI app demonstrating the full integration flow: enter an API key, fetch a POS auth token, and accept an NFC payment via MONEI Pay.
To run:
- Open
examples/MerchantDemo/MerchantDemo.xcodeprojin Xcode - Set your Apple Development Team in Signing & Capabilities
- Build and run on a device with MONEI Pay installed
The demo references the SDK as a local Swift package (../../), so any local SDK changes are picked up immediately.
Beta: MONEI Pay for iOS is currently in beta. Join via TestFlight.
Your backend generates POS auth tokens via the MONEI API:
curl -X POST https://api.monei.com/v1/pos/auth-token \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json"See the MONEI API docs for details.
Maintainers: clone the repo, install Node dependencies with pnpm (pnpm install), then use swift build / swift test. Git commits are checked with commitlint via a Husky commit-msg hook. Releases use release-it (pnpm run release): it bumps the version, updates the changelog and MoneiPaySDK.podspec, creates a Git tag and GitHub release. Publishing to the CocoaPods trunk runs in CI when that release is published.
MIT