# Payments

> Create, retrieve, capture, cancel, and list payments.

A payment moves money from a customer to your account. For most integrations you create a payment
with an amount and a payment method and VINR authorizes and captures it in one step. If you need to
separate authorization from capture, route across acquirers, or handle 3DS yourself, see **Advanced**
below each section.

> **Illustrative content.** Hand-authored to demonstrate the Standard/Advanced pattern; the production
> page will be generated from the VINR OpenAPI spec (roadmap item #1). Field names are representative.

## The payment object

- **id** `string` — Unique identifier for the payment, e.g. `pay_3Nx8a2Lk`.
- **amount** `integer` — Amount intended to be collected, in the smallest currency unit (e.g. cents).
- **currency** `string` — Three-letter [ISO currency code](/docs/payments/payment-methods), e.g. `EUR`.
- **status** `enum` — One of `succeeded`, `requires_action`, `processing`, `failed`, or `canceled`.
- **customer** `object` (expandable) — The customer this payment belongs to, if any. Expand to retrieve the full object.
  - **id** `string` — Customer identifier, e.g. `cust_8aZ2`.
  - **email** `string` — Email address on the customer record.
  - **default\_payment\_method** `string` — The `pm_` used when none is specified.
- **latest\_charge** `object` (expandable) — The most recent charge created by this payment, e.g. `ch_1Pf2`.
  - **id** `string` — Charge identifier.
  - **amount\_captured** `integer` — Amount captured so far, in the smallest currency unit.
  - **payment\_method\_details** `object` — Snapshot of the instrument used (card brand, last4, etc.).

## Create a payment

Pass an amount, currency, and payment method. VINR authorizes and captures in one step, returning a
`succeeded` payment.

`POST /v1/payments`

```bash
curl -X POST https://api.vinr.com/v1/payments \
-H "X-Api-Key: $VINR_SECRET_KEY" \
-d amount=5000 \
-d currency=EUR \
-d payment_method=pm_card_visa \
-d customer=cust_8aZ2
```

```js
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payment = await vinr.payments.create({
amount: 5000,
currency: 'EUR',
payment_method: 'pm_card_visa',
customer: 'cust_8aZ2',
});
```

```python
import vinr

payment = vinr.Payment.create(
  amount=5000,
  currency="EUR",
  payment_method="pm_card_visa",
  customer="cust_8aZ2",
)
```

```ruby
payment = Vinr::Payment.create(
amount: 5000,
currency: 'EUR',
payment_method: 'pm_card_visa',
customer: 'cust_8aZ2',
)
```

```json
{
"id": "pay_3Nx8a2Lk",
"amount": 5000,
"currency": "EUR",
"status": "succeeded",
"customer": "cust_8aZ2",
"latest_charge": "ch_1Pf2"
}
```

#### Advanced (Enterprise) — separate auth & capture, routing, idempotency

Enterprise integrations hold an authorization and capture later (e.g. on fulfillment), and control
how the payment is routed. All parameters below are optional additions to the create call above.

| Parameter                    | Type   | Description                                                           |
| ---------------------------- | ------ | --------------------------------------------------------------------- |
| `capture_method`             | enum   | `automatic` (default) or `manual` to authorize now and capture later. |
| `routing.preferred_acquirer` | string | Pin the payment to a specific acquirer connection.                    |
| `on_behalf_of`               | string | Settle to a connected sub-merchant account (platforms/marketplaces).  |
| `statement_descriptor`       | string | Override the descriptor shown on the customer's statement.            |

```ts
const auth = await vinr.payments.create(
  {
    amount: 5000,
    currency: 'EUR',
    payment_method: 'pm_card_visa',
    capture_method: 'manual',                 // funds held, not captured
    routing: { preferred_acquirer: 'acq_eu_1' },
  },
  { idempotencyKey: `order-${order.id}` },     // retries collapse to one payment
);
// auth.status === "requires_capture"
```

Always send an [idempotency key](/docs/api-reference/idempotency) on enterprise create calls so a
retried request never double-charges.

## Retrieve a payment

Fetch a payment by its ID. Returns the full payment object including the latest charge.

`GET /v1/payments/{id}`

```bash
curl https://api.vinr.com/v1/payments/pay_3Nx8a2Lk \
-H "X-Api-Key: $VINR_SECRET_KEY"
```

```js
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payment = await vinr.payments.retrieve('pay_3Nx8a2Lk');
```

```python
import vinr

payment = vinr.Payment.retrieve("pay_3Nx8a2Lk")
```

```ruby
payment = Vinr::Payment.retrieve('pay_3Nx8a2Lk')
```

```json
{
"id": "pay_3Nx8a2Lk",
"amount": 5000,
"currency": "EUR",
"status": "succeeded",
"customer": "cust_8aZ2",
"latest_charge": "ch_1Pf2",
"created": 1716300000
}
```

## Capture a payment

A payment created with `capture_method: 'manual'` sits in `requires_capture` until you capture it.
Capturing transitions the status to `succeeded` and settles the funds.

`POST /v1/payments/{id}/capture`

```bash
curl -X POST https://api.vinr.com/v1/payments/pay_3Nx8a2Lk/capture \
-H "X-Api-Key: $VINR_SECRET_KEY"
```

```js
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payment = await vinr.payments.capture('pay_3Nx8a2Lk');
```

```python
import vinr

payment = vinr.Payment.capture("pay_3Nx8a2Lk")
```

```ruby
payment = Vinr::Payment.capture('pay_3Nx8a2Lk')
```

```json
{
"id": "pay_3Nx8a2Lk",
"amount": 5000,
"currency": "EUR",
"status": "succeeded",
"customer": "cust_8aZ2",
"latest_charge": "ch_1Pf2",
"created": 1716300000
}
```

#### Advanced (Enterprise) — partial capture & 3DS handling

**Partial capture** — capture less than the authorized amount (e.g. you shipped part of an order);
the remainder is released.

```ts
await vinr.payments.capture('pay_3Nx8a2Lk', { amount_to_capture: 3000 });
```

**3DS / SCA** — when a create returns `status: 'requires_action'`, the customer must complete a
challenge. Confirm the result with the lower-level details call, then capture as usual.

```ts
if (payment.status === 'requires_action') {
  await vinr.payments.confirm('pay_3Nx8a2Lk', { details: challengeResult });
}
```

See [Strong Customer Authentication](/docs/payments/strong-customer-authentication) for the full
challenge flow.

## Cancel a payment

Cancel a payment that has not yet been captured. The status transitions to `canceled` and any
authorization hold on the customer's payment method is released.

`POST /v1/payments/{id}/cancel`

```bash
curl -X POST https://api.vinr.com/v1/payments/pay_3Nx8a2Lk/cancel \
-H "X-Api-Key: $VINR_SECRET_KEY"
```

```js
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payment = await vinr.payments.cancel('pay_3Nx8a2Lk');
```

```python
import vinr

payment = vinr.Payment.cancel("pay_3Nx8a2Lk")
```

```ruby
payment = Vinr::Payment.cancel('pay_3Nx8a2Lk')
```

```json
{
"id": "pay_3Nx8a2Lk",
"amount": 5000,
"currency": "EUR",
"status": "canceled",
"customer": "cust_8aZ2",
"latest_charge": "ch_1Pf2",
"created": 1716300000
}
```

## List payments

Returns a paginated list of payments, newest first. Use the `customer` filter to scope results to a
single customer.

| Parameter  | Type    | Description                                           |
| ---------- | ------- | ----------------------------------------------------- |
| `limit`    | integer | Number of results to return. Default `10`, max `100`. |
| `customer` | string  | Filter payments by customer ID.                       |

`GET /v1/payments`

```bash
curl "https://api.vinr.com/v1/payments?limit=10&customer=cust_8aZ2" \
-H "X-Api-Key: $VINR_SECRET_KEY"
```

```js
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payments = await vinr.payments.list({
limit: 10,
customer: 'cust_8aZ2',
});
```

```python
import vinr

payments = vinr.Payment.list(
  limit=10,
  customer="cust_8aZ2",
)
```

```ruby
payments = Vinr::Payment.list(
limit: 10,
customer: 'cust_8aZ2',
)
```

```json
{
"object": "list",
"data": [
  {
    "id": "pay_3Nx8a2Lk",
    "amount": 5000,
    "currency": "EUR",
    "status": "succeeded",
    "customer": "cust_8aZ2",
    "created": 1716300000
  }
],
"has_more": false
}
```

## Adjust an authorization

Increase or decrease the authorized amount before capture. Useful for hotel incidentals, tips added
after the sale, or removing a cancelled line item. The issuer re-evaluates the adjustment and may
decline an increase.

`POST /v1/payments/{id}/adjust`

```bash
curl -X POST https://api.vinr.com/v1/payments/pay_3Nx8a2Lk/adjust \
-H "X-Api-Key: $VINR_SECRET_KEY" \
-d amount=6500
```

```js
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payment = await vinr.payments.adjust('pay_3Nx8a2Lk', {
amount: 6500,   // new authorized ceiling — up from 5000
});
```

```python
import vinr

payment = vinr.Payment.adjust(
  "pay_3Nx8a2Lk",
  amount=6500,
)
```

```ruby
payment = Vinr::Payment.adjust(
'pay_3Nx8a2Lk',
amount: 6500,
)
```

```json
{
"id": "pay_3Nx8a2Lk",
"amount": 6500,
"amount_authorized": 6500,
"amount_capturable": 6500,
"status": "requires_capture",
"adjustment": {
  "previous_amount": 5000,
  "adjusted_at": 1716301200
}
}
```

#### Advanced — decrease and acquirer limits

Pass a `amount` lower than the current authorized amount to **decrease** the hold. Not all card
networks support authorization decreases — for networks that don't, VINR retains the original hold
and captures only the lower amount at capture time (releasing the difference automatically).

| Parameter | Type    | Description                                                                 |
| --------- | ------- | --------------------------------------------------------------------------- |
| `amount`  | integer | New authorized ceiling in minor units. Must differ from the current amount. |

See [Authorization adjustment](/docs/payments/authorization-adjustment) for the full guide on
industry-specific use cases (hotel, car rental, tips).

## Reverse a payment

A reversal cancels an authorized-but-not-yet-captured payment and immediately releases the hold.
Equivalent to `cancel` for most integrations; use `reverse` for acquirers that distinguish between
a void (same-day same-acquirer release) and a generic cancel.

`POST /v1/payments/{id}/reverse`

```bash
curl -X POST https://api.vinr.com/v1/payments/pay_3Nx8a2Lk/reverse \
-H "X-Api-Key: $VINR_SECRET_KEY"
```

```js
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const payment = await vinr.payments.reverse('pay_3Nx8a2Lk');
```

```python
import vinr

payment = vinr.Payment.reverse("pay_3Nx8a2Lk")
```

```ruby
payment = Vinr::Payment.reverse('pay_3Nx8a2Lk')
```

```json
{
"id": "pay_3Nx8a2Lk",
"amount": 5000,
"currency": "EUR",
"status": "canceled",
"reversal": {
  "id": "rev_2Ym4b",
  "reversed_at": 1716301500
}
}
```

#### Advanced — partial reversal

Pass `amount` to release only part of the authorization. Useful when one item in a multi-item
authorization ships and the rest is cancelled.

```ts
await vinr.payments.reverse('pay_3Nx8a2Lk', { amount: 2000 });
// releases 2000 of the 5000 hold; remaining 3000 stays authorized
```

See [Authorization adjustment](/docs/payments/authorization-adjustment) for the decision matrix
comparing reverse, void, adjust-down, and refund.

## Related events

`payment.succeeded`, `payment.requires_action`, `payment.captured`, `payment.failed`,
`payment.canceled`, `payment.adjusted`, and `payment.reversed`. See [Events](/docs/api-reference/events).
