Web Payment — Standard Contract (Co-publishing)
| Applies to | Newly onboarded co-publishing games |
| Endpoints | check_uid, create_order, payment_notify, plus optional get_server_list and get_character_list |
| Signature | HMAC-SHA256 for a new integration. Two MD5 variants exist for already-integrated titles but are being deprecated — see Authentication. |
| Errors | Depends on your algorithm — errcode in a 200 OK body under HMAC-SHA256 (assumed by the examples below), HTTP status codes under either MD5 variant. |
This is the contract for newly onboarded co-publishing (co-pub) games — a separate contract from Web Payment — Expub's 4-API flow, not a newer version of it. Which one applies to your game depends on its publishing model (co-pub vs expub), not on when it was integrated.
Two differences matter most if you have seen the expub contract before:
- No account-linked character list. There is no
getUserListequivalent and you never receive an AlogameuserId: the player types their UID on the top-up screen and Alogame validates it withcheck_uid. If one UID of yours can own several characters, there is an optional per-UID picker — API 3 — Get Character List — but it is keyed byuid, never by an Alogame account. - Errors are carried in the response body, not the HTTP status. Every response is
200 OKwith anerrcodefield. Alogame treatserrcode: 0as success and anything else as failure.
Both are optional and independent. Alogame never calls either unless your Alogame Operations contact has turned it on for your game.
get_server_list— if your UIDs are only unique within a server (two servers can each have a character100002078), implement it so the player picks a server before typing their UID, and every subsequent call carriesserver_idalongsideuid. Skip it if UIDs are globally unique.get_character_list— if one UID can own more than one character, implement it so the player picks which character receives the top-up after their UID is validated, andcreate_ordercarriescharacter_id. Skip it if a UID identifies exactly one character (the common case).
So the full step order for a game that implements both is: pick server → type UID → pick character → pay.
Flow
Steps highlighted in green are implemented by your game server; everything else is handled by Alogame.
Step-by-step breakdown
| # | Who acts | What happens | Your responsibility |
|---|---|---|---|
| 1 | User | Player opens the top-up screen | — |
| 2–3 | Game Server | (only if server-scoped) Alogame calls get_server_list | Return the list of servers the player can pick from |
| 4–6 | Alogame + User | Player picks a server (if any) and types their in-game UID | — |
| 7–8 | Game Server | Alogame calls check_uid with the UID (and server_id, if server-scoped) | Return the character for this UID, or a not-found error |
| 9 | Alogame | Character name shown for confirmation | — |
| 10–11 | Game Server | (only if a UID owns several characters) Alogame calls get_character_list | Return every character this UID owns |
| 12–14 | Alogame + User | Player picks a character (if any), then the package and payment method | — |
| 15 | Alogame Backend | Alogame generates plat_order_num (its own order code) | — |
| 16–18 | Game Server | Alogame calls create_order | Create a local order record; return your own order_num |
| 19–22 | Alogame + User + Payment Provider | Player pays; provider notifies Alogame via IPN | — |
| 23 | Alogame Backend | Alogame verifies the IPN and marks the order paid | — |
| 24–26 | Game Server | Alogame calls payment_notify with both order codes | Grant the item; return errcode: 0 |
| 27–28 | Alogame | Player receives success notification | — |
Authentication
Use HMAC-SHA256 for a new co-pub integration — this page's examples assume it: requests are signed over the raw request body and carry x-timestamp and x-signature headers. The algorithm, the raw-body pitfall, and verification samples in PHP, Python and Node.js are on the Authentication page. Two MD5 variants exist for titles already integrated on them, but they're being deprecated — do not build a new integration against either.
Response envelope
Under HMAC-SHA256 (this page's examples), all three endpoints answer 200 OK with the same wrapper:
{
"errcode": 0,
"msg": "success",
"data": { }
}
| Field | Type | Description |
|---|---|---|
errcode | integer | 0 = success. Any other value is treated as a failure by Alogame. |
msg | string | Human-readable result. Also used for not-found detection on check_uid — see below. |
data | object | Endpoint-specific payload. Required on check_uid and create_order. |
Alogame aborts the request after 8 seconds. A slow response is treated as a failure — for payment_notify that triggers a retry, so a request that eventually succeeds after 10 seconds can still be delivered twice. Make delivery idempotent (see API 5).
API 1 — Get Server List (optional)
Only called for a game Alogame Operations has configured as server-scoped
— skip this entirely if your game has one shared pool of UIDs. Called once,
when the player opens the top-up screen, before check_uid.
Method: POST
Reference path: /get_server_list (any path may be registered)
Request body: empty JSON object ({}), still signed like every other call.
Response — success
{
"errcode": 0,
"data": [
{ "serverId": "1", "name": "Server 1" },
{ "serverId": "2", "name": "Server 2" }
]
}
| Field | Type | Description |
|---|---|---|
data[].serverId | string | Your server's identifier. id is also accepted as an alias. |
data[].name | string | Display name shown in the picker. A server with a missing/blank name is dropped from the list. |
Any errcode other than 0, or a non-array data, is treated the same as
an empty list — the player sees no server picker rather than an error, so a
transient failure here degrades gracefully instead of blocking the top-up
screen from loading. There is no "not found" case to handle.
API 2 — Check UID
Called when the player enters their UID on the top-up screen (after picking a server, if API 1 applies to your game).
Method: POST
Reference path: /check_uid (any path may be registered)
Request body
| Field | Type | Description |
|---|---|---|
uid | string | In-game character UID as typed by the player |
server_id | string | Only sent if your game implements API 1. The serverId the player picked. |
{
"uid": "100002078",
"server_id": "1"
}
Response — success
{
"errcode": 0,
"msg": "success",
"data": {
"nickname": "DragonSlayer",
"server": "Server 1"
}
}
Alogame displays the character name for confirmation, reading the first field present: nickname, then characterName, then name. Return at least one of them — if all three are absent the order cannot proceed.
Response — UID does not exist
{
"errcode": 1001,
"msg": "user not found"
}
Alogame matches the msg string case-insensitively for the substring not found or not exist, and only then tells the player their UID is invalid so they can correct it. Any other msg is shown as a generic system error instead.
The rest of the wording is free — user not found, The role does not exist and Character not found all work.
API 3 — Get Character List (optional)
Only called for a game Alogame Operations has configured as
character-scoped — skip this entirely if a UID identifies exactly one
character in your game. Called once, right after check_uid succeeds, so it
can rely on the uid having already been validated.
Web Payment — Expub has an identically-named API 1, but
it is a different mechanism: that one is keyed by an Alogame userId and
returns every character linked to that account across your game. This one
is keyed by a single uid the player typed, and returns only the characters
that UID owns. Co-pub games have no Alogame-linked account, so only this
form applies here.
Method: POST
Reference path: /get_character_list (any path may be registered)
Request body
| Field | Type | Description |
|---|---|---|
uid | string | The UID already validated by API 2 |
server_id | string | Only sent if your game also implements API 1. Same value check_uid received. |
{
"uid": "100002078",
"server_id": "1"
}
Response — success
{
"errcode": 0,
"data": [
{ "characterId": "c-1001", "name": "DragonSlayer" },
{ "characterId": "c-1002", "name": "IronMage" }
]
}
| Field | Type | Description |
|---|---|---|
data[].characterId | string | Your character's identifier. id is also accepted as an alias. Alogame sends the picked value back as character_id on create_order. |
data[].name | string | Display name shown in the picker. characterName is also accepted as an alias. A character with a missing/blank name and no usable id is dropped from the list. |
Any errcode other than 0, or a non-array data, is treated the same as
an empty list — the player sees no character picker rather than an error, so
a transient failure here degrades gracefully. There is no "not found" case to
handle: a UID that does not exist is already rejected by check_uid, and an
empty list for a valid UID is a legitimate answer, not an error.
API 4 — Create Order
Called after the player confirms the package, before the payment UI is shown.
Method: POST
Reference path: /create_order
Request body
| Field | Type | Description |
|---|---|---|
uid | string | Character UID, already validated by API 2 |
plat_order_num | string | Alogame's order code. Store it — it is the correlation key for API 5 |
productid | string | Your own product ID, exactly as you submitted it to Alogame |
amount | integer | Order total in VND |
sandbox | integer | 0 in production, 1 in dev/staging |
server_id | string | Only sent if your game implements API 1. Same value check_uid already received. |
character_id | string | Only sent if your game implements API 3. The characterId the player picked. Deliver the purchase to this character, not merely to the UID. |
{
"uid": "100002078",
"plat_order_num": "260509161539010042",
"productid": "pack_500_gold",
"amount": 49000,
"sandbox": 0,
"server_id": "1",
"character_id": "c-1001"
}
Response — success
{
"errcode": 0,
"msg": "success",
"data": {
"order_num": "PARTNER-ORD-20260315-001"
}
}
data.order_num is required. It is your own order code and Alogame sends it back in API 5. A response with errcode: 0 but no order_num is treated as a failure and the order is aborted.
plat_order_numIf Alogame retries and the same plat_order_num arrives twice, return the original order_num with errcode: 0 rather than creating a second order.
API 5 — Payment Notify
Called after the payment provider confirms payment. Grant the purchased item.
Method: POST
Reference path: /payment_notify
Request body
| Field | Type | Description |
|---|---|---|
plat_order_num | string | Alogame's order code, same as API 4 |
order_num | string | Your order code, returned from API 4 |
amount | integer | Order total in VND |
status | integer | Always 1 (payment successful) |
{
"plat_order_num": "260509161539010042",
"order_num": "PARTNER-ORD-20260315-001",
"amount": 49000,
"status": 1
}
Response — success
{
"errcode": 0,
"msg": "success"
}
Any response other than errcode: 0 — including a timeout — schedules a retry. Alogame makes up to 4 attempts: the initial call, then retries after 60s, 120s, and 120s.
Deduplicate on order_num (or plat_order_num) and return errcode: 0 for a repeat of an order you have already delivered. Do not grant the item again, and do not return an error — an error keeps the retry loop running against an order that is already complete.
Endpoint registration — standard contract
Give your Alogame Operations Manager one base URL and three required paths per environment (plus either optional picker, if it applies), and a shared secret_key:
# Production
base_url: https://api.yourgame.com
get_server_list: /get_server_list # only if server-scoped
get_character_list: /get_character_list # only if a UID owns several characters
check_uid: /check_uid
create_order: /create_order
payment_notify: /payment_notify
# Dev / Staging
base_url: http://<dev-host>
Absolute URLs are accepted per endpoint if your paths live on different hosts. Production must use HTTPS with a valid TLS certificate; dev/staging may use HTTP.
The secret_key is issued by Alogame and stored encrypted. Never expose it in a client, a repository, or a log line.
With neither optional picker, check_uid receives nothing but uid, so a
uid alone must identify exactly one character — unique across every server,
or the same UID matches several characters and the order cannot be routed.
Each picker relaxes that in a different direction:
- API 1 — Get Server List adds
server_idtocheck_uid/create_order, so your UIDs only need to be unique within a server. - API 3 — Get Character List adds
character_idtocreate_order, so auidis allowed to own several characters — the player says which one.
Tell your Alogame Operations contact up front which case applies to your game: it decides which endpoints get wired in, and a game whose UIDs are ambiguous with neither picker enabled cannot deliver reliably.
Implementation checklist
- Submitted base URL + 3 required paths, plus either optional picker path that applies (production + dev/staging), to Alogame Operations
- Told Alogame Operations whether your game is server-scoped, so
get_server_listgets wired in andserver_idis turned on forcheck_uid/create_order - Told Alogame Operations whether one UID can own several characters, so
get_character_listgets wired in andcharacter_idis turned on forcreate_order - Production endpoints use HTTPS with a valid TLS certificate
- Signature verified against the raw request body, not a re-serialized object
-
x-timestamptreated as milliseconds; requests older than 600000 ms rejected - Signature compared with a constant-time function, not
== - All endpoints answer
200 OKwith{ errcode, msg, data }; failures use a non-zeroerrcode -
get_server_list(if implemented): returnsdata[]withserverId/id+nameper server -
check_uid: returnsdata.nickname(orcharacterName/name); not-foundmsgcontainsnot foundornot exist -
get_character_list(if implemented): returnsdata[]withcharacterId/id+name/characterNameper character -
create_order: returnsdata.order_num; a repeatedplat_order_numreturns the originalorder_num; delivery is routed tocharacter_idwhen it is sent -
payment_notify: grants the item, then returnserrcode: 0; a repeat of a delivered order returnserrcode: 0without re-granting - All endpoints respond in under 8 seconds
- Delivery logged with
plat_order_num,order_num,uid,productidfor CS lookup