Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developers.ligdicash.com/llms.txt

Use this file to discover all available pages before exploring further.

For every transaction event, LigdiCash sends two POST requests to your callback_url:
  • One in application/x-www-form-urlencoded
  • One in application/json
Both contain exactly the same data. Your server will therefore receive two calls for a single event. Without deduplication, you risk processing the same transaction twice — fulfilling an order twice, triggering two refunds, crediting an account twice.

Deduplication strategy

The simplest method is to mark each processed transaction in your database and ignore subsequent calls for the same transaction.
1

Extract your transaction_id

Parse custom_data to extract your transaction_id via keyof_customdata. See Parse custom_data.
2

Check whether it has already been processed

Before any processing, check whether this identifier already exists in your processed transactions table.
3

Process and mark atomically

If not yet processed, run your processing and mark the transaction as processed in the same database transaction. Atomicity avoids race conditions if the two requests arrive simultaneously.

Implementation

app.post('/callback', async (req, res) => {
  const entry = req.body.custom_data?.find(
    (item) => item.keyof_customdata === 'transaction_id'
  );
  const transactionId = entry?.valueof_customdata;
  if (!transactionId) return res.sendStatus(400);

  // Insert with unique constraint — fails if already processed
  const inserted = await db.query(
    'INSERT INTO processed_callbacks (transaction_id) VALUES (?) ON CONFLICT DO NOTHING',
    [transactionId]
  );

  if (inserted.rowCount === 0) {
    // Already processed — second request of the LigdiCash duplicate
    return res.sendStatus(200);
  }

  // First reception — business processing
  await processPayment(transactionId);

  res.sendStatus(200);
});
// With a UNIQUE constraint on transaction_id in the database
try {
    $db->insert('processed_callbacks', ['transaction_id' => $transactionId]);
} catch (UniqueConstraintException $e) {
    // Already processed
    http_response_code(200);
    exit;
}

// First reception — business processing
processPayment($transactionId);
A database unique constraint is more reliable than a simple SELECT followed by an INSERT — two simultaneous requests can both pass the SELECT before either has performed its INSERT.