Node.js Python Go Java PHP C# Ruby
const http = require ('http' );
const { verify, envelope, OK } = require ('./merchant-sign' );
http.createServer ((req, res) => {
const chunks = [];
req.on ('data' , (c) => chunks.push (c));
req.on ('end' , () => {
const raw = Buffer.concat (chunks); // verify the raw bytes BEFORE JSON.parse
res.setHeader ('Content-Type' , 'application/json' );
if (req.method !== 'POST' || req.url !== '/wallet/credit' ) { res.statusCode = 404 ; return res.end (); }
const check = verify (SECRET_KEY, MERCHANT_CODE, req.headers, req.method, req.url, raw);
if (check !== OK) return res.end (envelope (check));
const input = JSON.parse (raw);
// In ONE database transaction, unique on (merchantCode, txnId):
// txnId seen before -> answer exactly the stored result
// no successful debit in this round -> ROUND_NOT_FOUND; round closed -> ROUND_CLOSED
// add amount ("0.00" is a valid losing settlement), close the round if roundClosed
// never refuse because the player is disabled
res.end (envelope ('OK' , 'OK' , {"merchantTxnId" :"MT-88413" ,"balance" :"1260.50" })); // business results are always HTTP 200
});
}).listen (8080 );from flask import Flask, request, Response
from merchant_sign import verify, envelope, OK
app = Flask (__name__)
@app.post ("/wallet/credit" )
def credit ():
raw = request.get_data () # verify the raw bytes BEFORE parsing JSON
check = verify (SECRET_KEY, MERCHANT_CODE, request.headers, "POST" , request.path, raw)
if check != OK:
return Response (envelope (check), mimetype="application/json" )
data = request.get_json ()
# In ONE database transaction, unique on (merchantCode, txnId):
# txnId seen before -> answer exactly the stored result
# no successful debit in this round -> ROUND_NOT_FOUND; round closed -> ROUND_CLOSED
# add amount ("0.00" is a valid losing settlement), close the round if roundClosed
# never refuse because the player is disabled
return Response (envelope ("OK" , "OK" , {"merchantTxnId" :"MT-88413" ,"balance" :"1260.50" }), mimetype="application/json" )http.HandleFunc ("POST /wallet/credit" , func (w http.ResponseWriter, r *http.Request) {
raw, _ := io.ReadAll (http.MaxBytesReader (w, r.Body, 1 <<20 )) // verify the raw bytes BEFORE decoding
w.Header ().Set ("Content-Type" , "application/json" )
if check := merchantsign.Verify (secretKey, merchantCode, r.Header, r.Method, r.URL.EscapedPath (), raw, time.Now ()); check != merchantsign.OK {
w.Write (merchantsign.Envelope (check, "" , nil ))
return
}
var in map [string ]any
_ = json.Unmarshal (raw, &in )
// In ONE database transaction, unique on (merchantCode, txnId):
// txnId seen before -> answer exactly the stored result
// no successful debit in this round -> ROUND_NOT_FOUND; round closed -> ROUND_CLOSED
// add amount ("0.00" is a valid losing settlement), close the round if roundClosed
// never refuse because the player is disabled
w.Write (merchantsign.Envelope ("OK" , "OK" , json.RawMessage (`{"merchantTxnId":"MT-88413","balance":"1260.50"}` ))) // always HTTP 200
})@RestController
class WalletController {
@PostMapping (value = "/wallet/credit" , produces = "application/json" )
String credit (@RequestBody byte [] raw, @RequestHeader Map<String, String> headers) throws IOException {
String check = MerchantSign.verify (SECRET_KEY, MERCHANT_CODE, headers, "POST" , "/wallet/credit" , raw, System.currentTimeMillis ());
if (!check.equals (MerchantSign.OK)) {
return "{\"code\":\"" + check + "\",\"message\":\"" + check + "\"}" ;
}
JsonNode in = new ObjectMapper ().readTree (raw); // parse only after the signature checks out
// In ONE database transaction, unique on (merchantCode, txnId):
// txnId seen before -> answer exactly the stored result
// no successful debit in this round -> ROUND_NOT_FOUND; round closed -> ROUND_CLOSED
// add amount ("0.00" is a valid losing settlement), close the round if roundClosed
// never refuse because the player is disabled
return "{\"code\":\"OK\",\"message\":\"OK\",\"data\":" + "{\"merchantTxnId\":\"MT-88413\",\"balance\":\"1260.50\"}" + "}" ;
}
}<?php
require 'MerchantSign.php' ;
// /wallet/credit
header ('Content-Type: application/json' );
$raw = file_get_contents ('php://input' ); // verify the raw bytes BEFORE json_decode
$check = MerchantSign::verify (SECRET_KEY, MERCHANT_CODE, getallheaders (), $_SERVER['REQUEST_METHOD' ],
parse_url ($_SERVER['REQUEST_URI' ], PHP_URL_PATH), $raw);
if ($check !== MerchantSign::OK) {
echo MerchantSign::envelope ($check);
exit;
}
$in = json_decode ($raw, true );
// In ONE database transaction, unique on (merchantCode, txnId):
// txnId seen before -> answer exactly the stored result
// no successful debit in this round -> ROUND_NOT_FOUND; round closed -> ROUND_CLOSED
// add amount ("0.00" is a valid losing settlement), close the round if roundClosed
// never refuse because the player is disabled
echo MerchantSign::envelope ('OK' , 'OK' , json_decode ('{"merchantTxnId":"MT-88413","balance":"1260.50"}' , true ));app.MapPost ("/wallet/credit" , async (HttpRequest req) =>
{
using var ms = new MemoryStream ();
await req.Body.CopyToAsync (ms);
var raw = ms.ToArray (); // verify the raw bytes BEFORE deserializing
var headers = req.Headers.ToDictionary (h => h.Key, h => h.Value.ToString ());
var check = MerchantSign.Verify (SecretKey, MerchantCode, headers, req.Method, req.Path, raw);
if (check != MerchantSign.Ok)
return Results.Json (new { code = check, message = check });
var input = JsonDocument.Parse (raw).RootElement;
// In ONE database transaction, unique on (merchantCode, txnId):
// txnId seen before -> answer exactly the stored result
// no successful debit in this round -> ROUND_NOT_FOUND; round closed -> ROUND_CLOSED
// add amount ("0.00" is a valid losing settlement), close the round if roundClosed
// never refuse because the player is disabled
return Results.Text ("{\"code\":\"OK\",\"message\":\"OK\",\"data\":" + "{\"merchantTxnId\":\"MT-88413\",\"balance\":\"1260.50\"}" + "}" , "application/json" );
});require 'sinatra'
require 'json'
require_relative 'merchant_sign'
post '/wallet/credit' do
content_type :json
raw = request.body.read # verify the raw bytes BEFORE JSON.parse
check = MerchantSign.verify (SECRET_KEY, MERCHANT_CODE, request.env, 'POST' , request.path_info, raw)
return MerchantSign.envelope (check) unless check == MerchantSign::OK
input = JSON.parse (raw)
# In ONE database transaction, unique on (merchantCode, txnId):
# txnId seen before -> answer exactly the stored result
# no successful debit in this round -> ROUND_NOT_FOUND; round closed -> ROUND_CLOSED
# add amount ("0.00" is a valid losing settlement), close the round if roundClosed
# never refuse because the player is disabled
MerchantSign.envelope ('OK' , 'OK' , JSON.parse ('{"merchantTxnId":"MT-88413","balance":"1260.50"}' ))
end