# Payments

> Create, retrieve, list and cancel payments: amounts, statuses, metadata, items and expiry.

Source: https://rais.app/docs/payments

A **payment** is one attempt to collect an amount from a payer. You create it on your server, redirect the payer to its hosted checkout, and follow it until it ends. This guide covers everything you can set and read; the exact schema of every endpoint is in the [API reference](https://rais.app/docs/api.md).

## Create a payment


**Request**

```bash
curl https://api.rais.app/v1/payments \
  -H "Authorization: Bearer rais_sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: b7e1c1c6-6f0a-4d2f-9d8b-3a5c2f1e7a90" \
  -d '{
  "amount": 500000,
  "description": "Order #1042",
  "reference": "order-1042",
  "success_url": "https://shop.example/thanks",
  "cancel_url": "https://shop.example/cart"
}'
```

**Response** (`201`)

~~~json
{
  "id": "5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33",
  "object": "payment",
  "status": "pending",
  "amount": 500000,
  "currency": "DZD",
  "description": "Order #1042",
  "reference": "order-1042",
  "customer": {
    "id": null,
    "name": "Amina Benali",
    "email": "amina@example.dz",
    "phone": "+213550000000"
  },
  "items": [
    {
      "name": "Running shoes",
      "description": null,
      "quantity": 2,
      "unit_amount": 250000
    }
  ],
  "metadata": {
    "cart_id": "c_8812"
  },
  "livemode": false,
  "payment_page": "rais",
  "checkout_url": "https://pay.rais.app/pay/5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33",
  "success_url": "https://shop.example/thanks",
  "cancel_url": "https://shop.example/cart",
  "expires_at": 1793003600,
  "created": 1793000000,
  "paid_at": null,
  "amount_refunded": 0,
  "refunded": false,
  "fees": null,
  "net": null,
  "receipt_url": null,
  "failure": null,
  "payment_method": null,
  "processor": {
    "name": "satim",
    "order_id": "V721uPPfNNofVQAAABL3",
    "approval_code": null,
    "response_code": null
  }
}
~~~


Redirect the payer to `checkout_url`. The payment is `pending` until they pay. The payer's page lives on `https://pay.rais.app` and the API on `https://api.rais.app`: you call the API from your server, and only ever send the payer to the page.

## Amounts

Amounts are **integers in centimes**: 1 DA is 100 centimes, so `500000` is 5,000.00 DA. A float (`5000.5`) or a string (`"5000"`) is refused with `invalid_amount`.

- The smallest payment is 5000 centimes (50.00 DA), SATIM's minimum. A smaller amount, zero and negative numbers included, answers `amount_too_small`.
- The largest is 999999999999 centimes. A larger amount answers `amount_too_large`.
- The only currency is `DZD`, which is also the default. Any other value answers `currency_not_supported`.

Always compute the amount on your server from your own prices. An amount that comes from the browser can be changed by the payer.

## Parameters


- `amount` (integer, required unless `items` is given): the amount in centimes. When you send both, they must agree, or the answer is `amount_mismatch`.
- `currency` (string, optional): `DZD`.
- `description` (string, optional, at most 512 characters): what the payment is for. The payer sees it on the checkout and SATIM receives it.
- `reference` (string, optional, at most 64 characters): your own identifier of the order. Returned as is, and searchable with `GET /payments?reference=`.
- `customer` (object, optional): who is paying. Once the payment is paid, Raïs links it to a customer record made from these details and fills in `customer.id`; it is `null` while the payment is pending. A card is kept under that record only if the payer chooses to save it: see [Saved cards](https://rais.app/docs/security.md#saved-cards).
  - `name` (string, at most 255 characters): the payer's full name.
  - `email` (string, at most 255 characters): their e-mail address. A malformed address answers `invalid_email`.
  - `phone` (string, at most 50 characters): their phone number.
- `customer_id` (string, optional): the id of one of **your** customers, one of the store the key belongs to (any other id answers `customer_not_found`). The customer's page in the dashboard shows it, and a paid payment returns it as `customer.id`. It fills in the payer's name and e-mail. On the Raïs payment page it also lets the payer choose to save their card for next time, and pay with a card they saved earlier: they pick it and confirm with 3-D Secure (the card and its security code are stored encrypted, for your store only). Saved cards are kept per mode: a sandbox payment never offers a live card, nor a live payment a sandbox one.
- `items` (array, optional, at most 50 lines): what is being bought, shown on the receipt.
  - `name` (string, required): what is being bought.
  - `description` (string, optional): more detail.
  - `quantity` (integer, required): 1 or more.
  - `unit_amount` (integer, required): the price of one unit, in centimes.
- `success_url` (string, optional): where the payer goes after paying. See [Hosted checkout and return URLs](https://rais.app/docs/checkout.md).
- `cancel_url` (string, optional): where the payer goes if they cancel or the payment fails.
- `metadata` (object, optional): up to 20 pairs of your own. Keys are at most 40 characters; values must be strings of at most 500 characters.
- `payment_page` (string, optional): `rais` for Raïs' hosted checkout or `satim` for SATIM's own page. Defaults to your store's setting.
- `language` (string, optional): `en`, `fr` or `ar`, the language of the checkout and of the receipt.
- `expires_in` (integer, optional): seconds the payment stays payable, from 300 to 2592000 (30 days). Defaults to 86400 (24 hours).


Any field of the JSON body that the API does not know is refused with `parameter_unknown`, which catches typos such as `ammount`. Unknown **query** parameters are ignored instead: `GET /payments?stauts=paid` returns every payment, so check the spelling of your filters.

### Items

When you describe the order with `items`, the amount can be left out: it is the sum of `quantity` times `unit_amount` over the lines. Items appear on the receipt, so give each line a clear name.


**Request**

```bash
curl https://api.rais.app/v1/payments \
  -H "Authorization: Bearer rais_sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "name": "Running shoes",
      "quantity": 2,
      "unit_amount": 250000
    },
    {
      "name": "Delivery",
      "quantity": 1,
      "unit_amount": 80000
    }
  ],
  "success_url": "myapp://pay/result",
  "language": "fr"
}'
```


The payment above is for 580,000 centimes, that is 5,800.00 DA.

### Reference and metadata

Use `reference` for the identifier you search by, and `metadata` for anything else you want back with the payment, such as a cart id. Both come back on the payment, in `GET` requests and in events. Do not put secrets in either of them: `reference` is also added to the address the payer is sent back to.

## Statuses

| Status | Meaning |
| --- | --- |
| `pending` | Created and waiting for the payer. The payer may try again on the same link until `expires_at`. This is the only status in which a payment can be paid or cancelled. |
| `paid` | The money was received. `paid_at`, `fees`, `net`, `payment_method` and `receipt_url` are set. |
| `failed` | The bank declined the card, or the payment ended in an error. `failure` has a `code` (`payment_declined`, `processing_error` or `gateway_unavailable`) and a `message` for you, not for the payer. |
| `cancelled` | You cancelled it while it was pending. |
| `expired` | Nobody paid before `expires_at`. No event is sent when a payment expires. |
| `partially_refunded` | Part of the amount was returned. `amount_refunded` says how much. |
| `refunded` | All of the amount was returned. |
| `reversed` | SATIM reversed the authorisation after the payment. |

`pending` leads to `paid`, `failed`, `cancelled` or `expired`. A `paid` payment can become `partially_refunded`, then `refunded`, or `reversed`. Money states only move forward. A payment that ended `failed` because the bank declined the card, `cancelled` or `expired` cannot be paid later: create a new one.

One rare exception: a payment that ended `failed` with `failure.code` `processing_error` can still turn `paid`, when the gateway answered with an error at the moment the payer came back although the card had been charged. Raïs keeps asking the gateway about such payments (see below), and when it confirms the charge you receive `payment.succeeded`. Treat `payment.succeeded` as the final word even if you saw `payment.failed` first.

A payment is `expired` as soon as `expires_at` has passed without payment, even before anything else touches it. Nothing is sent when that happens, so do not wait for an event: when the order matters, compare `expires_at` with the clock, or retrieve the payment. A payment created in the dashboard with the expiry "Never" has `expires_at: null` and is payable until you cancel it.

### A paid payment

When a payment is `paid`, it looks like this (the same object a `payment.succeeded` event carries):

~~~json
{
  "id": "5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33",
  "object": "payment",
  "status": "paid",
  "amount": 500000,
  "currency": "DZD",
  "description": "Order #1042",
  "reference": "order-1042",
  "customer": {
    "id": "910158b6-8677-4d31-8779-919c4ea6428a",
    "name": "Amina Benali",
    "email": "amina@example.dz",
    "phone": "+213550000000"
  },
  "items": [
    {
      "name": "Running shoes",
      "description": null,
      "quantity": 2,
      "unit_amount": 250000
    }
  ],
  "metadata": {
    "cart_id": "c_8812"
  },
  "livemode": false,
  "payment_page": "rais",
  "checkout_url": "https://pay.rais.app/pay/5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33",
  "success_url": "https://shop.example/thanks",
  "cancel_url": "https://shop.example/cart",
  "expires_at": 1793003600,
  "created": 1793000000,
  "paid_at": 1793000300,
  "amount_refunded": 0,
  "refunded": false,
  "fees": {
    "satim": 3500,
    "rais": 1500,
    "total": 5000
  },
  "net": 495000,
  "receipt_url": "https://pay.rais.app/pay/5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33/receipt.pdf",
  "failure": null,
  "payment_method": {
    "type": "card",
    "brand": "dahabia",
    "last4": "7215",
    "masked": "628058**7215"
  },
  "processor": {
    "name": "satim",
    "order_id": "V721uPPfNNofVQAAABL3",
    "approval_code": "913180",
    "response_code": "00"
  }
}
~~~

`payment_method` shows the card scheme (`cib`, `dahabia` or `unknown`), the last four digits and a masked number, never more than the first six and last four digits. `processor` holds what SATIM knows: its order id, the approval code and the response code (`00` means approved).

### What a payment costs

A paid payment carries what it cost you and what you receive:

- `fees` has the `satim` part, the `rais` part and their `total`, in centimes, **excluding VAT**. It is `null` until the payment is `paid`: a payment that is pending, failed, expired or cancelled costs nothing.
- `net` is `amount` minus `fees.total`, before any refund: what you receive for this payment. In the example above, 500000 − 5000 = 495000 centimes (4,950.00 DA).
- The fees are set once, when the payment is paid, and are **not returned** when it is refunded, in full or in part: each refund has its own fee (see [Refunds](https://rais.app/docs/refunds.md)).
- A sandbox payment shows the **simulated** fees it would have cost in live: nothing is charged and no balance changes. The example above is a sandbox payment.
- A payment paid before fees were introduced has `fees: null` and `net: null`: nothing is charged afterwards for it.

The price list, when a fee is charged and worked examples are in [Pricing and fees](https://rais.app/docs/pricing.md).

### What the payer can do before it ends

While a payment is `pending`, the payer can leave the page, come back to the same `checkout_url` and try again, until `expires_at`. Closing the tab, pressing **Back to**, a mistyped card number or a SATIM form error do not change the status. The result page the payer lands on after an unsuccessful attempt offers a **Try again** button for as long as the payment can still be paid. A card refused by the bank is different: the payment becomes `failed` at once and its link no longer takes a payment.

### When the payer pays and never comes back

Some payers pay and close the tab before Raïs sees the result. Raïs does not leave such a payment `pending`: a background check asks the payment gateway about unpaid payments, the first time about 2 minutes after the gateway session was opened, then again after 5 minutes, 15 minutes, 1 hour, 6 hours and 24 hours (each wait counted from the previous check), and stops 48 hours after the session was opened. When the gateway says the payment was paid, the payment becomes `paid` and `payment.succeeded` is sent, **without the payer ever returning to your site**. Reading the payment through the API also asks the gateway (at most every 15 seconds).

So never rely on the browser redirect alone: confirm the payment with the webhook or with `GET /payments/{id}`. See [Hosted checkout and return URLs](https://rais.app/docs/checkout.md).

## Retrieve a payment


**Request**

```bash
curl https://api.rais.app/v1/payments/5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33 \
  -H "Authorization: Bearer rais_sk_test_..."
```


If the payment is still `pending`, Raïs asks the gateway for the outcome (at most every 15 seconds), so a payer who comes back from the checkout always sees the truth, even if their browser never reached Raïs. Retrieving a payment is the right way to confirm a result. A payment of another store or of the other environment answers `404`.

## List payments


**Request**

```bash
curl 'https://api.rais.app/v1/payments?status=paid&limit=3' \
  -H "Authorization: Bearer rais_sk_test_..."
```


Payments come back newest first, 20 at a time by default. Filter by `status`, by your own `reference`, or by creation time with `created_gte` and `created_lte` (Unix seconds). `status=pending` leaves out the payments that are past `expires_at`: ask for `status=expired` to see those. Payment links created in the dashboard are payments like any other and are listed here too. See [Pagination](https://rais.app/docs/pagination.md).

## Cancel a payment

Cancel a payment that is no longer needed so that its checkout cannot be used.


**Request**

```bash
curl -X POST https://api.rais.app/v1/payments/5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33/cancel \
  -H "Authorization: Bearer rais_sk_test_..."
```


Only a `pending` payment can be cancelled. Any other status answers `409` with `payment_not_cancellable`: a payment that is already `paid` has to be refunded instead. The same answer comes back while the payer is in the middle of a 3-D Secure step for that payment: try again in a moment. A successful cancellation emits a `payment.cancelled` event.

## Retrying safely

Send an `Idempotency-Key` with every payment you create. If a request times out and you send it again with the same key, you get the original payment back and nothing is created twice. See [Idempotency and retries](https://rais.app/docs/idempotency.md).

## What to read next


- [Hosted checkout and return URLs](https://rais.app/docs/checkout.md): the page your payer sees and where they go afterwards.
- [Webhooks](https://rais.app/docs/webhooks.md): learn the outcome without polling.
- [Refunds](https://rais.app/docs/refunds.md): return money for a paid payment.
