# Payments

> Create, retrieve, list and cancel hosted payments; statuses, references, metadata and payment methods.

`/v1/payments` is the **hosted-payment API**: each Payment has a Devzity checkout page. (Payments you record by hand in the dashboard, such as cash received, are not part of API v1.)

## Create

```js
const payment = await devzity.payments.create({
  amount: 500,
  externalId: "order-123",
  returnUrl: "https://shop.example/payment-return",
  metadata: { plan: "pro" },
})
```

| Field | Notes |
| --- | --- |
| `amount` | Required. Taka as a decimal string with at most two decimals, e.g. `"500.00"`. |
| `external_id` | Your own id (for example an order id). Unique per business **per environment**. Creating a second payment with the same value returns `external_id_conflict`. |
| `merchant_reference` | A human reference shown on receipts. Not unique. |
| `description` | What the customer is paying for. |
| `metadata` | Up to 20 string key/value pairs (keys ≤ 64 characters, values ≤ 500). Never shown to the customer. |
| `return_url` | Where checkout offers to send the customer afterwards. See [Checkout](https://docs.devzity.com/v1/checkout.md). |
| `allowed_methods` | Restrict to `BKASH` and/or `NAGAD`. Default: all your enabled providers. |
| `allowed_channels` | Restrict how the customer may pay (below). Default: every channel you accept. |

### Amounts

For an exact value, pass a string: `amount: "500.50"`. A plain number is convenient for simple values (`amount: 500`). The SDK is deliberately conservative: an amount that can't be represented exactly with at most two decimals — such as `0.1 + 0.2`, `0.001`, `NaN` or a huge number — is **rejected**, never silently rounded. That is a feature: it stops floating-point surprises from becoming wrong charges.

### Payment types (channels)

| API value | Shown to the customer as |
| --- | --- |
| `SEND_MONEY` | Send Money |
| `PAYMENT` | Payment |
| `CASH_IN` | Cash In |
| `BANK_TRANSFER` | Bank / Card Add Money |

Each type is independent. `BANK_TRANSFER` payments don't ask for the customer's phone number.

## Retrieve, list, cancel

```js
await devzity.payments.retrieve(payment.id)
await devzity.payments.list({ limit: 20, status: "PAID" })
await devzity.payments.cancel(payment.id)   // only while PENDING
```

`GET /v1/payments` supports `status`, `external_id`, `created_from`, `created_to`, `limit` and `starting_after`. See [Pagination](https://docs.devzity.com/v1/pagination.md).

## Statuses

| Status | Meaning |
| --- | --- |
| `PENDING` | Created; waiting for the customer to pay. |
| `PAID` | Payment confirmed. |
| `CANCELLED` | Cancelled before payment. |
| `REVERSED` | A paid payment was reversed in Devzity's records. This does **not** mean money was automatically refunded to the customer by bKash or Nagad. |

There is no `expired` or `failed` status: a payment stays `PENDING` until it is paid or you cancel it.

## The Payment object

`id`, `livemode`, `status`, `amount`, `currency` (`BDT`), `description`, `merchant_reference`, `external_id`, `metadata`, `allowed_methods`, `allowed_channels`, `return_url`, `checkout_url`, `created_at`, `paid_at`, `cancelled_at`. Every field is documented in the [API reference](https://docs.devzity.com/v1/api-reference.md). The SDK returns the same object in camelCase (`payment.checkoutUrl`, `payment.externalId`, …).
