Mnp

SKILL.md

MoonPay Platform Skill

Product summary

MoonPay Platform is a headless fiat-to-crypto payment API and SDK for building crypto ramps directly in your app. You control the user experience while MoonPay handles compliance, risk, and fraud. The platform uses a combination of REST API calls and embedded frames (iframes on web, WebViews on mobile) to manage sensitive operations like KYC and payment processing.

Key resources:

Key files and concepts:

  • Session tokens (created server-side, sent to frontend)
  • Access tokens and client tokens (returned from connect flow, used for API calls and frame initialization)
  • Frames: co-branded (MoonPay-hosted UI) and headless (minimal UI like Apple Pay button)
  • Connections: customer permission to associate MoonPay account with your app
  • Quotes: real-time prices and fees for transactions (price quotes and executable quotes)

When to use

Reach for this skill when:

  • Building payment flows: Implementing fiat-to-crypto purchase experiences (e.g., "buy crypto with Apple Pay")
  • Managing customer connections: Onboarding customers, checking connection status, handling KYC verification
  • Getting transaction quotes: Fetching real-time prices, fees, and limits before executing payments
  • Executing transactions: Setting up payment methods and processing crypto purchases
  • Handling compliance: Managing challenges (3D Secure, identity verification, authentication upgrades)
  • Tracking transactions: Polling or using webhooks to monitor transaction status (pending → complete/failed)
  • Testing integrations: Using test mode with test API keys and test payment cards
  • Integrating on web or mobile: Rendering frames in iframes (web) or WebViews (iOS/Android)

Quick reference

Authentication

Context Method Header
Server-side API calls Secret key Authorization: Api-Key sk_test_123 or sk_live_123
Client-side API calls Access token Authorization: Bearer <accessToken>
Frame initialization Client token Pass clientToken to frame setup

API Endpoints (core workflow)

Endpoint Purpose
POST /session Create session token (server-side)
POST /quotes Get executable quote with fees and limits
GET /payment-methods List available payment methods for customer
GET /transactions List transactions with pagination
GET /transactions/{id} Get single transaction details

Credential types

Credential Scope Persistence Use case
Session token One-time setup Don't persist Initialize connect flow on frontend
Access token API calls In-memory only Make authenticated API requests from frontend
Client token Frame auth In-memory only Initialize payment frames (Apple Pay, etc.)
Secret key Server operations Secure storage Create sessions, server-to-server calls

Frame types

Frame Type Use case
Connect Co-branded Customer login/KYC, returns access + client tokens
Check Invisible Verify existing connection without UI
Apple Pay Headless Execute Apple Pay transactions

Transaction statuses

Status Meaning
pending Payment accepted, assets transferring
complete Payment finalized, assets delivered
failed Payment failed, no funds transferred

Decision guidance

When to use SDK vs manual frame integration

Scenario Use SDK Use manual
You want drop-in frame setup with event handling
You need custom frame wrapper or WebView bridge
You're building with React/TypeScript
You're integrating into existing WebView infrastructure
You want automatic message serialization/validation

When to check connection vs render connect flow

Condition Action
Customer is new (first visit) Render connect frame directly
Customer has connected before Check connection first; render only if expired
Connection check returns connectionRequired Render connect flow
Connection check returns active Skip UI, use returned credentials

When to use price quotes vs executable quotes

Quote type When to use
Price quote Estimate costs before showing confirmation (no connection required)
Executable quote Execute actual transaction (requires active connection)

Workflow

1. Set up server-side session creation

Create a session endpoint on your server that:

  • Takes a unique customer ID and device IP
  • Calls POST /session with your secret key
  • Returns the sessionToken to your frontend
const res = await fetch("https://api.moonpay.com/platform/v1/session", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Api-Key sk_test_123",
  },
  body: JSON.stringify({
    externalCustomerId: "user_123",
    deviceIp: "203.0.113.1",
  }),
});
const { sessionToken } = await res.json();

2. Initialize client and check connection

On your frontend, create the SDK client and check if customer has an active connection:

const clientResult = createClient({ sessionToken });
const client = clientResult.value;

const checkResult = await client.checkConnection();
if (checkResult.value.status === "active") {
  // Use existing credentials
  const { accessToken, clientToken } = checkResult.value.credentials;
} else {
  // Render connect flow
}

3. Render connect flow if needed

If connection is missing or expired, render the co-branded connect frame:

const connectResult = await client.connect({
  container: document.querySelector("#connect-container"),
  onEvent: (event) => {
    if (event.kind === "complete") {
      const { accessToken, clientToken } = event.connection.credentials;
      // Store in memory, proceed to payment flow
    }
  },
});

4. List payment methods and get quotes

After connection, fetch available payment methods and get a quote:

const methodsResult = await client.getPaymentMethods();
const quoteResult = await client.getQuote({
  source: "USD",
  destination: "ETH",
  sourceAmount: "100.00",
  walletAddress: "0x...",
  paymentMethod: "apple_pay",
});

if (quoteResult.value.challenge) {
  // Handle challenge (see handling challenges section)
}

5. Execute transaction

Set up the payment frame and handle transaction events:

const applePayResult = await client.setupApplePay({
  quote: quoteResult.value.signature,
  container: document.querySelector("#apple-pay-container"),
  onEvent: (event) => {
    switch (event.kind) {
      case "ready":
        // Frame is ready, show button
        break;
      case "complete":
        // Transaction initiated, track via polling or webhooks
        const txnId = event.payload.transaction.id;
        break;
      case "quoteExpired":
        // Fetch new quote and update frame
        break;
      case "error":
        // Handle error, offer retry or alternate method
        break;
    }
  },
});

6. Track transaction status

Poll the transaction endpoint or use webhooks to monitor status:

const txnResult = await fetch(
  `https://api.moonpay.com/platform/v1/transactions/${txnId}`,
  {
    headers: { "Authorization": `Bearer ${accessToken}` },
  }
);
const transaction = await txnResult.json();
// Check transaction.status: pending, complete, or failed

Common gotchas

  • Never persist client credentials: Access tokens and client tokens expire and should only be held in memory. Fetch a new session token on each app visit.
  • Secret key exposure: Never commit your secret key to code or send it to the frontend. Always create sessions server-side.
  • Quote expiration: Quotes expire at expiresAt. Check this before executing and fetch a new quote if needed.
  • Missing CSP rules: On web, your Content Security Policy must allow frame-src and connect-src for *.moonpay.com.
  • Handshake timeout: If integrating frames manually, expect a handshake within 5 seconds. Fail fast if it doesn't arrive.
  • Channel ID routing: When multiple frames are open, use meta.channelId to route messages correctly and prevent cross-talk.
  • Test mode vs live: Test mode is determined by your API key (sk_test_... vs sk_live_...), not the URL. Use test keys for development.
  • Real email/phone in test mode: Test accounts require real email and phone numbers for OTP verification. Use the + suffix pattern to create multiple addresses from one inbox.
  • Executable quote requirements: Executable quotes require an active connection. If executable: false, the customer must complete a challenge first.
  • Transaction polling: Transactions start as pending. Use polling or webhooks to track when they reach complete or failed.
  • Apple Pay domain verification: On web, you must complete Apple's domain verification to use Apple Pay. This is a manual process.
  • WKWebView configuration: On iOS, set allowsInlineMediaPlayback = true for the connect frame to work properly.

Verification checklist

Before submitting work, verify:

  • Session token is created server-side with secret key, never exposed to frontend
  • Client credentials (access token, client token) are stored in memory only, not persisted
  • Connection status is checked before rendering connect flow
  • Quote signature is passed correctly to payment frame
  • Quote expiration is checked before executing transaction
  • All frame events are handled (ready, complete, error, quoteExpired, unsupported)
  • Transaction ID is captured from complete event and tracked via polling or webhooks
  • Challenges are detected and rendered as full-screen frames on mobile
  • Error responses include proper error handling and user-facing messages
  • CSP headers allow frame-src and connect-src for *.moonpay.com (web only)
  • Test mode uses sk_test_... API key; production uses sk_live_...
  • postMessage validation checks origin, version, and channelId (manual integrations)

Resources

Comprehensive page listing: https://moonpay.mintlify.app/llms.txt

Critical documentation pages:

  1. Core Concepts — Understand connections, frames, challenges, and quotes
  2. Connect a Customer — Step-by-step customer onboarding and connection flow
  3. Pay with Apple Pay — Complete transaction execution example with event handling

For additional documentation and navigation, see: https://moonpay.mintlify.app/llms.txt

Weekly Installs
4
First Seen
13 days ago
Installed on
amp4
cline4
opencode4
cursor4
kimi-cli4
codex4