Skip to content

Accept payments

Payments

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

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.

Create a payment

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"
}'
Response201
{
  "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

amountintegerrequiredrequired unless `items` is given

The amount in centimes. When you send both, they must agree, or the answer is amount_mismatch.

currencystring

DZD.

descriptionstringat most 512 characters

What the payment is for. The payer sees it on the checkout and SATIM receives it.

referencestringat most 64 characters

Your own identifier of the order. Returned as is, and searchable with GET /payments?reference=.

customerobject

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.

namestringat most 255 characters

The payer's full name.

emailstringat most 255 characters

Their e-mail address. A malformed address answers invalid_email.

phonestringat most 50 characters

Their phone number.

customer_idstring

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.

itemsarrayat most 50 lines

What is being bought, shown on the receipt.

namestringrequired

What is being bought.

descriptionstring

More detail.

quantityintegerrequired

1 or more.

unit_amountintegerrequired

The price of one unit, in centimes.

success_urlstring

Where the payer goes after paying. See Hosted checkout and return URLs.

cancel_urlstring

Where the payer goes if they cancel or the payment fails.

metadataobject

Up to 20 pairs of your own. Keys are at most 40 characters; values must be strings of at most 500 characters.

payment_pagestring

rais for Raïs' hosted checkout or satim for SATIM's own page. Defaults to your store's setting.

languagestring

en, fr or ar, the language of the checkout and of the receipt.

expires_ininteger

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.

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

StatusMeaning
pendingCreated 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.
paidThe money was received. paid_at, fees, net, payment_method and receipt_url are set.
failedThe 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.
cancelledYou cancelled it while it was pending.
expiredNobody paid before expires_at. No event is sent when a payment expires.
partially_refundedPart of the amount was returned. amount_refunded says how much.
refundedAll of the amount was returned.
reversedSATIM 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):

{
  "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).
  • 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.

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.

Retrieve a payment

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

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.

Cancel a payment

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

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.