> ## Documentation Index
> Fetch the complete documentation index at: https://dev.moonpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Import a card

> Send a card to MoonPay from your server, then buy crypto with it.

Send a card to MoonPay from your server and get a card `id`. Then quote and buy
with it, as in [Pay with card](https://dev.moonpay.com/platform/guides/pay-with-card.md).

See the [Going Live](https://dev.moonpay.com/platform/overview/going-live.md) section for requirements you
must meet before taking this integration to production.

## Prerequisites

* Card import enabled on your MoonPay account. Contact your MoonPay account
  team to enable it.
* A PCI-compliant server to send card data from.
* A [connected customer](https://dev.moonpay.com/platform/guides/connect-a-customer.md).

## How it works

> [!NOTE]
>
> Each purchase needs a fresh CVC. Import a card once and store its `id`. Before
> each later purchase with that card, send the new CVC with [Update the
> CVC](#update-the-cvc). MoonPay keeps a CVC for one hour.

### New customer

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor C as Customer
    participant FE as Your frontend
    participant BE as Your server
    participant API as MoonPay API
    participant MF as MoonPay frames

    FE->>API: getConnection()
    API-->>FE: connectionRequired
    FE->>MF: Render login frame
    C->>MF: Logs in
    MF-->>FE: complete (status: active)

    C->>FE: Enters card details
    FE->>BE: accessToken
    BE->>API: POST /platform/v1/payment-methods/cards
    API-->>BE: 201 { id, ... }

    FE->>API: getQuote({ paymentMethod: { type: "card", id } })
    C->>MF: Confirms purchase
    MF-->>FE: complete({ transaction })
```

### Returning customer

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor C as Customer
    participant FE as Your frontend
    participant BE as Your server
    participant API as MoonPay API
    participant MF as MoonPay frames

    FE->>API: getConnection()
    alt Login expired
        API-->>FE: connectionRequired
        FE->>MF: Render login frame
        C->>MF: Logs in
    end

    C->>FE: Re-enters the CVC
    FE->>BE: accessToken
    BE->>API: PATCH /platform/v1/payment-methods/cards/{id}/cvc
    API-->>BE: 204 No Content

    FE->>API: getQuote({ paymentMethod: { type: "card", id } })
    C->>MF: Confirms purchase
    MF-->>FE: complete({ transaction })
```

### 1. Connect the customer

[Connect the customer](https://dev.moonpay.com/platform/guides/connect-a-customer.md). Then get their
access token with
[`client.getCredentials()`](https://dev.moonpay.com/platform/sdk-reference/web/get-credentials) and
pass it to your server.

> [!WARNING]
>
> Hold the access token in memory only. Don't log it or write it to disk.

### 2. Import the card

Import a card the first time the customer pays with it. For a card you already
imported, skip to [Update the CVC](#update-the-cvc).

Call `POST /platform/v1/payment-methods/cards` from your server, with the
customer's access token as a Bearer token. The token needs the
`payment-methods.create` scope.

Send the request to MoonPay's PCI proxy, not `api.moonpay.com`. Use the host
that matches the access token:

| Mode | Host |
| - | - |
| Live | `https://secure.moonpay.com` |
| Test | `https://secure-sandbox.moonpay.com` |

We recommend sending the customer's `deviceIp` and `userAgent`. MoonPay uses
them for fraud checks.

```ts Import a card theme={null}
const res = await fetch(
  "https://secure.moonpay.com/platform/v1/payment-methods/cards",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify({
      number: card.number,
      expirationMonth: "03",
      expirationYear: "2028",
      cvc: card.cvc,
      billingAddress: {
        street: "123 Main St",
        town: "New York",
        postCode: "10001",
        state: "NY",
        country: "USA",
      },
      deviceIp: "203.0.113.7",
      userAgent: customerUserAgent,
    }),
  },
);

const { data } = await res.json();
```

```json Response (201) theme={null}
{
  "data": {
    "id": "b1c2d3e4-5678-90ab-cdef-1234567890ab",
    "type": "card",
    "cardType": "credit",
    "brand": "visa",
    "last4": "4242",
    "expirationMonth": "03",
    "expirationYear": "2028",
    "availability": { "active": true }
  }
}
```

#### Request body

| Field | Required | Description |
| - | - | - |
| `number` | Yes | The card number. |
| `expirationMonth` | Yes | 2-digit month, for example `"03"`. |
| `expirationYear` | Yes | 4-digit year, for example `"2028"`. |
| `cvc` | Yes | The card security code. |
| `billingAddress.street` | Yes | Street line. |
| `billingAddress.subStreet` | No | Second street line. |
| `billingAddress.town` | Yes | Town or city. |
| `billingAddress.postCode` | Yes | Postal code. |
| `billingAddress.state` | No | State or province. Required for the United States and Canada. |
| `billingAddress.country` | Yes | ISO 3166-1 alpha-3 country code, for example `"USA"`. |
| `deviceIp` | No | The customer's device IP address, IPv4 or IPv6. Recommended. |
| `userAgent` | No | The customer's browser or app user agent, up to 1024 characters. Recommended. |

#### Responses

* **`201 Created`**: MoonPay added the card. Store `data.id` against the
  customer so you can update the CVC for later purchases.
* **`200 OK`**: the customer already has this card. MoonPay returns the same
  `id` and stores the new CVC. Retries are safe, and if you lose a card's `id`,
  importing the card again returns it.

#### Errors

| Status | `code` | Meaning |
| - | - | - |
| `400` | `card_not_supported` | MoonPay doesn't accept this card. Ask the customer for a different card. |
| `400` | `invalid_request` | The request is invalid or MoonPay can't add the card. `errors` lists any invalid fields. |
| `401` | `unauthorized` | The access token is missing, invalid or expired, or a live token was sent to the test host. |
| `403` | `forbidden` | The token lacks the `payment-methods.create` scope, card import isn't enabled, or a live request didn't come through the PCI proxy. |
| `409` | `conflict` | The request conflicts with the customer's current state. |
| `422` | `unprocessable_entity` | MoonPay can't add a card for this customer, for example because card payments aren't available to them. |

### 3. Update the CVC

When a returning customer pays with a card you already imported, ask them for
the CVC and send it with
`PATCH /platform/v1/payment-methods/cards/{cardId}/cvc`. Use the card `id` from
the import response as `cardId`. You don't need to send the card number or
billing address again.

Call it from your server through the same PCI proxy host, with the customer's
access token. The token needs the `payment-methods.create` scope. MoonPay keeps
the CVC for one hour, so send it shortly before the purchase.

```ts Update the CVC theme={null}
const res = await fetch(
  `https://secure.moonpay.com/platform/v1/payment-methods/cards/${cardId}/cvc`,
  {
    method: "PATCH",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify({ cvc: card.cvc }),
  },
);

if (res.status !== 204) {
  // Handle the error. See the table below.
}
```

#### CVC update request body

| Field | Required | Description |
| - | - | - |
| `cvc` | Yes | The card security code. |

#### CVC update responses

* **`204 No Content`**: MoonPay stored the new CVC. The response has no body.

#### CVC update errors

| Status | `code` | Meaning |
| - | - | - |
| `400` | `invalid_request` | `cardId` isn't a valid card `id`, or `cvc` is missing or invalid. |
| `401` | `unauthorized` | The access token is missing, invalid or expired, or a live token was sent to the test host. |
| `403` | `forbidden` | The token lacks the `payment-methods.create` scope, card import isn't enabled, or a live request didn't come through the PCI proxy. |
| `404` | `not_found` | The customer has no card with this `id`. [Import the card](#import-the-card) again. |

### 4. Quote and buy

Get a quote with `paymentMethod: { type: "card", id }`, then run the purchase.

* [Get a quote](https://dev.moonpay.com/platform/guides/pay-with-card.md#get-a-quote)
* [Execute the transaction](https://dev.moonpay.com/platform/guides/pay-with-card.md#execute-the-transaction),
  or use the [buy button](https://dev.moonpay.com/platform/guides/pay-with-buy-button.md)
* [Handle challenges](https://dev.moonpay.com/platform/guides/handling-challenges.md)
* [Track the transaction](https://dev.moonpay.com/platform/guides/pay-with-card.md#track-the-transaction)
