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

# Card Payments

Nuvion's card payment flow uses a **Payment Intent** and an **Intent Action**. Create a Payment Intent to declare the charge, then submit an Intent Action to confirm the payment.

### Step 1: Create a payment intent

A Payment Intent represents your intent to charge a card. No funds are moved at this stage.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.nuvion.dev/payment-intents \
    -H "Authorization: Bearer $NUVION_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "currency": "USD",
      "amount": 5000,
      "account_id": "acc_01HXYZ5678EFGH",
      "reference": "order_abc123",
      "description": "Order #1042"
    }'
  ```
</CodeGroup>

<ParamField body="currency" type="string" required>
  ISO 4217 currency code. e.g. `USD`.
</ParamField>

<ParamField body="amount" type="number" required>
  Amount in the smallest currency unit. `5000` = \$50.00 USD.
</ParamField>

<ParamField body="account_id" type="string" required>
  The ID of the account to credit on successful charge.
</ParamField>

<ParamField body="reference" type="string" required>
  Your unique internal order or transaction reference.
</ParamField>

<ParamField body="description" type="string">
  A human-readable payment description. Maximum 250 characters.
</ParamField>

<ParamField body="intent_action" type="object">
  Optional confirmation details. Include this when you want to create the Payment Intent and submit the card payment in the same request.
</ParamField>

```json theme={null}
{
  "id": "01HXYZ7K3M9PQRST4UVWXY2Z6",
  "currency": "USD",
  "amount": 5000,
  "reference": "order_abc123",
  "description": "Order #1042",
  "status": "requires_action",
  "entity_id": "ent_01HXYZ1234ABCD",
  "account_id": "acc_01HXYZ5678EFGH",
  "created": 1735725600000
}
```

The intent is created with `status: requires_action`. No charge happens until you submit an Intent Action.

#### Create and confirm in one request

If your server already has the encrypted card payload, you can include `intent_action` when creating the Payment Intent.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.nuvion.dev/payment-intents \
    -H "Authorization: Bearer $NUVION_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "currency": "USD",
      "amount": 5000,
      "account_id": "acc_01HXYZ5678EFGH",
      "reference": "order_abc124",
      "description": "Order #1043",
      "intent_action": {
        "action": "confirm",
        "category": "collections",
        "payment_type": "card-acq",
        "payment_type_data": {
          "data": "<base64_encrypted_card_data>",
          "billing_address": {
            "address_line_one": "350 Fifth Avenue",
            "city": "New York",
            "state": "NY",
            "country": "US"
          }
        },
        "customer": {
          "first_name": "Ada",
          "last_name": "Lovelace",
          "email": "ada@example.com"
        }
      }
    }'
  ```
</CodeGroup>

```json theme={null}
{
  "id": "01HXYZ7K3M9PQRST4UVWXY2Z7",
  "currency": "USD",
  "amount": 5000,
  "reference": "order_abc124",
  "description": "Order #1043",
  "status": "completed",
  "entity_id": "ent_01HXYZ1234ABCD",
  "account_id": "acc_01HXYZ5678EFGH",
  "created": 1735725600000,
  "intent_action": {
    "id": "01HACT9012EFGI",
    "currency": "USD",
    "amount": 5000,
    "applicable_fee": 75,
    "charge_amount": 5000,
    "refunded_amount": 0,
    "reference": "order_abc124",
    "action": "confirm",
    "category": "collections",
    "payment_type": "card-acq",
    "payment_type_data": {
      "payment_country": "US",
      "last_four_digits": "1111",
      "card_brand": "visa",
      "card_type": "credit"
    },
    "status": "completed",
    "entity_id": "ent_01HXYZ1234ABCD",
    "account_id": "acc_01HXYZ5678EFGH",
    "payment_intent_id": "01HXYZ7K3M9PQRST4UVWXY2Z7",
    "created": 1735725600000
  }
}
```

### Step 2: Submit an intent action

Submit the card payment using `POST /intent-actions` with `payment_type` set to `card-acq` and the encrypted card payload in `payment_type_data.data`. See [Card data encryption](/api-reference/payment-intents#card-data-encryption) for how to create this value.

<ParamField body="payment_intent_id" type="string" required>
  The ID of the Payment Intent to charge against. The intent must still have `status: requires_action`.
</ParamField>

<ParamField body="intent_action.action" type="string" required>
  Always `confirm`.
</ParamField>

<ParamField body="intent_action.category" type="string" required>
  Always `collections`.
</ParamField>

<ParamField body="intent_action.payment_type" type="string" required>
  The payment method. Use `card-acq` for card payments.
</ParamField>

<ParamField body="intent_action.payment_type_data.data" type="string" required>
  Base64-encoded RSA-OAEP SHA-256 encrypted card object.
</ParamField>

<ParamField body="intent_action.payment_type_data.billing_address" type="object" required>
  Cardholder billing address.

  <Expandable title="billing_address fields">
    <ParamField body="address_line_one" type="string" required>
      First line of the billing address.
    </ParamField>

    <ParamField body="address_line_two" type="string">
      Apartment, suite, or second address line.
    </ParamField>

    <ParamField body="city" type="string" required>
      Billing city.
    </ParamField>

    <ParamField body="state" type="string" required>
      Billing state, region, or province.
    </ParamField>

    <ParamField body="country" type="string" required>
      Two-letter country code. e.g. `US`.
    </ParamField>

    <ParamField body="zip" type="string">
      Postal or ZIP code.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="intent_action.payment_type_data.auth_model" type="string">
  Card authentication model. `3ds_required`.
</ParamField>

<ParamField body="intent_action.payment_type_data.browser_info" type="object" required>
  Browser data used for 3DS authentication.
</ParamField>

<ParamField body="intent_action.customer" type="object" required>
  Cardholder identity details.

  <Expandable title="customer fields">
    <ParamField body="first_name" type="string" required>
      Customer first name.
    </ParamField>

    <ParamField body="last_name" type="string" required>
      Customer last name.
    </ParamField>

    <ParamField body="email" type="string" required>
      Customer email address.
    </ParamField>

    <ParamField body="phone_number" type="string">
      Customer phone number, when available.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="intent_action.return_url" type="string">
  URL to redirect the cardholder to after 3DS completes. Required unless `auth_model` is `3ds_disabled`.
</ParamField>

#### 3DS Authentication

3DS is the default card authentication model. If you omit `auth_model`, Nuvion treats the payment as `3ds_required`, and `browser_info` becomes required. If the issuer requires customer interaction, the response returns `status: pending_user_action`, `requires_action: true`, and a `next_action.url` to redirect the cardholder to.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.nuvion.dev/intent-actions \
    -H "Authorization: Bearer $NUVION_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "payment_intent_id": "01HXYZ7K3M9PQRST4UVWXY2Z6",
      "intent_action": {
        "action": "confirm",
        "category": "collections",
        "payment_type": "card-acq",
        "payment_type_data": {
          "data": "<base64_encrypted_card_data>",
          "billing_address": {
            "address_line_one": "350 Fifth Avenue",
            "city": "New York",
            "state": "NY",
            "country": "US"
          },
          "browser_info": {
            "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "ip_address":"213.93.4.172",
            "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
            "java_enabled": false,
            "javascript_enabled": true,
            "language": "en-US",
            "color_depth": "24",
            "screen_height": "900",
            "screen_width": "1440",
            "timezone_offset": "-300"
          }
        },
        "customer": {
          "first_name": "Ada",
          "last_name": "Lovelace",
          "email": "ada@example.com"
        },
        "return_url": "https://yourplatform.com/checkout/complete"
      }
    }'
  ```
</CodeGroup>

```json theme={null}
{
  "id": "01HACT9012EFGH",
  "currency": "USD",
  "amount": 5000,
  "applicable_fee": 75,
  "charge_amount": 5000,
  "refunded_amount": 0,
  "reference": "order_abc123",
  "action": "confirm",
  "category": "collections",
  "payment_type": "card-acq",
  "payment_type_data": {
    "auth_model": "3ds_required",
    "payment_country": "US",
    "last_four_digits": "1111",
    "card_brand": "visa",
    "card_type": "credit"
  },
  "status": "pending_user_action",
  "entity_id": "ent_01HXYZ1234ABCD",
  "account_id": "acc_01HXYZ5678EFGH",
  "payment_intent_id": "01HXYZ7K3M9PQRST4UVWXY2Z6",
  "return_url": "https://yourplatform.com/checkout/complete",
  "requires_action": true,
  "next_action": {
    "type": "redirect_to_url",
    "url": "https://checkout.nuvion.co/card/01HACT9012EFGH"
  },
  "created": 1735725600000
}
```

Redirect the cardholder to `next_action.url`. After authentication, Nuvion redirects the cardholder to return\_url. Payment details such as status will be appended as query params to the return\_url. Do not treat the redirect as proof of payment; confirm completion from the payment intent status or webhook.
Like so; `<return-url>?payment_intent_action_id=01KVFT796D5M1RQ0S853P4XXXX&status=completed&payment_intent_id=01KVFT2B67R57FWGVDFNZ1XXXX&amount=300&currency=EUR`

### Step 3: Check payment status

Use `GET /payment-intents/{id}` to check the latest status after redirect or while waiting for a webhook.

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.nuvion.dev/payment-intents/01HXYZ7K3M9PQRST4UVWXY2Z6 \
    -H "Authorization: Bearer $NUVION_API_KEY"
  ```
</CodeGroup>

```json theme={null}
{
  "id": "01HXYZ7K3M9PQRST4UVWXY2Z6",
  "currency": "USD",
  "amount": 5000,
  "reference": "order_abc123",
  "description": "Order #1042",
  "status": "completed",
  "entity_id": "ent_01HXYZ1234ABCD",
  "account_id": "acc_01HXYZ5678EFGH",
  "created": 1735725600000
}
```

### Apple Pay

For Apple Pay, set `intent_action.payment_type` to `applepay-acq`. Apple Pay does not use `payment_type_data.data`, full billing address fields, or card encryption in this request. The only required billing field is `payment_type_data.billing_address.country`.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.nuvion.dev/intent-actions \
    -H "Authorization: Bearer $NUVION_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "payment_intent_id": "01HXYZ7K3M9PQRST4UVWXY2Z6",
      "intent_action": {
        "action": "confirm",
        "category": "collections",
        "payment_type": "applepay-acq",
        "payment_type_data": {
          "billing_address": {
            "country": "US"
          }
        },
        "customer": {
          "first_name": "Ada",
          "last_name": "Lovelace",
          "email": "ada@example.com"
        },
        "return_url": "https://yourplatform.com/checkout/complete"
      }
    }'
  ```
</CodeGroup>

### Payment intent statuses

| Status            | Description                                            |
| ----------------- | ------------------------------------------------------ |
| `requires_action` | Intent created — no action submitted yet               |
| `processing`      | Intent action is being processed                       |
| `completed`       | Charge successful                                      |
| `cancelled`       | Intent cancelled before completion                     |
| `failed`          | Payment could not be completed — check `status_reason` |

### Intent action statuses

| Status                | Description                                 |
| --------------------- | ------------------------------------------- |
| `pending`             | Action created and being processed          |
| `pending_user_action` | Awaiting 3DS completion from the cardholder |
| `completed`           | Charge successful                           |
| `failed`              | Charge declined or failed                   |

### Webhooks

Use Payment Intent webhooks as the final confirmation that a card payment succeeded, failed, or was cancelled. This is especially important after a 3DS redirect, because the redirect only tells you the customer returned to your site.

| Event                      | Trigger                                           |
| -------------------------- | ------------------------------------------------- |
| `payment_intent.completed` | Charge successful — funds credited to the account |
| `payment_intent.failed`    | Charge declined or failed                         |
| `payment_intent.cancelled` | Payment Intent cancelled before confirmation      |

Webhook payloads use Nuvion's standard webhook envelope:

```json theme={null}
{
  "event": "payment_intent.completed",
  "data": {
    "id": "01HXYZ7K3M9PQRST4UVWXY2Z6",
    "currency": "USD",
    "amount": 5000,
    "reference": "order_abc123",
    "description": "Order #1042",
    "status": "completed",
    "entity_id": "ent_01HXYZ1234ABCD",
    "account_id": "acc_01HXYZ5678EFGH",
    "created": 1735725600000
  }
}
```

Return a `2xx` response after receiving the webhook. If delivery fails, Nuvion retries with exponential backoff. See [Webhooks](/webhooks/overview) for delivery and retry details.
