#Refund Idempotency / Reconciliation Issue

1 messages · Page 1 of 1 (latest)

tacit geode
#

We run automated refunds with the following flow:

Cron picks a payment_id
Backend calls POST /refunds
Dodo processes the refund and returns a refund_id
We persist the refund_id in our database
Problem Scenario

If the server crashes, times out, or the DB write fails after step 3 but before step 4 completes, the refund is successfully created on Dodo’s side, but we permanently lose the refund_id.

When the cron retries:

Dodo correctly prevents duplicate refunds
API returns PAYMENT_ALREADY_REFUNDED
But the response does not include the original refund_id

As a result:

our DB remains inconsistent
we cannot reconcile the refund automatically
manual investigation becomes necessary
Expected Behavior

For idempotent refund requests, it would help if the API returned either:

the original refund_id, OR
a way to fetch the existing refund by idempotency key/payment reference

This would allow safe retry + reconciliation after partial failures.

Any Workaround for this?

wintry juncoBOT
# tacit geode We run automated refunds with the following flow: Cron picks a payment_id Backe...

Short answer: there’s no built-in way today to recover the original refund_id from a failed retry or idempotency replay.

Workarounds:

Use idempotency keys + persist before calling

  • Generate an idempotency key per refund attempt
  • Store it in DB before calling POST /refunds
  • On retry, reuse the same key → prevents duplicates and lets you track the attempt internally

Fetch refunds by payment_id

  • Use the “list refunds” or “get payment details” endpoint (if available in your setup)
  • Filter refunds for that payment_id
  • Reconcile by matching amount + timestamp

Store a “refund_pending” state

  • Mark payment as refund_initiated before API call
  • On recovery job, query Dodo for refunds on that payment and backfill refund_id

Webhook-based recovery (recommended)

  • Listen to refund webhook events
  • Persist refund_id from webhook instead of relying solely on sync response
  • This covers crashes/timeouts cleanly

If you want true idempotent replay returning the same refund_id, that’s currently not supported—worth raising with the Dodo team as a feature request.

#

Hey @tacit geode, We have limited availability over weekends, but rest assured we'll get back to you as soon as possible!