# Payments

Create, retrieve, and list payments.

`POST /api/v1/payments` is **synchronous and auto-captured** — when the response comes back, the funds are already on their way (or the card has been refused). `Idempotency-Key` is required.

## The Payment object

```json
{
  "id": "pay_01HABCXYZ...",
  "status": "succeeded",
  "amount": 2999,
  "currency": "CHF",
  "reference": "order_1001",
  "customer": "cus_01HCUSXYZ...",
  "payment_method": {
    "type": "card",
    "brand": "visa",
    "last4": "1111",
    "exp_month": 3,
    "exp_year": 2030
  },
  "next_action": null,
  "failure": null,
  "created_at": "2026-05-20T12:00:00Z"
}
```

| Field            | Type           | Notes                                                               |
| ---------------- | -------------- | ------------------------------------------------------------------- |
| `id`             | string         | Stable, prefixed with `pay_`.                                       |
| `status`         | string         | `succeeded` · `failed` · `requires_action` (3DS challenge pending). |
| `amount`         | integer        | Minor units (cents/rappen).                                         |
| `currency`       | string         | ISO 4217 (`CHF`, `EUR`, `USD`, etc.).                               |
| `reference`      | string         | Your own reference, free text.                                      |
| `customer`       | string \| null | `cus_...` ID of the attached customer, if any.                      |
| `payment_method` | object         | Brand, last 4, expiry — **never** full PAN.                         |
| `next_action`    | object \| null | Present only when `status: "requires_action"`.                      |
| `failure`        | object \| null | Present only when `status: "failed"`.                               |
| `created_at`     | string         | ISO 8601 UTC.                                                       |

***

## Create a payment

```
POST /api/v1/payments
```

### Required headers

```
Authorization: Bearer sk_test_...
Content-Type: application/json
Idempotency-Key: <unique-per-attempt>
```

### Required body fields

| Field            | Type    | Notes                     |
| ---------------- | ------- | ------------------------- |
| `amount`         | integer | Minor units. Must be > 0. |
| `currency`       | string  | ISO 4217.                 |
| `payment_method` | object  | See below.                |

### `payment_method` (card)

| Field         | Type    | Notes                                                                                    |
| ------------- | ------- | ---------------------------------------------------------------------------------------- |
| `type`        | string  | `card`                                                                                   |
| `number`      | string  | The card number. Test environment only — see [Test cards](/api-reference/test-cards.md). |
| `exp_month`   | integer | 1-12.                                                                                    |
| `exp_year`    | integer | 4-digit year.                                                                            |
| `cvc`         | string  | 3 or 4 digits.                                                                           |
| `holder_name` | string  | **Required.** The name on the card.                                                      |

### Optional body fields

| Field                         | Type             | Notes                                                                                                                                                                                                    |
| ----------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `reference`                   | string           | Your own reference. Free text.                                                                                                                                                                           |
| `customer`                    | string \| object | Either an existing `cus_...` ID or an inline customer payload.                                                                                                                                           |
| `metadata`                    | object           | Up to 20 string keys, values ≤ 500 chars. Echoed back on read.                                                                                                                                           |
| `shopper_ip`                  | string           | The shopper's IP. Improves fraud scoring.                                                                                                                                                                |
| `browser_info`                | object           | `user_agent`, `accept_header`. Improves fraud scoring.                                                                                                                                                   |
| `risk_data`                   | object           | Nested risk signals (basket value, tenure, promo). Forwarded to the upstream risk engine.                                                                                                                |
| `authentication`              | string           | `automatic` to opt into 3-D Secure.                                                                                                                                                                      |
| `success_url` / `failure_url` | string           | **Required on every card payment when the connection has 3-D Secure enabled** (the sandbox default) — not only when the request sets `authentication: "automatic"`. Both must be HTTPS with no fragment. |

### Example — successful Visa

```bash
curl -X POST https://staging.swisspay.ai/api/v1/payments \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "amount": 2999,
    "currency": "CHF",
    "reference": "order_1001",
    "payment_method": {
      "type": "card",
      "number": "4111111111111111",
      "exp_month": 3,
      "exp_year": 2030,
      "cvc": "737",
      "holder_name": "Jane Doe"
    }
  }'
```

### Example — with an inline customer

The customer is created if it doesn't already exist (matched by `email` within your account) and attached to the payment.

```json
{
  "amount": 2999,
  "currency": "CHF",
  "reference": "order_1003",
  "customer": {
    "email": "new@example.com",
    "name": "New Customer",
    "external_id": "user_99"
  },
  "payment_method": { /* card */ }
}
```

### Example — with fraud signals

```json
{
  "amount": 2999,
  "currency": "CHF",
  "reference": "order_risk_1004",
  "shopper_ip": "203.0.113.42",
  "browser_info": {
    "user_agent": "Mozilla/5.0 (Macintosh) AppleWebKit/605",
    "accept_header": "text/html,application/xhtml+xml"
  },
  "risk_data": {
    "basket": { "value": 2999, "items_count": 2 },
    "customer_tenure_days": 184,
    "promo_code": "WELCOME"
  },
  "payment_method": { /* card */ }
}
```

These fields are forwarded to our upstream risk engine but **not echoed back** on the response. Confirm acceptance by checking HTTP 201 and `status: "succeeded"`.

### Example — 3-D Secure (automatic)

```json
{
  "amount": 8999,
  "currency": "EUR",
  "reference": "order_3ds_visa",
  "authentication": "automatic",
  "success_url": "https://merchant.example/checkout/return",
  "failure_url": "https://merchant.example/checkout/return",
  "payment_method": {
    "type": "card",
    "number": "4871049999999910",
    "exp_month": 3,
    "exp_year": 2030,
    "cvc": "737",
    "holder_name": "Jane Doe"
  }
}
```

Two possible outcomes:

1. **Frictionless.** HTTP 201, `status: "succeeded"`, identical to a normal sale.
2. **Challenge required.** HTTP 200, `status: "requires_action"`, plus:

   ```json
   "next_action": {
     "type": "redirect_to_url",
     "redirect_url": "https://staging.swisspay.ai/checkout/3ds/3ds_..."
   }
   ```

   Redirect the customer to `next_action.redirect_url`. After they complete (or abandon) the challenge, we redirect them to `success_url` or `failure_url` with `?payment_id=pay_...` appended.

The **source of truth** for the final state is `GET /api/v1/payments/:id`.

### Validation rules for return URLs

* `success_url` and `failure_url` are required on **every** card payment whenever the merchant's connection has 3-D Secure enabled — including the frictionless happy path and payments that never set `authentication: "automatic"`. Because the sandbox connection ships with 3DS enabled, treat them as required in test mode.
* Both must be HTTPS. No URL fragment (`#...`) allowed.
* `payment_method.holder_name` is required on every card payment.

### Configuring 3DS

3DS is enabled per processor connection from **Settings → Connected providers** in the dashboard.

***

## Retrieve a payment

```
GET /api/v1/payments/{id}
```

Returns the same Payment object as `POST /payments`.

```bash
curl https://staging.swisspay.ai/api/v1/payments/pay_01HABCXYZ \
  -H "Authorization: Bearer sk_test_..."
```

Unknown IDs — including IDs that belong to another merchant — return `404 Not Found` with no body.

***

## List payments

```
GET /api/v1/payments?page=1&per_page=20
```

| Query param | Default | Max                                                                                                                        |
| ----------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
| `page`      | `1`     | —                                                                                                                          |
| `per_page`  | `20`    | `100`                                                                                                                      |
| `customer`  | —       | A `cus_...` ID. Returns only payments attached to that customer. Unknown IDs return an empty list (no cross-account leak). |

### Example — payments for one customer

```bash
curl 'https://staging.swisspay.ai/api/v1/payments?customer=cus_01HCUSXYZ' \
  -H "Authorization: Bearer sk_test_..."
```

Response:

```json
{
  "data": [ /* payment objects */ ],
  "page": 1,
  "per_page": 20,
  "total_count": 156,
  "has_more": true
}
```

***

## See also

* [Idempotency](/api-reference/idempotency.md) — pick the right `Idempotency-Key`.
* [Errors](/api-reference/errors.md) — every error code we return.
* [Test cards](/api-reference/test-cards.md) — sandbox card numbers.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://support.swisspay.ai/api-reference/payments.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
