Idempotency and Retries

"Idempotent" means that sending the same request twice has the same effect as sending it once. PayMe has no idempotency key, so you must make retries safe yourself.

What happens when a request runs twice

Request Second call while the first is still running Second call after the first finished
generate-sale A second, separate sale A second, separate sale — transaction_id is not checked for uniqueness
generate-sale with buyer_key / token (creates and charges) A second charge is possible A second charge is possible
pay-sale Rejected: HTTP 429, code 998 "Duplicated action detected, please retry later" If the first succeeded: rejected with HTTP 500, code 305, current status in status_additional_info. If the first failed: accepted as a new charge attempt.
refund-sale — partial Rejected: HTTP 429, code 998 Runs again — a second refund, as long as refundable balance remains
refund-sale — full Rejected: HTTP 429, code 998 Rejected with code 305
capture-sale Rejected: HTTP 429, code 998 An authorization can be captured only once.

Notes:

Before retrying, check the sale

If a request timed out and you do not know whether it ran, look the sale up instead of resending it.

POST /get-sales accepts these filters:

By default get-sales leaves out unpaid sales (status initial). To find an unpaid sale, pass sale_status including initial.

get-sales works with seller_payme_id alone; partners can also use payme_client_key.

⚠ TO CONFIRM: Reading sale_status from get-sales is most likely the right way to tell whether a timed-out operation ran — capture → completed (not authorized/failed), full refund → refunded, void → a voided status. Please confirm this is the recommended check, and the exact status strings for partial refunds and voids (the docs show partial-refund; PayMe's API code suggests partially-refunded). Note that sale_status alone cannot show whether a retried partial refund already ran: compare sale_refund_buffer (the amount still refundable, returned by refund-sale and sent in each refund callback — get-sales does not return it) or count the refund callbacks, each of which has its own payme_transaction_id. [Q25]

Retrying safely

  1. Retry only on network errors, timeouts, or HTTP 429 code 998. Never retry a response with status_code 1 that names a problem with your input.
  2. On 998, wait (honour the Retry-After header), then check the sale before retrying.
  3. Before retrying anything that moves money (pay-sale, refund-sale, capture-sale, generate-sale with buyer_key), check the sale's current state as above.
  4. Store PayMe's payme_sale_id as soon as generate-sale returns, so every later step refers to one sale.
  5. Treat callbacks as confirmation, and process each one once — see Callbacks.

⚠ TO CONFIRM: What client-side timeout should integrators use for Payments API calls (typical and maximum response time)? [Q29]

PayMe's own retries

Rate limits

⚠ TO CONFIRM: Is 100 requests per 10 seconds per seller also the default in production? [Q28]