Passkeys

Saved cards are unlocked by a passkey — Face ID, Touch ID or the device's own screen lock. A passkey belongs to a domain, and ours is secure.pay.rezolve.com. Before that domain will vouch for your app, it has to publish your app's identifiers, and your app has to claim the domain. Until both sides are in place the prompt simply never appears: iOS reports the same code it uses for "the shopper dismissed it", so the SDK reads it as a cancellation and nothing is logged.

Needs iOS 16 or Android 9. Below those, checkout still works — the shopper types the card each time.

This is not your baseUrl. Keep calling the API on the endpoint you were given; the passkey domain is a separate host and you never configure it in the SDK. The only place it appears in your app is the iOS entitlement below — Android does not name it at all. The SDK learns it from the API's own response, so a baseUrl pointing at the API host is correct and nothing needs to change there.

Step 1: Claim the domain in your app (iOS only)

In Xcode, add the Associated Domains capability to the app target and the entry webcredentials:secure.pay.rezolve.com. Android needs nothing in the app — the signing key is what identifies it.

Keep the entitlements file free of XML comments. The iOS loader cannot parse them, and it does not fail the build: it drops every entitlement silently, so the app ships with no Associated Domains at all.

Step 2: Register the app in the portal

Open Settings → Mobile apps in your merchant portal and add one row per app:

  • iOS — your Team ID and bundle id together, e.g. ABCDE12345.com.leon.shop. Both are in Xcode: the Team ID beside the team name under Signing & Capabilities, the bundle id on General.
  • Android — the package name and the SHA-256 fingerprint of the signing key. ./gradlew signingReport prints both. Add one row per key: debug and release builds are signed differently, so a release-only row leaves your testers without passkeys.

Step 3: Install the app after registering, not before

The published list refreshes within a minute of you saving. Android picks it up on the next checkout, but iOS reads it only when the app is installed or updated — a build already on a tester's phone will not see a row added afterwards. Delete and reinstall it.

Checking it worked

Both platforms read a public file from the passkey domain. Fetch the one for your platform and look for your own identifier — if it is not there, the prompt will not appear, and this is the fastest way to tell that apart from a bug in your app.

PlatformFileLook for
iOShttps://secure.pay.rezolve.com/.well-known/apple-app-site-associationYour TeamID.bundle.id under webcredentials.apps.
Androidhttps://secure.pay.rezolve.com/.well-known/assetlinks.jsonYour package name and the SHA-256 fingerprint of the key that signed the build on the device, under get_login_creds.

Testing on a simulator or emulator: a passkey needs a screen lock, and a fresh device has none. On iOS, enrol a face under Features → Face ID, then approve each prompt with Features → Face ID → Matching Face — without enrolment no prompt is drawn at all and the saved-cards button looks inert. On Android, set a PIN or pattern in Settings first, and use an emulator image with Google Play services; a plain AOSP image has no credential manager.


Did this page help you?