Skip to content
Vum Game Market

Reseller API · v1

Vum Game Market API

Base URL https://vumgm.com/api/v1. JSON in, JSON out, USD cents for money. Keys are created in the reseller portal after your account is approved.

Authentication

Every request carries four headers. The signature is a hex HMAC-SHA256 of a canonical string, using your signing secret.

X-Api-Key:   vgmk_…
X-Timestamp: 1761170000            (unix seconds, ±300 s)
X-Nonce:     8f2c0e4d9a1b4c77      (16–64 chars, never reused)
X-Signature: hex(HMAC_SHA256(secret, canonical))

canonical = timestamp + "\n" + nonce + "\n" + METHOD + "\n" + path_with_query + "\n" + sha256_hex(raw_body)
           e.g. "1761170000\n8f2c0e4d9a1b4c77\nPOST\n/api/v1/orders\n"
           (GET requests hash the empty string)

PHP

function vumgm(string $method, string $path, ?array $body = null): array {
    $key = getenv('VUMGM_KEY'); $secret = getenv('VUMGM_SECRET');
    $raw = $body === null ? '' : json_encode($body);
    $ts = (string) time(); $nonce = bin2hex(random_bytes(12));
    $sig = hash_hmac('sha256', "$ts\n$nonce\n$method\n$path\n".hash('sha256', $raw), $secret);
    $ch = curl_init('https://vumgm.com'.$path);
    curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => $raw ?: null,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json', "X-Api-Key: $key", "X-Timestamp: $ts", "X-Nonce: $nonce", "X-Signature: $sig"]]);
    return json_decode(curl_exec($ch), true);
}

Node.js

import crypto from 'node:crypto';
async function vumgm(method, path, body) {
  const raw = body ? JSON.stringify(body) : '';
  const ts = String(Math.floor(Date.now() / 1000)), nonce = crypto.randomBytes(12).toString('hex');
  const hash = crypto.createHash('sha256').update(raw).digest('hex');
  const sig = crypto.createHmac('sha256', process.env.VUMGM_SECRET).update(`${ts}\n${nonce}\n${method}\n${path}\n${hash}`).digest('hex');
  const res = await fetch('https://vumgm.com' + path, { method, body: raw || undefined, headers: {
    'Content-Type': 'application/json', 'X-Api-Key': process.env.VUMGM_KEY, 'X-Timestamp': ts, 'X-Nonce': nonce, 'X-Signature': sig } });
  return res.json();
}

Errors

Errors return {"error": {"code": "…", "message": "…"}}. Codes: missing_auth, invalid_key, bad_signature, stale_timestamp, replayed_nonce, ip_not_allowed, insufficient_scope, rate_limited (429), validation_failed, product_unavailable, package_unavailable, insufficient_credit (402), daily_limit, order_limit, price_below_cost, reference_conflict (409), not_found, server_error.

GET /account

Credit line, tier and key info. Any scope. Also returns server_time for clock checks.

GET /products · GET /products/{slug}

Scope catalog:read. Products currently open for ordering, with packages[].id, price_usd_cents (your wholesale price), requires_zone, and custom_quantity limits where custom amounts are allowed.

POST /recipients/validate

Scope recipients:validate. Body {"product":"bigo-live","recipient_id":"902837461"}. Returns valid, verified (false when the supplier cannot check) and name where available.

POST /orders

Scope orders:write. Charged to your credit line; fulfilment starts automatically.

{
  "reference": "your-unique-id-123",   // required, idempotency key (max 64, [A-Za-z0-9._-])
  "product": "bigo-live",
  "package_id": 3,                     // or "quantity": 1234 for custom amounts
  "recipient_id": "902837461",
  "recipient_zone": null               // required when the product requires_zone
}

201 = created. 200 = the same reference was already used with identical parameters; the original order is returned and you are not charged twice. A different payload with a used reference returns 409. On network errors, retry with the same reference.

Statuses: queued → processing → supplier_pending → completed, or under_review / failed / refunded. Only completed means delivered.

GET /orders/{reference}

Scope orders:read. Accepts our reference or yours.

GET /orders?status=&limit=&before_id=

Newest first, up to 100 per page. Pass next_before_id as before_id to page.

Webhooks

Set an HTTPS endpoint in the portal. We POST the order object on order.pending, order.under_review, order.completed, order.failed, order.refunded, retrying with backoff for about 2 hours. Verify every delivery:

expected = "sha256=" + hex(HMAC_SHA256(webhook_secret, X-VumGM-Timestamp + "." + raw_body))
compare expected with X-VumGM-Signature in constant time; reject timestamps older than 5 minutes;
de-duplicate on X-VumGM-Delivery (event id). Respond 2xx within 10 s.

Test mode

Keys created in test mode authenticate and sign exactly like live keys, but POST /orders returns a simulated completed order with "test": true. Nothing reaches a supplier and nothing is charged. All other endpoints return real data.