# FAQ and troubleshooting

> Answers to the questions integrations raise, and what to check when something does not work.

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

## General

### Which currencies and cards are supported?

Payments are in Algerian dinars (`DZD`) only. Payers pay with CIB and Edahabia cards, processed by SATIM.

### How much does Raïs cost?

A paid payment costs SATIM's fee (0.7% of the amount, never less than 10.00 DA and never more than 300.00 DA) plus Raïs's (0.3%); a refund costs 10.00 DA. There is no subscription and no setup fee, a failed payment costs nothing, and every figure excludes VAT. See [Pricing and fees](https://rais.app/docs/pricing.md).

### What are the smallest and largest payments?

The smallest payment is 50.00 DA (5000 centimes), SATIM's minimum. The largest is 999999999999 centimes. The smallest refund is 1.00 DA.

### Can I call the API from a browser or a mobile app?

No. Secret keys must never leave your server, and the API sends no CORS headers on purpose. Create payments on your server and give the browser or the app the `checkout_url`. See [Mobile and app integrations](https://rais.app/docs/mobile.md).

### Where do I create keys and webhook endpoints?

In the dashboard, under **Developers**: **API keys**, **Webhooks** and **Events**.

## Payments

### Why do I get `invalid_amount`?

Amounts are integers in centimes. `5000.5`, `"5000"` and `1500.00` are refused. To charge 1,500 DA, send `150000`.

### Why do I get `parameter_unknown`?

The request has a field the API does not know, usually a typo. The `param` in the error names it.

### How long can a payer take to pay?

A payment stays payable for 24 hours by default. Set `expires_in` to anything from 300 seconds to 30 days. After that its status is `expired` and it cannot be paid.

### The payer came back but the payment is still `pending`

Your server may be asking before the result has reached Raïs. Retrieving a pending payment asks the gateway for the outcome, so retry after a moment, and rely on the webhook. If the payer abandoned the page, it stays `pending` until it expires. Nothing is sent when it expires: see the next question.

### How do I know that a payment expired?

There is no event for it. A payment that nobody paid reads as `expired` as soon as `expires_at` has passed: retrieve it, or compare `expires_at` with the clock. Close the matching order in your own system at that time.

### The payer paid and closed the page. Will I still be told?

Yes. Raïs asks the gateway about unpaid payments in the background, starting about two minutes after the payment session opened and for up to 48 hours, and sends `payment.succeeded` when the gateway says it was paid. See [Payments](https://rais.app/docs/payments.md#when-the-payer-pays-and-never-comes-back).

### My filter has no effect

Unknown **query** parameters are ignored, so `GET /payments?stauts=paid` returns every payment. Check the spelling: the filters are `status`, `reference`, `created_gte` and `created_lte`.

### A payment failed. Can the payer try again?

It depends on why it failed. If the **bank declined the card** (`failed` with `failure.code: payment_declined`), the payment is final: create a new payment, with a new `Idempotency-Key`. If the payer only left the page, pressed **Back to** or met an error before paying, the payment is still `pending`: the same `checkout_url` keeps working until `expires_at`, and the result page offers a **Try again** button. A `cancelled` or `expired` payment is final too.

### Can I send the payment link by e-mail or SMS?

Yes. `checkout_url` is a plain link to the hosted page: share it with the payer. Anyone who holds it can pay it, so send it only to the payer.

## Webhooks

### The signature does not match

The usual causes, in order:

1. You verified **parsed and re-serialised JSON** instead of the raw body. Read the bytes before any parsing.
2. A proxy or a framework changed the body (decoded it, re-encoded it, or added a newline).
3. You used the secret of **another endpoint** or another environment: sandbox and live endpoints have different secrets.
4. You compared the whole header with the hex digest. The header has `t=` and `v1=` parts.
5. Your clock is more than 300 seconds off.

Test your code with the [official test vectors](https://rais.app/docs/webhooks.md#verify-the-signature).

### No webhook arrives

Check, in this order: the endpoint is **enabled** (after 20 consecutive failed deliveries it is switched off); it subscribes to the event type; its **environment** matches the payment (sandbox endpoints get sandbox events only); the address is reachable from the internet and not `localhost` or a private address; a live endpoint uses `https://`; and your server answers with `2xx` and not a redirect. The delivery log of the dashboard shows each attempt and what your server answered.

### I received the same event twice

Delivery is at least once. De-duplicate on the event `id`. See [Webhooks](https://rais.app/docs/webhooks.md#handle-each-event-once).

### How long do retries go on?

Up to 8 attempts over about 20 hours, then the delivery is marked failed and can be resent from the dashboard.

## Checkout and return URLs

### `status=paid` is in the address, but the API says `pending`

Believe the API. The address can be opened by anybody, with any parameters. See [Hosted checkout and return URLs](https://rais.app/docs/checkout.md#never-trust-the-redirect).

### `status=failed` is in the address, but the API says `pending`

The payer's attempt did not go through, but the payment is not over: they can press **Try again** and pay until `expires_at`. Do not cancel the order because of the address. See [Hosted checkout and return URLs](https://rais.app/docs/checkout.md#status-failed-does-not-always-end-the-payment).

### The payer was not sent back to my site

The result page never sends the payer back by itself: it stays on screen so they can read the payment's details, and a **Return to** button opens your `success_url` or `cancel_url`. The button is missing when the payment has no `success_url` or `cancel_url` and your store has no default, or when the address does not pass the rules for the environment (for example `http://` on a live payment), in which case Raïs does not use it.

### The checkout does not show in my iframe

It cannot be embedded: it forbids being framed, to protect payers. Redirect to it or open it in a browser tab.

### The deep link does not open my app

Check that the scheme in `success_url` matches the one your app registers, that the payer's browser session is allowed to open it (use `ASWebAuthenticationSession` or Custom Tabs, not a plain web view), and that you did not add a space or a non-ASCII character to the address.

## Refunds and receipts

### `payment_not_refundable`

Only `paid` and `partially_refunded` payments can be refunded.

### My refund is `pending`

The gateway's answer was lost, and the amount is reserved until the refund is settled by hand. Do not repeat it with a new key, and contact support with the refund id. See [Refunds](https://rais.app/docs/refunds.md#pending-refunds).

### The receipt answers 404

Only paid, partially refunded and refunded payments have a receipt, and a key only sees receipts of its own store and environment. See [Receipts](https://rais.app/docs/receipts.md).

### `insufficient_balance`

A live refund is taken out of your store's balance together with its fee (10.00 DA), so the balance must cover both: "The store's balance is not sufficient to cover this refund and its fee." Top up your balance, wait for payments to come in, or refund a smaller amount. See [Refunds](https://rais.app/docs/refunds.md#what-a-refund-costs).

### Are the fees returned when I refund a payment?

No. The fee of a payment is not returned when it is refunded, in full or in part, and the refund has its own fee of 10.00 DA. See [Pricing and fees](https://rais.app/docs/pricing.md).

## Going live

### My live key answers `401 invalid_api_key`

The key is unknown, revoked or expired, it is used from an address outside its allowlist, or its store is not approved for live payments (any more). The business has to be verified before a live key can exist: see [Going live](https://rais.app/docs/going-live.md). The API never says which of these it is.

### I used a live key with a sandbox payment id

Sandbox and live are separate. The payment is not found (`404`) because it belongs to the other environment.

### Is Raïs PCI DSS or SOC 2 certified?

Raïs does not claim either certification. What the documentation states is what the platform does: the card is typed on a page hosted by Raïs or SATIM and never reaches your servers, keys are stored hashed, saved cards are encrypted. See [Security and safety](https://rais.app/docs/security.md).

## Quick reference

| You see | Probably | Do |
| --- | --- | --- |
| `401 invalid_api_key` | A wrong, revoked or expired key, a call from an address outside the allowlist, or a store that is suspended or not approved for live payments | Check the key, its environment and the store |
| `403 insufficient_scope` | The key lacks the scope | Create a key with the scope |
| `400 invalid_amount` | A float or a string | Send an integer in centimes |
| `400 amount_too_small` | Less than 50.00 DA | Raise the amount |
| `400 invalid_url` | A bad return URL for the environment | Use https, or your app scheme |
| `404 payment_not_found` | Another store or environment, or a bad id | Use the key of the right store and environment |
| `409 payment_not_cancellable` | The payment is not pending | Refund it instead if it is paid |
| `400 insufficient_balance` | A live refund and its fee are more than your balance | Top up your balance, or refund less |
| `422 idempotency_key_reused` | The same key with different parameters | Use a new key for a new request |
| `429 rate_limit_exceeded` | Too many requests, or too many failed authentications from this address | Wait for Retry-After, and stop using revoked keys |
| `503 gateway_unavailable` | The gateway cannot be reached | Retry shortly with the same key |

Still stuck? Write to salam@rais.app with the `Rais-Request-Id` of the request that failed.
