Skip to main content
All articles
Compliance12 min read

Online payment fraud against merchants in Morocco: fake screenshots, false receipts, stolen access

Online payment fraud aimed at merchants in Morocco: fake payment screenshots, spoofed redirects, refund scams and stolen access. Scenarios and a checklist.

Online payment fraud aimed at merchants can do without any stolen card: fake proof of payment or stolen access is enough. A "payment sent" screenshot, an edited deposit receipt, a redirect to your success page with no payment behind it, a refund request to another account, stolen access to your portal. One rule defuses almost all of them: only the status read in the portal or through the API, or a signed webhook, proves that a payment happened. This guide goes through each scenario, what counts as proof, and the checklist to run before you ship an order.

Online payment fraud against merchants: what 3-D Secure leaves to you

ChariPay is a Moroccan payment platform, a PSP operated by Chari Money, a payment institution licensed by Bank Al-Maghrib. Card payments go through 3-D Secure authentication: the cardholder confirms with their bank, which protects the merchant against most unpaid charges and disputes. It is real protection, not an absolute guarantee, and it covers one thing only: the card payment itself. To understand what makes an authentication fail, read 3-D Secure and success rates.

The frauds described here go around the card. They do not try to pay with someone else's card; they try to make you believe a payment happened, to get you to send money back, or to take over your account. 3-D Secure cannot help there, because no card payment is involved. Here are the six scenarios in this guide, with what counts as proof or protects you in each:

ScenarioThe misleading signalWhat counts as proof or protects you
Screenshot or fake receiptA "payment sent" image received by messageThe status read in the portal or through the API, or the signed webhook
Spoofed redirectThe buyer lands on your success pageThe signed webhook, never the redirect
Refund to another account"I overpaid, send the difference to this RIB"The original payment, refunded from the portal or through the API
Portal takeoverA beneficiary or an operation you do not recognizeTwo-factor authentication, roles, transfer validation codes
Exposed API keyA key visible in a code repository or a screenshotImmediate key rotation
Insider fraudAn outgoing operation nobody approvedDual approval, chained audit log

The fake payment screenshot scam and the false receipt

This scenario is simple, and it works because it plays on speed. A customer writes that they have paid and attaches a screenshot: a confirmation screen, a bank message, a PDF receipt. The image can be edited, come from another payment, or show a payment that later failed. A screenshot is never proof of payment, however convincing it looks.

What counts is the payment's state at ChariPay, which you read yourself. A payment link has a clear status (active, paid, canceled, expired), and you are notified in the portal when the payment comes in. If you are integrated through the API, confirm the order when the signed webhook arrives. For a cash deposit at an agency, the rule is the same: as soon as the network confirms the deposit, the link turns "Paid" and you are notified in the portal; on the server side, read the link's state with GET /v1/payment-links/{reference}. A photo of a deposit slip does not replace that status.

Before you ship an order, follow these steps:

  1. 1Ignore the image and open the portal: find the order's link or transaction from your own session.
  2. 2Read the status: "paid" for a single-use link, or the matching transaction in the list for a reusable link.
  3. 3Compare the amount collected with the order amount, and the reference with the customer.
  4. 4On the server side, if you are integrated, wait for the signed webhook, or read the state through the API.
  5. 5Ship only then. If the status is not there, reply politely that the order will go out as soon as the payment shows on your side.

In chat-based selling, the sale is closed in a conversation and the "proof" arrives in the same conversation: the rule does not change. The full flow, from the message to the payment link, is in selling on Instagram and WhatsApp.

Watch a real status change: Start in test mode. Three fields (name, work email, company), an activation link received by email to choose your password, then the portal, where you create your test key after re-authenticating; pay a link with the test card and watch it turn "paid," with no real money involved. It is free, with no time limit and no approval to wait for: your business verification (KYB) only gates the move to production.

The spoofed redirect: the webhook is the proof

On an ecommerce site, the same trick takes a technical form. After payment, the hosted checkout sends the buyer back to your success URL. Nothing stops someone from opening that URL directly, or tampering with it, without paying. If your site validates the order because the buyer "came back to the success page," it ships without payment.

The rule is simple: confirm the order when the webhook arrives, never on the redirect alone: the redirect says the buyer came back, not that the payment is secured. And a webhook is only worth something once it is verified. Three checks turn it into proof:

  • The signature: X-CHARI-SIGNATURE is an HMAC-SHA256 computed over the timestamp and the raw request body with your endpoint's secret; recompute it over the raw bytes, before any parsing, and compare in constant time.
  • The timestamp: reject any request whose X-CHARI-TIMESTAMP is off by more than ±5 minutes, so that an old intercepted message cannot be replayed.
  • Deduplication: deduplicate on Chari-Event-Id, which stays the same for an event across all attempts, so that a repeated delivery does not validate the same order twice.

The implementation, with a complete receiver, is covered in getting your webhook integration right.

The refund request to another payment method

The "overpayment" scenario is subtler. A customer claims they paid twice, or too much, and asks you to send the difference back by bank transfer, to a RIB they send you by message. Sometimes the original payment does not even exist: only a fake proof comes with it. Sometimes it exists, but the RIB belongs to a third party.

Two rules defeat it. First, check in the portal what was actually collected, just as you would for a screenshot. Then, if a refund is owed, refund the original operation and nothing else, in full or partially, from the portal or through the API. The refund is tied to the payment you are refunding; it does not go to an account someone points you to. A refund is not instantaneous on the bank side: a customer in a hurry who demands it "right now, to another account" should put you on alert.

Never refund by bank transfer to a RIB received by message. From the portal, an executed transfer cannot be canceled: if the RIB belonged to a fraudster, the money is gone.

Portal takeover

The portal is where money leaves: transfers, bill payments. Stolen access (a reused password, a shared computer, a session left open) can therefore be costly. The first protection, two-factor authentication (TOTP), is turned on from your account settings. Once enabled, it is requested at sign-in; the portal can also require it from users authorized for sensitive operations — refunds, transfers, beneficiary management. Re-authentication before every sensitive action and secured sessions come on top: with two-factor authentication enabled, a stolen password is not enough on its own.

Roles limit what a compromised account can do. Portal roles separate viewing from acting: the accountant reads everything without triggering anything, the cashier collects without seeing the balance or transfers, the owner or an authorized manager triggers outgoing operations. A compromised cashier account therefore cannot transfer your balance.

For transfers, two more protections apply. Every transfer sent from the portal is validated by a six-digit one-time code, emailed to each validation address designated in Settings → Security; you set the number of approvers required. And a security delay applies after adding a new beneficiary: an intruder cannot add an unknown beneficiary and transfer your balance to it right away. Each layer is detailed in securing portal access.

The exposed API key

An API key gives a program access to your account. A key is a secret: never in the browser, a mobile app, a git repository or a screenshot. Leaks have ordinary causes: a key pasted into a web page's code, a repository made public, a screenshot shared with a contractor.

If a key may have been seen, replace it without waiting: two keys can coexist during a rotation, without downtime, which lets you switch over before retiring the old one. The step-by-step response is in protecting your API keys.

Insider fraud

Nobody likes to consider it, but an internal control rule also protects your team: when a single person can move money out, that person is the first one suspected at the slightest discrepancy. Separating duties avoids that suspicion.

ChariPay applies it at several levels. Sensitive actions (payouts, adjustments, account freezes) require a distinct requester and approver. For transfers, the validation code goes to each validation address you have designated, and you set the number of approvers required. Roles separate viewing from acting: the cashier collects without seeing the balance or transfers. Finally, the audit log is cryptographically chained: history cannot be rewritten.

In practice: give the preparation and the approval of transfers to two different people, and give each person the narrowest role that does the job.

The merchant's anti-fraud checklist

  1. 1Never ship on a screenshot or a receipt photo: read the status in the portal.
  2. 2Confirm online orders on the signed webhook, never on the redirect.
  3. 3Verify every webhook: signature over the raw body, timestamp within ±5 minutes, deduplication on Chari-Event-Id.
  4. 4Refund only the original operation, from the portal or through the API, never by transfer to a RIB received by message.
  5. 5Never share access: each user has their own portal sign-in and turns on two-factor authentication (TOTP) from their account settings.
  6. 6Assign roles: cashiers to collect, an accountant to read, a small number of authorized people for outgoing operations.
  7. 7Designate your transfer validation addresses in Settings → Security and set the number of approvers required.
  8. 8Treat every new beneficiary as a signal: an addition you did not make should trigger an immediate check.
  9. 9Keep your API keys server-side and rotate them at the slightest doubt.
  10. 10Review access every time a team member leaves.

What the platform does on its side (double-entry accounting reconciled daily, continuous monitoring, sensitive data masked in logs) is on our security and compliance page.

Frequently asked questions

How can I tell whether a payment screenshot is genuine?

You cannot tell from the image, and you do not need to. Open the portal from your own session and read the status of the link or transaction: "paid," with the right amount. If you are integrated through the API, wait for the signed webhook. Until that status shows on your side, the order is not paid, whatever screenshot you received.

A customer sends me a photo of their cash deposit receipt: is that enough?

No. For a cash payment on a payment link, as soon as the network confirms the deposit, the link turns "Paid" and you are notified in the portal; on the server side, read the link's state with GET /v1/payment-links/{reference}. For an order from your hosted storefront, read its status in the portal. Wait for that status before delivering: a photo of a receipt does not replace it.

Is the confirmation page enough to prove a customer paid?

No. Confirm the order when the webhook arrives, never on the redirect alone: the redirect says the buyer came back, not that the payment is secured. Anyone can open your success URL without paying. The webhook, on the other hand, is signed with your endpoint's secret and timestamped: verify the signature and reject any drift beyond ±5 minutes.

Does 3-D Secure protect merchants against all fraud?

No. 3-D Secure protects the merchant against most unpaid charges and disputes on card payments, because the cardholder confirms with their bank. It can do nothing against frauds where no card payment takes place: a fake screenshot, a false receipt, a spoofed redirect, a refund request to another account, stolen access to the portal.

A customer asks to be refunded to a different account: what should I do?

Decline politely. First check in the portal what was actually collected. If a refund is owed, refund the original operation, in full or partially, from the portal or through the API: it is tied to the original payment. Never transfer money to a RIB received by message: an executed transfer cannot be canceled.

How do I stop a team member from sending a transfer on their own?

Combine two settings. Roles first: the cashier collects without seeing the balance or transfers, and the accountant reads everything without triggering anything. Code validation next: every transfer sent from the portal requires a one-time code emailed to the validation addresses you designate, and you set the number of approvers required. Finally, the audit log is cryptographically chained: history cannot be rewritten.

What should I check before shipping an order paid online?

Three things, read on your side and not in a customer's message: the payment status (in the portal, through the API or in the signed webhook), the amount collected compared with the order amount, and the reference that ties the payment to the right customer. For a cash deposit, wait until the link turns "Paid."

Next step

Run the checklist in realistic conditions, with no real money: Start in test mode, then pay a link with the test card, watch the status change, verify a signed webhook and refund the operation. Once those habits are in place, tighten access with securing portal access.

Written by ChariPay team.

Read next

Compliance5 min read

Protecting your API keys

Protecting your payment API keys: five simple rules, planned rotation, and what to do, in order, when a key has already leaked into a repository.

Ready to try it yourself?

Create your account and take test-mode payments today: it is free, with no approval to wait for. A question? Our team supports merchants and developers alike.

  • Free, no commitment
  • Test mode from sign-up
  • No approval to wait for