Yeebet TestKit — Seamless Test Cases

The Seamless (single wallet) test suite contains 12 test cases, executed by the TestKit in the selected order.

Merchant callbacks support two request formats. Use the switch above to view the request examples for your integration — JSON is shown by default. Responses and assertions are identical in both formats.

General Conventions

Endpoints

Step typeDirectionEndpoint
loginTestKit → Yeebet platform (STG){SGO_API_URL}/api/login (POST, application/json body)
balance / deduct / deposit / rollbackTestKit (acting as the platform) → merchant callbacksThe Balance / Deduct / Deposit / Rollback URLs configured on the page

Request format (JSON mode). Every callback is a POST with Content-Type: application/json and a JSON body. The bets field is a bare JSON object when the step carries a single bet ("bets": {...}) and a JSON array when a notification carries multiple bets. Optionally, the page toggle “Send bets as JSON string” (off by default) transmits the same shape as a JSON string (e.g. "{\"gameid\":...}") so the merchant must JSON-parse it again. The signature is unaffected either way (bets is excluded from signing). When a token is configured, every callback request also carries the token field (which is included in the signature); the examples below use token=mock_123. The login request carries the same configured token as its optional token parameter (Company Session Token).

Request format (Form mode). Callbacks are POST with Content-Type: application/x-www-form-urlencoded, except balance, which becomes a GET with the parameters in the query string. Form values are strings, so bets is serialized to a JSON string with the same shape as in JSON mode — an object string (bets={...}) for a single bet, an array string (bets=[{...},...]) when a notification carries multiple bets — and the merchant must JSON-parse it again. Form mode never sends a token field. The form examples below show the decoded field values; on the wire they are URL-encoded.

Signature (identical in both formats). Every request carries a sign parameter (MD5): lowercase the key names, drop empty values plus the sign and bets fields, sort keys in ascending ASCII order, join as k=v&...&key={secretKey} (values are not URL-encoded), then take the MD5 hex digest. The login request is signed with the Login Secret Key configured in the Login section; wallet callbacks are signed with the Callback Secret Key.

Assertions

• Each step asserts Number(response.result) === expected_result_code (every step in this suite expects 0), except the duplicate / unknown notification steps (003 step 2, 005 step 3, 010 step 3, 011 step 1): there the merchant may answer 0 or reject the resend with a negative code (e.g. -1000 SameIdAlreadyProcessed) — either passes as long as the balance check below shows no movement.
• For steps with an expected balance change, the executor calls the merchant Balance endpoint before and after the step and asserts the delta (tolerance 0.005).
login additionally requires a non-empty openurl; balance additionally requires a numeric balance field.

Identifiers

• All steps within one test case share the same serialnumber (they refer to the same bet). For a single bet, bets.id and bets.serialnumber equal the top-level serialnumber (numeric); multi-bets each carry their own. gameroundno/gameno share one per-case number and gameroundid is an independent number. createtime/settletime are Unix seconds, frozen per case (duplicate resends are byte-identical). bets.currency is always present and equals the page currency; bets.gameid is the gameid configured on the page (the same value sent as gid at login). Field types inside bets: id, serialnumber, gameroundno, gameno, gameroundid, createtime, settletime are integers; cid is a string; bets.username is the page username.
Top-level type and amount are strings in both modes ("type": "1", "amount": "-1.00"); the signature is computed over the same text (amount=-1.00&...&type=1).
• Every notification carries a fresh notifyid. Duplicate-notification cases (the second send in 004, 006, and 011) resend the same notifyid.
• A rollback notification re-sends the notifyid of the request it rolls back, together with the same type and serialnumber (doc 2.6.5 / 2.7.4 note II: the merchant verifies notifyid + type + serialnumber). Only 011 (unknown serialnumber, no original request) carries a fresh notifyid.

All examples below use appid=your_appid, username=player01, currency=THB; sign and the id fields are placeholders. A full request body is shown only the first time it appears; later cases describe the differences.

001 Login

Verify that /api/login returns the game lobby link. If login fails, subsequent tests may also fail. The login request is the same in both formats. If a Token is configured, it is sent as the optional token parameter (Company Session Token) and echoed back in JSON-format wallet callbacks. The gid parameter carries the gameid configured on the page (required there) so the launch enters that specific game; the same gameid is used as bets[].gameid in Deduct/Deposit callbacks.

Request (POST application/json)
{
  "appid": "your_appid",
  "username": "player01",
  "iscreate": "1",
  "clienttype": "1",
  "language": "2",
  "currency": "THB",
  "gid": "101",
  "token": "mock_123",
  "sign": "0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b"
}
Expected response (result = 0 and a non-empty openurl)
{
  "result": 0,
  "openurl": "https://stg-lobby.example.com/?token=xxxx"
}

002 Get Player Balance

The platform calls the merchant Balance endpoint; expect result = 0 and a numeric balance.

Request (POST application/json)
{
  "appid": "your_appid",
  "username": "player01",
  "token": "mock_123",
  "notifyid": "25081111203512345",
  "sign": "0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b"
}
Request (GET query string)
GET {Balance_url}?appid=your_appid&username=player01&notifyid=25081111203512345&sign=0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b
Expected response
{
  "result": 0,
  "balance": 100.00
}

003 Duplicate serialnumber Deduct

The same deduct notification is sent twice (same serialnumber and type). The merchant must not deduct twice (idempotency). The second response may be result = 0 or a negative rejection code; the pass criterion is that the balance does not move again.

StepEndpointtypeamountExpected resultBalance change
1Deduct1-1.000-1.00
2Deduct1-1.000 or negative (rejection)0 (no second deduction)
Request (POST application/json)
{
  "appid": "your_appid",
  "username": "player01",
  "token": "mock_123",
  "notifyid": "25081111203512345",
  "type": "1",
  "amount": "-1.00",
  "currency": "THB",
  "serialnumber": "1754882501123456",
  "bets": {
    "gameid": 101,
    "createtime": 1756090000,
    "serialnumber": 1754882501123456,
    "userstatus": 0,
    "betpoint": "PLAYER",
    "betodds": 2.0,
    "userid": 100001,
    "username": "player01",
    "commamount": 0.0,
    "gameroundid": 1754882501123900,
    "settletime": 1756090000,
    "gameresult": "",
    "closeRound": false,
    "winlost": 0.0,
    "gametype": 4,
    "gamestyle": 1,
    "currency": "THB",
    "id": 1754882501123456,
    "gameroundno": 1754882501123800,
    "state": 0,
    "gameno": 1754882501123800,
    "bettype": 0,
    "cid": "338891",
    "betamount": 1.0,
    "foregift": 0.0
  },
  "sign": "0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b"
}
Request body (POST application/x-www-form-urlencoded, decoded)
appid=your_appid
username=player01
notifyid=25081111203512345
type=1
amount=-1.00
currency=THB
serialnumber=1754882501123456
bets={"gameid":101,"createtime":1756090000,"serialnumber":1754882501123456,"userstatus":0,"betpoint":"PLAYER","betodds":2.0,"userid":100001,"username":"player01","commamount":0.0,"gameroundid":1754882501123900,"settletime":1756090000,"gameresult":"","closeRound":false,"winlost":0.0,"gametype":4,"gamestyle":1,"currency":"THB","id":1754882501123456,"gameroundno":1754882501123800,"state":0,"gameno":1754882501123800,"bettype":0,"cid":"338891","betamount":1.0,"foregift":0.0}
sign=0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b
Expected response for step 1 (balance after the deduction, plus the merchant order number)
{
  "result": 0,
  "balance": 99.00,
  "orderno": "M20260811001"
}

Step 2 is an exact resend of step 1 (same notifyid, serialnumber, and payload); expected response is the same with result = 0 and no second deduction.

004 Settle Deposit

A bet deduct (type=1) followed by a settle deposit (type=9) for the same serialnumber.

StepEndpointtypeamountExpected resultBalance change
1Deduct1-1.000-1.00
2Deposit9+2.000+2.00
Step 2 request (POST application/json; same serialnumber as step 1, new notifyid; the bet now carries the win/loss result)
{
  "appid": "your_appid",
  "username": "player01",
  "token": "mock_123",
  "notifyid": "25081111203698765",
  "type": "9",
  "amount": "2.00",
  "currency": "THB",
  "serialnumber": "1754882501123456",
  "bets": {
    "gameid": 101,
    "createtime": 1756090000,
    "serialnumber": 1754882501123456,
    "userstatus": 0,
    "betpoint": "PLAYER",
    "betodds": 2.0,
    "userid": 100001,
    "username": "player01",
    "commamount": 1.0,
    "gameroundid": 1754882501123900,
    "settletime": 1756090000,
    "gameresult": "",
    "closeRound": true,
    "winlost": 1.0,
    "gametype": 4,
    "gamestyle": 1,
    "currency": "THB",
    "id": 1754882501123456,
    "gameroundno": 1754882501123800,
    "state": 1,
    "gameno": 1754882501123800,
    "bettype": 0,
    "cid": "338891",
    "betamount": 1.0,
    "foregift": 0.0
  },
  "sign": "0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b"
}
Step 2 request body (POST application/x-www-form-urlencoded, decoded; same serialnumber as step 1, new notifyid)
appid=your_appid
username=player01
notifyid=25081111203698765
type=9
amount=2.00
currency=THB
serialnumber=1754882501123456
bets={"gameid":101,"createtime":1756090000,"serialnumber":1754882501123456,"userstatus":0,"betpoint":"PLAYER","betodds":2.0,"userid":100001,"username":"player01","commamount":1.0,"gameroundid":1754882501123900,"settletime":1756090000,"gameresult":"","closeRound":true,"winlost":1.0,"gametype":4,"gamestyle":1,"currency":"THB","id":1754882501123456,"gameroundno":1754882501123800,"state":1,"gameno":1754882501123800,"bettype":0,"cid":"338891","betamount":1.0,"foregift":0.0}
sign=0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b
Expected response
{
  "result": 0,
  "balance": 101.00,
  "orderno": "M20260811002"
}

005 Duplicate serialnumber Deposit

The same settle deposit notification is sent twice (same serialnumber and type). The merchant must not credit the winnings twice (idempotency). The second settle response may be result = 0 or a negative rejection code; the pass criterion is that the balance does not move again.

StepEndpointtypeamountExpected resultBalance change
1Deduct1-1.000-1.00
2Deposit9+2.000+2.00
3Deposit9+2.000 or negative (rejection)0 (no second credit)

Request/response are identical to 004; step 3 is an exact resend of step 2.

006 Void Bet Deposit

A bet deduct followed by a void-bet deposit (type=7) returning the stake.

StepEndpointtypeamountExpected resultBalance change
1Deduct1-1.000-1.00
2Deposit7+1.000+1.00

Step 2 has the same structure as the Deposit in 004, with these differences: type="7", amount="1.00", and the bet carries winlost="1.00", commamount="1.00", state=1. The response format is the same as Deposit.

007 ReSettle Deposit

Bet deduct, settle deposit, then a re-settle correction deposit (type=10) for the same bet.

StepEndpointtypeamountExpected resultBalance change
1Deduct1-1.000-1.00
2Deposit9+2.000+2.00
3Deposit10+0.500+0.50

Step 3 has the same structure as Deposit, with these differences: type="10", amount="0.50", and the bet carries winlost="1.50", state=2. The response format is the same as Deposit.

008 Rollback Deduct

A deduct notification is assumed lost, so the platform sends a rollback (type=1). The merchant must return the deducted amount and respond result = 0.

StepEndpointtypeamountExpected resultBalance change
1Deduct1-1.000-1.00
2Rollback1+1.000+1.00
Step 2 request (POST application/json; rollback re-sends the deduct's notifyid, type and serialnumber from step 1; no bets/currency)
{
  "appid": "your_appid",
  "username": "player01",
  "token": "mock_123",
  "notifyid": "25081111204055555",
  "type": "1",
  "amount": "1.00",
  "serialnumber": "1754882501123456",
  "errmsg": "timeout",
  "sign": "0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b"
}
Step 2 request body (POST application/x-www-form-urlencoded, decoded; rollback re-sends the deduct's notifyid, type and serialnumber from step 1; no bets/currency)
appid=your_appid
username=player01
notifyid=25081111204055555
type=1
amount=1.00
serialnumber=1754882501123456
errmsg=timeout
sign=0d8f2e0b8a5c4f1e9c7a6b5d4e3f2a1b
Expected response (current balance after the rollback)
{
  "result": 0,
  "balance": 100.00
}

009 Rollback Deposit

A settle deposit notification is assumed lost, so the platform sends a rollback (type=9). The merchant must remove the deposited amount and respond result = 0.

StepEndpointtypeamountExpected resultBalance change
1Deduct1-1.000-1.00
2Deposit9+2.000+2.00
3Rollback92.000-2.00

Step 3 has the same structure as the Rollback in 008, with these differences: type="9", amount="2.00". The response format is the same.

010 Duplicate Rollback Deduct

The same rollback notification is sent twice (same notifyid, type and serialnumber). The merchant must only return the deducted amount once (idempotency). The second rollback response may be result = 0 or a negative rejection code; the pass criterion is that the balance does not move again.

StepEndpointtypeamountExpected resultBalance change
1Deduct1-1.000-1.00
2Rollback1+1.000+1.00
3Rollback1+1.000 or negative (rejection)0 (no second rollback)

Request/response are identical to 008; step 3 is an exact resend of step 2.

011 Rollback Unknown Serial

A rollback for a serialnumber the merchant never processed. The merchant must not move the balance; per the spec it responds result = 0, but a negative rejection code (e.g. order not found) is accepted as well. The pass criterion is that the balance does not change.

StepEndpointtypeamountExpected resultBalance change
1Rollback11.000 or negative (rejection)0 (no balance movement)

The request has the same structure as the Rollback in 008 (with a brand-new serialnumber).

Expected response
{
  "result": 0,
  "balance": 100.00
}