A Swift Package for embedding Crossmint's checkout experience in iOS apps.
Add the package via Swift Package Manager:
In Xcode: File > Add Package Dependencies, paste the URL below, and select Up to Next Major Version from 1.0.0.
https://github.com/Crossmint/crossmint-checkout-swift.git
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/Crossmint/crossmint-checkout-swift.git", from: "1.0.0")
]Then add "CrossmintCheckout" to your target's dependencies.
Use the Crossmint Orders API to create an order from your backend. The response includes an orderId and clientSecret.
POST https://www.crossmint.com/api/2022-06-09/orders
Pass your client-side API key (ck_...) — the same key used by the Orders API's client-side counterpart. It is required; without it the hosted checkout renders an "Invalid input" configuration error.
import SwiftUI
import CrossmintCheckout
struct CheckoutView: View {
let orderId: String
let clientSecret: String
var body: some View {
CrossmintEmbeddedCheckout(
apiKey: "ck_production_...",
orderId: orderId,
clientSecret: clientSecret
)
}
}| Property | Type | Required | Description |
|---|---|---|---|
apiKey |
String |
Yes | Your client-side API key (ck_...) |
orderId |
String? |
Yes* | The order ID returned by the Orders API |
clientSecret |
String? |
Yes* | The client secret returned by the Orders API |
payment |
CheckoutPayment? |
No | Payment configuration (fiat/crypto, allowed methods) |
appearance |
CheckoutAppearance? |
No | UI customization (colors, fonts, border radius) |
lineItems |
CheckoutLineItems? |
No | Line item configuration (not yet implemented) |
recipient |
CheckoutRecipient? |
No | Recipient configuration (not yet implemented) |
identityVerificationHandling |
IdentityVerificationHandling? |
No | .external renders no KYC step inside checkout; you present it yourself (see below) |
controller |
CrossmintCheckoutController? |
No | Observable order state (order, client secret, KYC credentials) |
CrossmintEmbeddedCheckout(
apiKey: "ck_production_...",
orderId: orderId,
clientSecret: clientSecret,
payment: CheckoutPayment(
crypto: CheckoutCryptoPayment(enabled: true, defaultChain: "base"),
fiat: CheckoutFiatPayment(
enabled: true,
defaultCurrency: "usd",
allowedMethods: CheckoutAllowedMethods(applePay: true, card: true)
),
defaultMethod: .fiat
)
)CrossmintEmbeddedCheckout(
apiKey: "ck_production_...",
orderId: orderId,
clientSecret: clientSecret,
appearance: CheckoutAppearance(
variables: CheckoutAppearanceVariables(
colors: CheckoutVariablesColorStyle(
textPrimary: "#000000",
textSecondary: "#606060",
backgroundPrimary: "#FFFFFF",
borderPrimary: "#E0E0E0"
)
),
rules: CheckoutAppearanceRules(
primaryButton: CheckoutPrimaryButtonRule(
borderRadius: "8px",
colors: CheckoutColorStyle(background: "#6C5CE7", text: "#FFFFFF")
)
)
)
)Note:
lineItemsandrecipientare accepted as parameters but not yet implemented. Passing either will display an error.
Pass a CrossmintCheckoutController to observe the order as the buyer progresses. The controller exposes the latest order, the orderClientSecret, and derived identityVerificationCredentials.
struct CheckoutView: View {
@StateObject private var controller = CrossmintCheckoutController()
var body: some View {
CrossmintEmbeddedCheckout(
apiKey: "ck_production_...",
orderId: orderId,
clientSecret: clientSecret,
controller: controller
)
.onReceive(controller.$order) { order in
print("Order phase: \(String(describing: order?.phase))")
}
}
}Order events are also available as chained methods: .onOrderUpdated { update in } and .onOrderCreationFailed { message in }.
Reusing a controller across checkout sessions requires calling clear() first.
Some orders require identity verification before payment. By default checkout renders that step inline and you do nothing.
To show the step in your own layout instead, pass identityVerificationHandling: .external to checkout. Then watch the controller for credentials and present CrossmintIdentityVerification with them. A sheet works well: the credentials appear when the order needs verification, and they go away when the backend confirms it.
struct CheckoutView: View {
@StateObject private var controller = CrossmintCheckoutController()
@State private var kycCredentials: IdentityVerificationCredentials?
var body: some View {
CrossmintEmbeddedCheckout(
apiKey: "ck_production_...",
orderId: orderId,
clientSecret: clientSecret,
identityVerificationHandling: .external,
controller: controller
)
.onReceive(controller.$order) { order in
kycCredentials = order?.identityVerificationCredentials
}
.sheet(item: $kycCredentials) { credentials in
CrossmintIdentityVerification(
apiKey: "ck_production_...",
credentials: credentials
)
.onComplete { status in print("KYC finished: \(status)") }
.onError { error in print("KYC error: \(error.message)") }
}
}
}Checkout does not wait for a signal from your component. It polls the order until the backend confirms the verification, then continues on its own.
CrossmintIdentityVerification can also be used standalone, without embedded checkout, if you obtain the credentials from your backend's order response (payment.preparation.kyc).
| Property | Type | Required | Description |
|---|---|---|---|
apiKey |
String |
Yes | Your client-side API key (ck_...) |
credentials |
IdentityVerificationCredentials |
Yes | From the controller or your backend's order response |
locale |
CheckoutLocale? |
No | UI language of the verification flow |
Attach handlers with chained methods, all optional:
.onReady { }— the verification UI finished loading.onComplete { status in }— the buyer finished; the status carries the outcome.onCancel { }— the buyer dismissed the flow.onError { error in }— something failed;retriablesays whether showing the view again can work
The view fills the space you give it, and the verification content scrolls inside the view. Use onReady to drive your own loading indicator.
Note: Document capture needs camera access. Add
NSCameraUsageDescriptionto your app's Info.plist or the capture step fails.
Note:
identityVerificationHandlingrequires a current Crossmint deployment. Older deployments ignore the flag and render the verification step inline as well.
See a full working example at crossmint-swift-checkout-demo.
See LICENSE for details.