Server-to-Server Implementation Guide

This guide walks backend developers step by step with pseudocode through everything needed to integrate server-to-server endpoints.

1. Database Deposits Table

Create the deposits table:

CREATE TABLE deposits (
    deposit_id VARCHAR(36) PRIMARY KEY,
    user_id VARCHAR(36) NOT NULL,
    amount INTEGER NOT NULL,
    result VARCHAR(36)
);

2. /thndr/pay Endpoint

Payload:

{
  "userId": "<userId>",
  "depositId": "<depositId>",
  "amount": 1234
}

Execute following commands in one SQL transaction:

  1. Check if this deposit was already processed:

const deposits = sql("
    SELECT * FROM deposits
    WHERE deposit_id = body.depositId
"); 

if(deposits.length > 0) { 
    res.status(200).send('Already processed'); 
}
  1. Debit the user

const results = sql("
    UPDATE user_balances SET balance = balance - body.amount 
    WHERE user_id = body.userId 
    AND balance >= body.amount
");

if(results.length == 0) { 
  // Insufficient balance or user not found 
  res.status(400).send(
      { 
          { 
              errors: [ 
                  { 
                      code: 'INSUFFICIENT_BALANCE',
                      isClientSafe: true,
                  },
              ], 
          }
      }); 
  }

  1. Store deposit for idempotency and future result:

sql("
    INSERT INTO deposits(deposit_id, user_id, amount, result) 
    VALUES (body.depositId, body.userId, body.amount, NULL)
");

3. /thndr/results Endpoint

Payload:

{
  "userId": "<userId>",
  "depositId": "<depositId>",
  "result": "WIN" | "DRAW" | "LOSE" | "REFUND",
  "amount": 1234
}

Execute following commands in one SQL transaction:

  1. Check if deposit exists

const deposits = sql("
    SELECT * FROM deposits
    WHERE deposit_id = body.depositId
");

if(deposits.length == 0) { 
    // Unknown deposit, ignoring
    res.status(200).send('Deposit ignored');
}
  1. Check if result already processed

const deposit = deposits[0];

if (deposit.result !== null) {
    res.status(200).send('Result already processed');
}
  1. Process the result:

if (body.result === 'WIN' || 
    body.result === 'DRAW' ||
    body.result === 'REFUND') {
    // Credit the user 
    sql("
        UPDATE user_balances
        SET balance = balance + body.amount
        WHERE user_id = body.userId
    ");
} else if (body.result === 'LOSE') {
    // For result 'LOSE', no balance changes needed
}
  1. Mark deposit as processed

sql("
    UPDATE deposits 
    SET result = body.result 
    WHERE deposit_id = body.depositId
");

Last updated