Skip to main content
All articles
Guide20 min read

Stripe in Morocco in 2026: not available — and how to get paid anyway

Stripe is not available in Morocco. Why, what the workarounds really cost, and how to collect in dirhams with an API whose vocabulary you already know.

You searched for "Stripe Morocco" because your product is ready and the only thing missing is payment collection. The short answer fits in one sentence: Morocco is not on the list of countries where Stripe is available, so a business established in Morocco cannot open an account there to charge its customers. The useful answer is longer — it explains why, what the workarounds doing the rounds are really worth, and how to wire up, in a handful of calls, a Moroccan gateway whose vocabulary you already know: payment session, signed webhook, idempotency key, test and live keys.

The answer in 50 words

No, Stripe is not available in Morocco in 2026. The official list of supported countries, published at stripe.com/fr/global, does not include Morocco, and no launch date has been announced publicly. A Moroccan business therefore cannot open an account there to charge its customers.

The rest of this article starts from that fact, and from nothing else. We compare neither pricing nor features: we explain why things are the way they are, what happens when you try to work around it, and how to collect payments in dirhams today with an integration that looks like the ones you have already built.

Why a foreign payment service cannot simply set up shop in Morocco

It is tempting to read this absence as a mere rollout delay. It is rather the consequence of a framework: in Morocco, collecting money on behalf of a third party is a regulated activity, and three constraints alone explain why a service designed elsewhere does not open up here overnight. None of the three can be fixed with a line of code.

The Bank Al-Maghrib licence

Receiving a buyer's money, holding it while the payment is processed, then paying it out to a merchant: that is the business of a payment institution, and it requires a licence from Bank Al-Maghrib. The licence is not a formality you request online. It commits an entity incorporated under Moroccan law, with its own capital, its governance, its internal-control and compliance obligations, and it places merchants' funds under the supervision of the central bank.

For a merchant, that boils down to one simple question to put to any provider: which entity holds the licence, and do my funds pass through it? A foreign service with no licensed Moroccan entity cannot answer it — and that is the underlying reason it does not open accounts for Moroccan businesses. For such a service, opening Morocco is not a matter of adding a country to a drop-down; it means creating a local entity, capitalising it, getting it licensed and running it under supervision.

Settlement in dirhams, into a Moroccan account

A Moroccan business invoices in dirhams, keeps its books in dirhams and pays its suppliers, its salaries and its taxes in dirhams. The money it collects therefore has to arrive in dirhams, in an account held in Morocco, with a RIB that lets the accountant match every sale to every movement.

A service that settles its merchants in a foreign currency, into an account held outside Morocco, sits outside that circuit. Bringing the funds home falls under foreign-exchange regulation; every conversion opens a gap between what the customer paid and what the business records; and the money has to be repatriated before it can be used in Morocco. This is not an integration detail: it is why local settlement, in dirhams, into an account in the company's name, is the first criterion for a payment provider in Morocco — before the commission, before the documentation.

VAT and the invoice

When your customers are in Morocco, your sales there are in principle subject to Moroccan VAT, and your invoices must reflect it. The provider collecting the payment has to let you build that chain — the sale, the payment, the payout — with no conversion to explain and no foreign intermediary to reconcile when the return is filed.

A payment collected by a foreign entity complicates that chain instead of simplifying it: the money left in one currency, comes back in another, on a date that is no longer the date of the sale. We come back to this in the next section, with the usual caveat: your chartered accountant or your adviser remains the only person who can rule on your specific situation.

The workarounds doing the rounds, and what they really cost

On developer forums and in founders' groups, two ideas come up as soon as the question is asked. They are not absurd — they answer a real need — but they have consequences that are rarely discovered before they have been suffered. We state them cautiously: there is no universal figure, and every situation deserves to be checked with your adviser before taking a single step.

Setting up a company abroad

The first workaround is to incorporate a company outside Morocco — a US LLC through an online incorporation programme, a British or French company — and to open the account in that company's name. Technically, it works: the account opens, payments go through, the checkout page displays.

What changes is everything else:

  • The funds are collected outside Morocco, by a foreign entity. Your revenue no longer arrives in your Moroccan company; it arrives in a company you now have to keep alive, with its own bookkeeping, its filings and its running costs.
  • The tax obligations of the country of incorporation come on top of your own. A US, British or French company has its own returns to file, even when all of its real activity happens in Morocco.
  • The Moroccan obligations do not go away. If your customers are Moroccan and you operate from Morocco, the question of VAT on those sales and that of foreign-exchange regulation — the position of the Office des Changes on income collected abroad by a resident — arise in full. That is the point to check first with your adviser, before opening anything at all.
  • The risk of closure. An account opened by a company whose activity, directors and customers are all in an unsupported country can be reviewed, suspended or closed — with funds frozen for the duration of the review. You choose neither the moment nor the length.
  • Your Moroccan customers, meanwhile, are still in Morocco. Moroccan cards are not always accepted by a foreign account, and a buyer who sees an amount in a currency they don't recognise, or an authorisation declined by their bank, abandons the basket. You have solved your payment-collection problem by creating one for each of your customers.

Going through someone else's account

The second workaround is to use the account of a friend, a partner or a service provider established in a supported country, who then passes the funds on to you. Payment services' terms of use generally rule this arrangement out — an account is opened in the name of an entity for that entity's business, not someone else's — and it exposes both parties: the account holder, who is collecting for a business that is not theirs and carries its disputes, and you, who depend on a pass-through with no contractual framework, no RIB in your name and no recourse if the account is closed.

Add the questions the previous section has already raised — where are the funds, who declares them, how do they get back to Morocco — and the workaround costs more than the problem it claims to solve.

What to take away

These arrangements answer a question of availability with a question of structure. If your company, your customers and your business are in Morocco, the proportionate answer is to collect payments in Morocco, in dirhams, into an account in your name, with a licensed provider — and to save the energy of incorporating abroad for the day you actually have customers abroad.

Collecting payments in Morocco with ChariPay: the vocabulary you already know

ChariPay is Chari's payment gateway, operated by Chari Money, a payment institution licensed by Bank Al-Maghrib. If you have already integrated a modern gateway, you are not starting from scratch: the concepts are the same, the API is REST, amounts are in dirhams and the result arrives through a signed webhook. Here is the mapping, concept by concept.

What you are looking forWhat you find at ChariPay
Payment session and hosted checkout pagePOST /v1/payment-sessions returns a checkoutUrl you redirect the buyer to
Payment link, no codePOST /v1/payment-links or the portal: sent by e-mail, QR code, PDF poster, single or multiple use
Signed webhookHMAC-SHA256 in X-CHARI-SIGNATURE, timestamp in X-CHARI-TIMESTAMP, ±5 min tolerance
Idempotency keyIdempotency-Key header on creates, plus an externalId that is unique per environment
Test and live keyschari_sk_test_… and chari_sk_live_…, on a single base URL, https://api-psp.charipay.ma
Test card4918914107195005, CVV 123, 3-D Secure code 555
Full or partial refundPOST /v1/refunds, 202 response — and refunding a customer is free
SubscriptionsPOST /v1/subscriptions, with a billing schedule and automatic retries at D+1, D+3 and D+7
MetadataA free-form metadata object (up to 4 KB), returned in the payment webhook
ErrorsA single envelope { "error": { "code", "message" }, "correlationId" }

The three building blocks that follow — the session, the webhook, idempotency — are enough for a first live payment. The rest plugs in afterwards, at your product's pace.

The payment session: one call, one URL

You create a session server-side with the amount in dirhams — there is no currency field, everything is in MAD, in major units with two decimals — your order reference, the buyer and the return URLs. The response contains the URL of the hosted checkout page, where the buyer pays by card with 3-D Secure or chooses to pay in cash at an agency. The API key travels in the X-CHARI-PAY-API-KEY header, and never in code that runs on the buyer's side.

bash
curl -X POST 'https://api-psp.charipay.ma/v1/payment-sessions' \
  -H 'X-CHARI-PAY-API-KEY: chari_sk_test_...' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: commande-2026-0421-paiement' \
  -d '{
    "amount": 250.00,
    "orderId": "ORD-2026-0421",
    "externalId": "commande-2026-0421",
    "config": {
      "customer": {
        "email": "amine.bennani@example.com",
        "firstName": "Amine",
        "lastName": "Bennani",
        "phone": "+212600000000"
      },
      "urls": {
        "accept": "https://votre-boutique.ma/paiement/succes",
        "decline": "https://votre-boutique.ma/paiement/echec",
        "notification": "https://votre-boutique.ma/webhooks/charipay"
      }
    },
    "metadata": { "cartId": "c_987", "source": "web" }
  }'

The 201 response contains, among other things, the sessionId and the checkoutUrl:

json
{
  "sessionId": "ps_5Kd0Rn",
  "checkoutUrl": "https://pay.chari.ma/checkout/ps_5Kd0Rn",
  "expiresAt": "2026-09-11T10:15:00Z"
}

Redirect the buyer to checkoutUrl, and that is all there is to the visible part. The session is single-use by default and expires 72 hours after creation if you don't supply an expiresAt. The accept and decline URLs bring the buyer back to your site; they are not authoritative — a buyer who closes the tab after paying will never see your success page, while the money has very much arrived. The webhook is what counts, and it is the next building block.

Two fields deserve particular attention. orderId is your business reference, displayed and returned, with no uniqueness constraint. externalId is different: unique per merchant and per environment, it makes the create replayable — we come back to it below. The URLs in config.urls are all optional: a missing value falls back to the one configured for your account, then to the platform default.

The signed webhook: verification in Node

A payment does not complete at the moment you make the call: the buyer goes through their bank, validates 3-D Secure, comes back — or doesn't. The result reaches you by webhook, and that notification is the only source of truth. Every delivery carries two headers: X-CHARI-SIGNATURE, a lowercase-hex HMAC-SHA256 computed over the string timestamp + "." + raw body, and X-CHARI-TIMESTAMP, the timestamp in milliseconds since the epoch. The API tolerates a gap of ±5 minutes between that timestamp and the time of receipt.

javascript
const crypto = require('crypto');

function verify(rawBody, signature, timestamp, secret) {
  // Fenêtre anti-rejeu de ±5 minutes — l'horodatage est en millisecondes.
  if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60 * 1000) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  // Une signature malformée doit répondre false — jamais jeter (500 → retentatives).
  if (!/^[0-9a-f]{64}$/i.test(signature)) return false;

  // Comparaison en temps constant — jamais `===`.
  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'));
}

And the route that uses it, with the body kept raw — this is the most common mistake: a framework that parses the JSON before verification, then a re-serialised object whose bytes are no longer the ones that were signed.

javascript
app.post('/webhooks/charipay', express.raw({ type: '*/*' }), async (req, res) => {
  const raw = req.body.toString('utf8');            // le corps brut, avant tout parsing
  const ok = verify(
    raw,
    req.get('X-CHARI-SIGNATURE'),
    req.get('X-CHARI-TIMESTAMP'),
    process.env.CHARIPAY_WEBHOOK_SECRET
  );
  if (!ok) return res.status(401).end();

  const eventId = req.get('Chari-Event-Id');        // la clé de déduplication
  if (await db.events.exists(eventId)) return res.status(200).end();

  await db.events.insert({ eventId, payload: JSON.parse(raw) });
  await queue.push('handle-charipay-event', eventId);
  return res.status(200).end();                     // accusé, le reste suit
});

Four rules make up the whole security of the verification: compute the HMAC over the raw bytes before any parsing; reject timestamps that are too far off; compare in constant time; deduplicate on Chari-Event-Id, not on Chari-Webhook-Id, which changes with every delivery attempt. The sandbox and production sign with separate secrets.

If your server does not answer 2xx, we retry: 1 min, 5 min, 30 min, 1 h, then every 6 h, up to 16 attempts over roughly 72 hours. If your server was down for maintenance longer than that, the portal's delivery log shows every delivery — number of attempts, your server's last response, the exact body of the event — and lets you replay it in one click. The events you will be waiting for most: payment.succeeded, payment.failed (if you asked for it when creating the session) and, for refunds, refund.succeeded. An article on this blog covers the five classic mistakes and the fifteen-line handler: Getting your webhook integration right.

Idempotency, in two layers

The network drops at the wrong moment, your HTTP client retries, and you don't know whether the first attempt went through. Two mechanisms complement each other so that this situation never costs a double charge:

  • `Idempotency-Key`, the header you send on your creates: replaying a call with the same value returns the first result instead of creating a duplicate. That is the protection against a timeout, a dropped connection, a message queue delivering twice.
  • `externalId`, the field in the body: unique per merchant and per environment, it turns the create into an operation you can rerun without keeping any intermediate state. Create a session twice with the same externalId and the API returns the existing session with a 200 instead of a 201 — the status code tells you exactly what happened.

The same externalId can exist once in the sandbox and once in production without conflict. Derive these keys from your order reference — never from a random draw, which would miss the whole point. The details and the right key patterns are in Never charge twice — idempotency explained.

Test and production: one base URL, two keys

There is only one base URL, https://api-psp.charipay.ma, in the sandbox as in production: the API key selects the environment. A chari_sk_test_… key targets the sandbox, a chari_sk_live_… key targets production. URLs, payloads and error codes are identical; on go-live day, you change the key, the webhook secret, and nothing else.

Until production is enabled on your account, a live key gets an explicit 403 with the code PRODUCTION_ACCESS_NOT_ENABLED. That is not an integration bug, it is an administrative step — the list of what has to be true before switching over is in Sandbox to production: the go-live list.

In the sandbox, a single test card is enough: 4918914107195005, with a future expiry date, 123 as the CVV and 555 as the 3-D Secure code. It triggers a real journey — checkout page, authentication, webhook — with no money moving.

Not every sale goes through a basket. A quote accepted on WhatsApp, a service invoiced remotely, a membership fee, a deposit: the payment link covers these cases without a line of code. From the portal — or through POST /v1/payment-links — you create a link for the amount you want, single-use or reusable, and you send it by e-mail, share it as a QR code or print it as a poster. The buyer pays on the same hosted page, by card or in cash at an agency, and the webhook is the same as for a session. Payment link or checkout: which to pick helps you choose between the two according to your volume.

Refunds, subscriptions, products

  • Refunding. POST /v1/refunds with the payment identifier — operationId or externalId — a reason, and a refundReference of your choosing; in full by default, or for a partial amount with refundAmount. The API answers 202 while the refund settles, then refund.succeeded arrives by webhook. Replaying the same refundReference returns the existing refund without debiting twice.
  • Subscriptions. Create a subscription with its billing schedule; a failed charge is retried at D+1, D+3 and D+7, and every step reaches you by webhook. In the sandbox, an endpoint forces the next billing date, which lets you run through a year of subscription in a minute and check your invoicing before a real customer has to live with it.
  • Products and orders. A simple catalogue, with orders attached to each product — useful when your sales come down to a few fixed items.

What collecting locally changes

A gateway built for Morocco does more than reproduce what exists elsewhere. Three points in particular change how you collect here: cash at agencies, money available instantly in a payment account in your name, and a sandbox you open on your own.

Cash at agencies

Some of your customers have no card, or don't want to use it online. ChariPay is the only payment gateway in Morocco that also collects cash at agencies: on the checkout page, the buyer chooses cash, receives a reference, and deposits the amount at an agency of the Chari network. You receive payment.succeeded when the deposit is confirmed — the same webhook, the same deduplication, the same reconciliation as for a card. The developer writes nothing extra; the accountant sees cards and cash on the same statement and in the same export.

For an online shop, that can replace cash on delivery and its trail of refused parcels and cash riding along with the courier: the order ships already paid. Taking cash without a till covers it end to end, from the payment link to the poster in the shop window.

Money available instantly, in a payment account with a RIB

Every successful payment — card or cash — is available instantly in your ChariPay payment account, an account held by Chari Money, with a RIB in your company's name that you download from the portal or through the API. It is not a balance awaiting settlement. From that account, you transfer to your Moroccan bank account — with a fee quote before confirmation, a one-time code if you have enabled it, and a 24-hour security delay for any new beneficiary —, you pay bills, you trigger telecom top-ups through the API.

The payout to your bank follows at your own pace: by transfer on demand, or automatically every night once a settlement account is configured. You pay a supplier in the morning with last night's takings. Set against what the section on workarounds describes — funds in a foreign currency, in a foreign entity, to be repatriated before they can be used in Morocco — this is the most concrete difference between collecting here and collecting elsewhere. The Wallet and payouts page details what you can do from the account.

The self-serve sandbox, free, with no time limit

You wait for no one to get started. Sign-up is online at portal-psp.charipay.ma/register; you activate the account by e-mail, choose a password, log in, select your company, request a confirmation token, create your test key yourself and make your first call — eight steps, which the Postman collection runs through in order, with no meeting and no e-mail to support. The sandbox is free and has no time limit: it remains your staging environment long after you go live.

There is also what you would expect from a serious gateway, and you will find it in the same place: PCI DSS Level 1 certification, data processed under Law 09-08, a portal with multiple users and roles — owner, manager, cashier, accountant — and CSV exports for reconciliation.

Frequently asked questions

Is Stripe available in Morocco?

No. The official list of supported countries, at stripe.com/fr/global, does not include Morocco. A business established in Morocco cannot open an account there to charge its customers.

When will Stripe be available in Morocco?

No timetable has been announced publicly. Any date you read elsewhere is a guess. The only reliable source remains the official list of supported countries; in the meantime, a project that needs to get paid today needs a solution that is available today, in dirhams, in an account in its name.

Is a company abroad a solution for Moroccan customers?

It is a solution for customers abroad. For Moroccan customers, it moves the problem instead of solving it: funds collected outside Morocco, tax obligations in two countries, Moroccan foreign-exchange and VAT questions left entirely open, the risk of the account being closed, Moroccan cards not always accepted. To be checked with your adviser before taking any step — and, in most cases, to be set aside in favour of collecting locally.

I run a subscription SaaS: how do I get paid in Morocco?

With ChariPay subscriptions: you create the subscription through the API with its billing schedule, the charges follow, a failure is retried at D+1, D+3 and D+7, and every event — successful payment, failure, cancellation — reaches you through a signed webhook. In the sandbox, an endpoint forces the next billing date so you can test your invoicing without waiting a month. The amount of each charge lands instantly in your payment account, in dirhams. The Subscriptions page describes the full cycle.

My code was written for a foreign gateway: what needs to change?

Less than you think, because the architecture is the same: a server-side create, a redirect to a hosted page, a signed webhook that is authoritative, an idempotency key on creates. What changes is the names: amount in MAD with no currency field, orderId, externalId, config.customer, config.urls; the X-CHARI-PAY-API-KEY authentication header; the X-CHARI-SIGNATURE and X-CHARI-TIMESTAMP signature headers with their timestamp + "." + raw body scheme; deduplication on Chari-Event-Id. Allow one working session for the payment session and the webhook, then test with the test card. The API documentation gives every endpoint with examples in four languages; a Postman collection, the OpenAPI contract and a pack for code assistants can be downloaded in the same place.

How much does it cost?

The sandbox is free. Going live costs an activation fee of MAD 6,000, VAT included, paid once. The commission per successful transaction — a percentage — and the security deposit are set after your application has been reviewed, according to your products, your payment methods and your volumes: the proposal arrives with figures before any commitment. Refunding a customer is free. The details are on the Pricing page.

How do I check that my integration works before going live?

In the sandbox, with the test card 4918914107195005 (CVV 123, 3-D Secure code 555): it runs the full journey — checkout page, authentication, webhook — with no money moving. Send a test event to your webhook endpoint — the API offers it on every declared endpoint — and read in the delivery log what your server answered. Also test a declined payment, a partial refund and a webhook your server answers with an error, to see the retries in action.

Not all my customers have a bank card: what do I do?

Nothing in particular on the code side: the checkout page offers cards and cash at an agency, the buyer chooses, and you receive the same webhook either way. For remote collection, a payment link sent by WhatsApp or e-mail does the same job, and the poster with a QR code does it in the shop.

Next step

Create your sandbox account at portal-psp.charipay.ma/register: online sign-up, activation by e-mail, a test key you create yourself, with no meeting and no commitment. Then open the API documentation — twelve modules, sixty-two endpoints, examples in curl, JavaScript, Python and PHP — and make your first POST /v1/payment-sessions with the test card. When payment.succeeded reaches your server with a valid signature, you have done the essential part: going live then comes down to changing a key.

Written by ChariPay team.

Read next

Guide3 min read

Getting paid on Shopify in Morocco

A Shopify store, Moroccan customers — how to accept cards and cash in dirhams, with the money available instantly in a payment account.

Guide3 min read

Getting paid on WooCommerce in Morocco

Your WordPress store deserves better than cash on delivery — cards and cash in dirhams, signed webhooks, money available instantly.

A question about your integration?

Our team supports merchants and developers alike, from the first test through to go-live.