The paymentpermission policy is required.In test mode, the frame uses window.confirm to simulate the Apple Pay payment. If your iframe uses the sandbox attribute, you will need to include allow-modals.
To embed the Apple Pay frame in a native iOS app, you render it inside a WKWebView. The frame communicates with the host app through the postMessage-based frames protocol. See Events below.You must handle JavaScript dialogs. The frame uses window.confirm, for example to present the simulated Apple Pay sheet in test mode. By default a WKWebView silently dismisses window.confirm, alert, and prompt: the call returns false with no UI shown, and the frame reads that as the customer cancelling, so the transaction comes back with status: "failed".To fix this, conform your view controller to WKUIDelegate, set webView.uiDelegate, and present the confirm panel, for example as a UIAlertController:
extension MoonPayFrameViewController: WKUIDelegate { func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { // Present the dialog and return the customer's choice. }}
Wire this even if you only ship live mode, since the default behaviour applies to any dialog the frame surfaces.For a complete native walkthrough that covers rendering the frame, wiring the bridge and events, and the full dialog handler, see iOS manual integration.
{ "version": 2, "meta": { "channelId": "ch_1" }, "kind": "complete", "payload": { "transaction": { "status": "failed", "failureReason": "Your payment was declined by your bank.", "failureCode": "authorizationDeclined" } }}
typeMessage<T> =T & {version: 2;meta: { channelId: string };};enumTransactionStatus { /** The transaction has successfully completed. The payment has been made and the crypto has been transferred. **/completed = "completed", /** The payment has been completed and the crypto transfer is underway. **/pending = "pending", /** The transaction has failed. No payment was applied and the crypto was not transferred. **/failed = "failed",}typeFailureCode = | "applePayMerchantUnavailable" | "transactionNotAllowed" | "validationError" | "serviceUnavailable" | "authorizationDeclined" | "unknown";typeTransaction = | { /** The MoonPay identifier for this transaction. **/id: string; /** The status of the transaction. **/status:TransactionStatus.completed |TransactionStatus.pending; } | {status:TransactionStatus.failed; /** A stable, machine-readable code identifying the failure category. Branch on this value for programmatic handling. May be omitted when the frame cannot classify the failure. **/failureCode?:FailureCode; /** A developer-friendly error message detailing the reason for the transaction failure. **/failureReason: string; };typeApplePayCompleteEvent =Message<{kind: "complete";payload: {transaction:Transaction; };}>;
Failure codes
When a complete event has status: "failed", the failureCode field indicates the failure category. Use this value — not failureReason — for programmatic branching. failureReason is a human-readable fallback suitable for display when you do not have custom copy for a given code.
failureCode is optional. When the frame cannot classify a failure, it sends
failureReason alone — fall back to showing that message to the customer.
failureCode
Default failureReason
When it fires
Recommended handling
applePayMerchantUnavailable
”Apple Pay is temporarily unavailable. Please try again later.”
Apple Pay merchant validation failed.
Retry after a short delay or offer an alternative payment method.
transactionNotAllowed
”This transaction is not allowed for your account.”
Payment method or account is not eligible (for example, region restriction or KYC limit).
Show the failureReason and guide the customer to resolve the issue or choose a different payment method.
Verification is required before the transaction can proceed. Render the
challenge frame at the provided URL. Do not
construct the URL yourself — use it as-is.
typeMessage<T> =T & {version: 2;meta: { channelId: string };};typeApplePayError =Message<{kind: "error";payload: {code: | "configurationError" | "quoteExpired" | "invalidQuote" | "applePayUnavailable" | "generic"; /** A developer-facing error message with details on recovery or documentation. This message is not intended to be rendered in UI. */message: string; };}>;
Code
Description
configurationError
The frame configuration is invalid. Check initialization parameters.
quoteExpired
The quote has expired. Fetch a new quote and send it via setQuote.
invalidQuote
The quote signature is invalid or malformed.
applePayUnavailable
Apple Pay is not available in the current environment or browser.
Provide a new quote to the frame. Pass signature as returned by the quote endpoint. Upon receiving this event, the frame disables the Apple Pay button until the quote is revalidated.