Idempotency
Safely retrying a request that may have already succeeded.
Every transaction-creating request (MOMO_DEPOSIT, MOMO_WITHDRAWAL, CRYPTO_BUY, CRYPTO_SELL)
takes a partnerOrderId — a string you choose, unique per partner. This is your idempotency key.
Why this matters
A network timeout doesn't tell you whether your request reached the gateway before the connection
dropped. If you blindly retry, you risk creating a second real transaction for what the customer
experiences as one payment. Idempotency keys solve this: retry with the same partnerOrderId as
many times as you need, and the gateway guarantees it only ever creates the transaction once.
How it behaves
Submit the same partnerOrderId twice (whether that's a genuine retry or a bug on your end):
{
"transactionId": "0192b7a4-6f1e-7c3a-9d2b-4e5f6a7b8c9d",
"partnerOrderId": "deposit-2026-10-10-001",
"status": "GW-00-PENDING",
"duplicate": true
}duplicate: true tells you this is the original transaction's current state, not a new one.
Everything else in the response — transactionId, status — reflects the real, already-existing
transaction. You can safely treat a duplicate: true response exactly like a successful create:
it's giving you a status check for free.
How it's enforced
This isn't just an application-level check — it's backed by a real unique database constraint
on (PartnerId, PartnerOrderId). Two concurrent requests with the same key can both pass an
initial "does this exist yet?" check (a genuine race), but only one of them can win the insert; the
other's write fails on the constraint, and the gateway re-reads the winner's row and returns it with
duplicate: true instead of surfacing a database error. This is deliberately not a soft,
best-effort check — the correctness holds under real concurrent traffic, not just sequential
retries.
Choosing a partnerOrderId
- It only needs to be unique within your own partner account — other partners' values live in a completely separate namespace.
- Use something your own system already generates for the underlying payment/order, so a retry is naturally the same key without extra bookkeeping on your side.
- It's a string — UUIDs, your own internal order numbers, and composite keys (
"order-42-attempt-1") all work, as long as you don't reuse one for two genuinely different transactions.
