REST API - v1
রিসেলারদের জন্য গেম টপ আপ ও ভাউচার API
গেম টপ আপ, গেম রিচার্জ আর ভাউচার কোডের জন্য একটাই REST API। ক্যাটালগ পড়ুন, প্লেয়ার যাচাই করুন, অর্ডার তৈরি করুন, আর ফলাফল নিন webhook বা status poll দিয়ে।
SHOP2TOPUP-এর পেছনে যে হোস্ট-টু-হোস্ট (H2H) টপ আপ API কাজ করে, এটি সেটিই। HTTPS-এর ওপর চলা সাধারণ JSON API, যেখানে আছে Bearer অথেন্টিকেশন, UUID আইডেমপোটেন্সি কী, প্রকাশিত রেট লিমিট আর মেশিনে পড়া যায় এমন স্থির এরর কোড। ফলে কোনো শপ, স্টোরফ্রন্ট বা বট ক্লায়েন্ট লাইব্রেরি ছাড়াই প্রোগ্রাম দিয়ে ডিজিটাল গেম ক্রেডিট বিক্রি করতে পারে। টানা 5+ বছর ধরে চালু থাকা এই একই ইন্টারফেস দিয়ে 3,000,000+ অর্ডার ডেলিভারি হয়েছে।
দাম, পেমেন্ট মেথড আর পার্টনার অ্যাকাউন্ট কীভাবে চলে, তা জানতে দেখুন SHOP2TOPUP রিসেলার প্রোগ্রামের সারসংক্ষেপ.
কিছু সাইন করার আগেই আপনার প্রথম কল
প্রতিটি endpoint একটি সাধারণ HTTPS রিকোয়েস্ট, তাতে থাকে মাত্র একটি হেডার। কোনো SDK ইনস্টল করতে হয় না, কোনো client library vendor করতে হয় না, build step-ও যোগ করতে হয় না। নিচের কলটি পেস্ট করুন, আপনার key বসান, আর টার্মিনালেই লাইভ ক্যাটালগ রেসপন্স পেয়ে যাবেন।
curl -s "https://shop2topup.com/api/endpoints/v1/catalog/categories?bigCategoryId=1" \
-H "Authorization: Bearer YOUR_KEY_ID.YOUR_KEY_SECRET"{
"success": true,
"categories": [
{
"id": 12,
"name": "Free Fire",
"description": "Garena Free Fire diamonds",
"big_category_id": 1,
"big_category_name": "Mobile Games"
}
]
}বেস ইউআরএল
https://shop2topup.com/api/endpoints/v1
ডকুমেন্টেশনের প্রতিটি path এই বেসের সাপেক্ষে লেখা। রেসপন্স আসে JSON-এ, আর তাতে সবসময় একটি boolean success ফিল্ড থাকে। তাই আপনার ক্লায়েন্টে একটি branch দিয়েই সফল ফলাফল আর বাকি সব আলাদা করা যায়।
এরর রেসপন্সের কাঠামো
সব স্ট্যাটাস কোডেই ব্যর্থতার কাঠামো একই থাকে। পরের ধাপ ঠিক করতে error.code ব্যবহার করুন, কারণ এটি API চুক্তির অংশ। কখনো error.message দেখে পরের ধাপ ঠিক করবেন না। এটি মানুষের পড়ার জন্য লেখা এবং এর ভাষা যেকোনো সময় বদলাতে পারে।
{
"success": false,
"error": {
"code": "PRICE_INCREASED",
"message": "Price has increased beyond expected",
"action": "FIX_INPUT",
"retryable": false,
"retry_after": null,
"details": {
"expected_unit_price": "0.950000",
"current_unit_price": "0.980000"
}
}
}পুরো API এক স্ক্রিনে
11টি endpoint দিয়েই পুরো ইন্টিগ্রেশন হয়ে যায়: ক্যাটালগ পড়া, আইটেমের দাম জানা, প্লেয়ার যাচাই, অর্ডার দেওয়া এবং পরে হিসাব মিলিয়ে দেখা। প্রতিটি সারি সরাসরি পূর্ণ রেফারেন্সে নিয়ে যায়। সেখানে প্রতিটি parameter, একটি উদাহরণ রিকোয়েস্ট ও একটি উদাহরণ রেসপন্স আছে।
| মেথড | পাথ | কী করে | রেফারেন্স |
|---|---|---|---|
| GET | /account | অ্যাকাউন্টের তথ্য দেখুন | রেফারেন্স পড়ুন |
| GET | /catalog/big-categories | বিগ ক্যাটাগরির তালিকা | রেফারেন্স পড়ুন |
| GET | /catalog/categories | ক্যাটাগরির তালিকা | রেফারেন্স পড়ুন |
| GET | /catalog/subcategories | সাবক্যাটাগরির তালিকা (প্রোডাক্ট) | রেফারেন্স পড়ুন |
| GET | /catalog/subcategory/:itemId/price | আইটেমের দাম দেখুন | রেফারেন্স পড়ুন |
| GET | /catalog/category/:categoryId/requirements | ক্যাটাগরির রিকোয়ারমেন্ট দেখুন | রেফারেন্স পড়ুন |
| POST | /player/validate | প্লেয়ার যাচাই করুন | রেফারেন্স পড়ুন |
| POST | /orders/create | অর্ডার তৈরি করুন | রেফারেন্স পড়ুন |
| GET | /orders/:orderId | অর্ডারের স্ট্যাটাস দেখুন | রেফারেন্স পড়ুন |
| POST | /orders/batch | একসাথে কয়েকটি অর্ডার দেখুন | রেফারেন্স পড়ুন |
| GET | /orders | অর্ডারের তালিকা | রেফারেন্স পড়ুন |
এর সাথে আছে অর্ডার স্ট্যাটাসের webhook, যার পূর্ণ বিবরণ এই পেজের নিচের দিকে পাবেন।
অথেন্টিকেশন ও অ্যাক্সেস নিয়ন্ত্রণ
অথেন্টিকেশনের জন্য লাগে মাত্র একটি HTTP হেডার। প্রতিটি রিকোয়েস্টে একটি Authorization হেডার থাকে। তাতে থাকে key ID ও secret দিয়ে তৈরি একটি Bearer credential, যা আপনার রিসেলার প্যানেলের API Access পেজ থেকে দেওয়া হয়। key ID দিয়ে অ্যাকাউন্ট চেনা যায়। secret প্রমাণ করে অ্যাকাউন্টটি আপনার, আর সার্ভারে HMAC চেক দিয়ে সেটি যাচাই হয়।
secret শুধু একবারই দেখানো হয়, তৈরি করার সময়। পরে আর কখনো সেটি ফিরে পাওয়া যায় না। আপনার স্ট্যাকে যে secret manager আগে থেকে আছে, সেখানে রাখুন, environment variable হিসেবে দিন, আর source control থেকে দূরে রাখুন। হারিয়ে গেলে উদ্ধার করার উপায় নেই, rotate করতে হবে।
এখানে কোনো OAuth রিডাইরেক্ট নেই, সেশন কুকি নেই, রিফ্রেশ টোকেন নেই, আগে থেকে শিডিউল করে রাখার মতো কিছুও নেই। অটোমেটেড ইন্টিগ্রেশনের জন্য এটা জরুরি: ক্রন জব বা কিউ ওয়ার্কার একই ক্রেডেনশিয়াল বছরের পর বছর ব্যবহার করতে পারে, রিনিউ করার কোনো ধাপ লাগে না। মেয়াদ ফুরানোর কোনো ঘড়িও নেই, তাই রাত তিনটায় ঘুম ভেঙে উঠতে হয় না।
প্রতিটি API কী-এর সঙ্গে চাইলে একটি IP অ্যালাউলিস্ট জুড়ে দেওয়া যায়। তালিকাটি খালি না থাকলে অন্য যেকোনো ঠিকানা থেকে আসা রিকোয়েস্ট বিজনেস লজিক চলার আগেই IP_NOT_ALLOWED ও HTTP 403 দিয়ে বাতিল হয়ে যায়। তাই ক্রেডেনশিয়াল ফাঁস হলেও আপনার নিজের সার্ভারের বাইরে সেটি অকেজো। কী রোটেশন মানে একটা সাধারণ ডিপ্লয়, কোনো মাইগ্রেশন নয়: নতুন কী তৈরি করুন, সেটি চালু করুন, তারপর পুরোনোটি মুছে দিন।
প্রতিটি key একটি রিসেলার অ্যাকাউন্টের, আর সেই অ্যাকাউন্টের ওয়ালেট থেকেই খরচ করে। তাই secret কখনো ব্রাউজার, মোবাইল বান্ডল বা পাবলিক রিপোজিটরিতে রাখবেন না। API কল করুন আপনার backend থেকে, আর আপনার front end যোগাযোগ করবে আপনার backend-এর সাথে। এখানে লেখা সবকিছু server-to-server কলকারীর কথা ধরে লেখা।
Authorization: Bearer <keyId>.<secret>এক কলেই key যাচাই করুন
GET /account হলো credential-এর health check। এটি একসাথে তিনটি প্রশ্নের উত্তর দেয়: key অথেন্টিকেট হয় কি না, অ্যাকাউন্ট চালু আছে কি না, আর যা অর্ডার করতে যাচ্ছেন তার জন্য ওয়ালেটে যথেষ্ট ব্যালেন্স আছে কি না। boot-এর সময় এবং প্রতিটি batch run-এর আগে এটি চালান।
অথেন্টিকেশন ও অ্যাকাউন্ট রেফারেন্স পড়ুনশুরু থেকে শেষ পর্যন্ত একটি পূর্ণ ইন্টিগ্রেশন
কার্যকর একটি ইন্টিগ্রেশনে ধাপ মাত্র 4টি। নিচের নমুনাগুলো cURL, Node ও PHP-তে লাইভ API-তে সবগুলো ধাপ চালায়। নিজের credential বসিয়ে দিলে যেমন লেখা আছে তেমনই চলবে।
- 1
ক্যাটালগ পড়ুন
বিগ ক্যাটাগরি থেকে ক্যাটাগরি হয়ে সাবক্যাটাগরিতে যান, তারপর যে আইটেম বিক্রি করতে যাচ্ছেন তার বর্তমান দাম দেখে নিন। ট্রি ক্যাশ করে রাখুন, তবে দাম প্রতিবার নতুন করে নিন।
- 2
প্লেয়ার যাচাই করুন
কোনো লেনদেন হওয়ার আগেই আইডেন্টিফায়ার থেকে ইন-গেম নাম আর রিজিয়ন বের করে নিন। টাইপের ভুল আর ভুল রিজিয়নের সমস্যা এই ধাপেই ধরা পড়ে।
- 3
অর্ডার তৈরি করুন
একটি UUID তৈরি করে আগে সেভ করে রাখুন, তারপর সেটি order_id হিসেবে পাঠান। এটিই আপনার idempotency key, আর create রিকোয়েস্ট নিরাপদে আবার পাঠানোর একমাত্র উপায়ও এটাই।
- 4
ডেলিভারি কনফার্ম করুন
অর্ডার স্ট্যাটাস webhook আসার অপেক্ষা করুন। নিজের ঠিক করা টাইমআউটের পরও যে অর্ডার পেন্ডিং থাকে, সেগুলোর স্ট্যাটাস আলাদা করে চেক করে নিন।
পুরো প্রক্রিয়া: cURLকোড দেখুনকোড লুকান
export S2T_KEY="YOUR_KEY_ID.YOUR_KEY_SECRET"
export S2T_BASE="https://shop2topup.com/api/endpoints/v1"
# 1. Find the item you want to sell.
curl -s "$S2T_BASE/catalog/subcategories?categoryId=12" \
-H "Authorization: Bearer $S2T_KEY"
# 2. Read the exact price you will be charged for it.
curl -s "$S2T_BASE/catalog/subcategory/999/price" \
-H "Authorization: Bearer $S2T_KEY"
# 3. Confirm the player exists BEFORE any money moves.
curl -s -X POST "$S2T_BASE/player/validate" \
-H "Authorization: Bearer $S2T_KEY" \
-H "Content-Type: application/json" \
-d '{"sub_category_id": 999, "player_id": "123456789", "server": "Asia"}'
# 4. Create the order. order_id is YOUR uuid and YOUR idempotency key.
curl -s -X POST "$S2T_BASE/orders/create" \
-H "Authorization: Bearer $S2T_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "01912345-6789-7abc-8def-0123456789ab",
"sub_category_id": 999,
"quantity": 1,
"requirements": { "player_id": "123456789", "server": "Asia" },
"expected_unit_price": "0.950000"
}'
# 5. Read the order back until it leaves "pending".
curl -s "$S2T_BASE/orders/01912345-6789-7abc-8def-0123456789ab" \
-H "Authorization: Bearer $S2T_KEY"সম্পূর্ণ ফ্লোর উদাহরণ (Node.js)কোড দেখুনকোড লুকান
// Node 18+ — no dependencies, global fetch and global crypto.
const BASE = 'https://shop2topup.com/api/endpoints/v1';
const KEY = process.env.S2T_KEY; // "<keyId>.<secret>", server-side only.
async function call(path, init = {}) {
const res = await fetch(BASE + path, {
...init,
headers: {
Authorization: `Bearer ${KEY}`,
'Content-Type': 'application/json',
...(init.headers || {}),
},
});
if (res.status === 429) {
const wait = Number(res.headers.get('Retry-After') || 5);
await new Promise((r) => setTimeout(r, wait * 1000));
return call(path, init);
}
const body = await res.json();
if (!res.ok || body.success === false) {
throw Object.assign(new Error(body?.error?.code || 'HTTP_' + res.status), {
code: body?.error?.code,
status: res.status,
});
}
return body;
}
async function sell({ itemId, categoryId, playerId, server }) {
// 1 + 2. Catalog and current price.
const { subcategories } = await call(
`/catalog/subcategories?categoryId=${categoryId}`,
);
const item = subcategories.find((s) => s.item_id === itemId);
const { price } = await call(`/catalog/subcategory/${itemId}/price`);
// 3. Player check before charging.
const { player } = await call('/player/validate', {
method: 'POST',
body: JSON.stringify({
sub_category_id: itemId,
player_id: playerId,
server,
}),
});
// 4. Create the order. Persist orderId BEFORE the call so a crash mid-flight
// can be replayed with the same uuid instead of double-charging.
const orderId = crypto.randomUUID();
await saveIntent(orderId, itemId, playerId);
const { order } = await call('/orders/create', {
method: 'POST',
body: JSON.stringify({
order_id: orderId,
sub_category_id: itemId,
quantity: 1,
requirements: { player_id: playerId, server },
expected_unit_price: price.unit_price,
}),
});
// 5. Confirm. A webhook usually beats the poll; the poll is the safety net.
let status = order.status;
while (status === 'pending') {
await new Promise((r) => setTimeout(r, 20000));
status = (await call(`/orders/${orderId}`)).order.status;
}
return { orderId, status, itemName: item?.name, playerName: player.player_name };
}পুরো প্রক্রিয়া: PHPকোড দেখুনকোড লুকান
<?php
// PHP 8 — plain cURL, no SDK required.
const S2T_BASE = 'https://shop2topup.com/api/endpoints/v1';
function s2t(string $path, ?array $body = null): array
{
$ch = curl_init(S2T_BASE . $path);
$headers = [
'Authorization: Bearer ' . getenv('S2T_KEY'),
'Content-Type: application/json',
];
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30,
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($raw, true) ?: [];
if ($status >= 400 || ($data['success'] ?? false) === false) {
throw new RuntimeException($data['error']['code'] ?? 'HTTP_' . $status);
}
return $data;
}
// 1 + 2. Catalog, then the exact price for the item.
$catalog = s2t('/catalog/subcategories?categoryId=12');
$price = s2t('/catalog/subcategory/999/price');
// 3. Player check before charging.
$player = s2t('/player/validate', [
'sub_category_id' => 999,
'player_id' => '123456789',
'server' => 'Asia',
]);
// 4. Create the order with your own uuid as the idempotency key.
$orderId = sprintf(
'%04x%04x-%04x-7%03x-%04x-%04x%04x%04x',
random_int(0, 0xffff), random_int(0, 0xffff),
random_int(0, 0xffff), random_int(0, 0x0fff),
random_int(0x8000, 0xbfff),
random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff)
);
$order = s2t('/orders/create', [
'order_id' => $orderId,
'sub_category_id' => 999,
'quantity' => 1,
'requirements' => ['player_id' => '123456789', 'server' => 'Asia'],
'expected_unit_price' => $price['price']['unit_price'],
]);
// 5. Read it back until it settles.
do {
sleep(20);
$latest = s2t('/orders/' . $orderId);
} while ($latest['order']['status'] === 'pending');
echo $latest['order']['status'];দুটি বিষয় হুবহু অনুসরণ করুন। create কলের আগে অর্ডারের UUID সেভ করুন, পরে নয়। আর ক্যাশ করা দামের ওপর ভরসা না করে দাম আবার পড়ে নিন। এই দুই অভ্যাসেই প্রায় সব ডাবল চার্জ এবং সব PRICE_INCREASED রিজেকশন বন্ধ হয়ে যায়।
Webhook: অর্ডার স্ট্যাটাস কলব্যাক
একটি HTTPS এন্ডপয়েন্ট রেজিস্টার করুন, অর্ডারের ফল জানা মাত্রই প্ল্যাটফর্ম সেখানে পাঠিয়ে দেবে। ফলে আপনার স্টোরফ্রন্টকে আর বারবার পোলিং করতে হবে না, ঘটনা ঘটলেই সাড়া দিতে পারবে। প্রতিটি কলব্যাক সাইন করা থাকে, তাই কাজ শুরুর আগে নিশ্চিত হতে পারবেন যে payload আমাদের কাছ থেকেই এসেছে।
ইভেন্ট
| ইভেন্ট | কখন পাঠানো হয় |
|---|---|
| order.completed | অর্ডারের একটি ইউনিট ডেলিভারি হয়েছে। পুরো অর্ডারের অবস্থা জানতে data.status দেখুন। |
| order.refunded | অর্ডারের একটি ইউনিট আপনার ওয়ালেটে রিফান্ড হয়েছে। সব ইউনিট রিফান্ড হলে data.status-এ refunded দেখায়। |
| webhook.test | আপনার হ্যান্ডলার যাচাই করতে আপনি প্যানেল থেকে একটি সাইন করা টেস্ট ডেলিভারি পাঠিয়েছেন। |
ডেলিভারি হেডার
| হেডার | ভ্যালু |
|---|---|
| Content-Type | application/json |
| X-Shop2Topup-Signature | sha256=<hex> ফরম্যাটে, পার্স করার আগের মূল রিকোয়েস্ট বডির ওপর হিসাব করা HMAC-SHA256 |
| X-Shop2Topup-Event | ইভেন্টের নাম, যেমন অর্ডার সম্পন্ন হলে order.completed মান আসে |
| User-Agent | shop2topup-webhook/1.0 |
কলব্যাক পেলোড
প্রতিটি কলব্যাকে একই তিনটি টপ-লেভেল ফিল্ড থাকে: event, timestamp ও data। data-র গঠন স্ট্যাটাস এন্ডপয়েন্টের ফেরত দেওয়া অর্ডার অবজেক্টের মতোই। ভাউচার কোড শুধু সম্পন্ন অর্ডারেই থাকে, player_name null হতে পারে, আর অর্ডার ইউনিটে ভাগ হলে sub_transaction_summary দেখা যায়।
{
"event": "order.completed",
"timestamp": "2026-03-11T14:30:00.000Z",
"data": {
"order_id": "01912345-6789-7abc-8def-0123456789ab",
"status": "completed",
"player_id": "123456789",
"player_name": "ProGamer99",
"subcategory_name": "FF 100 Diamonds",
"quantity": 1,
"charged_amount": "0.950000",
"currency": "USD",
"created_at": "2026-03-11T14:28:00.000Z",
"completed_at": "2026-03-11T14:30:00.000Z",
"vouchers": [
{
"code": "XXXX-YYYY-ZZZZ",
"serial_number": "SN-9928371",
"expiry_date": "2027-03-11"
}
],
"sub_transaction_summary": {
"total": 1,
"completed": 1,
"failed": 0,
"pending": 0,
"processing": 0,
"retrying": 0,
"refunded": 0
}
}
}সিগনেচার যাচাই
প্রতিটি ডেলিভারিতে X-Shop2Topup-Signature হেডার থাকে। এর মান sha256= দিয়ে শুরু, তার পরে থাকে আপনার webhook secret দিয়ে হিসাব করা raw রিকোয়েস্ট বডির HMAC-SHA256, hex আকারে। JSON পার্স করার আগেই raw বাইটের ওপর যাচাই করুন, আর তুলনা করুন constant time-এ। যে হ্যান্ডলার আগে পার্স করে পরে যাচাই করে, নতুন করে serialise করা বডি দিয়ে তাকে ধোঁকা দেওয়া যায়।
// Express — verify the signature against the RAW body, not the parsed object.
const crypto = require('crypto');
app.post(
'/shop2topup/webhook',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.get('X-Shop2Topup-Signature') || '';
const expected =
'sha256=' +
crypto
.createHmac('sha256', process.env.S2T_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).end();
}
const payload = JSON.parse(req.body.toString('utf8'));
// Answer fast, then do the work. The delivery times out after 10 seconds.
res.status(200).end();
enqueueSettlement(payload.data.order_id, payload.event);
},
);ডেলিভারি ও রিট্রাই
প্রতিটি কলব্যাক মাত্র একবার পাঠানো হয়, আর উত্তর দেওয়ার জন্য আপনার এন্ডপয়েন্ট পায় 10 সেকেন্ড। এর পেছনে কোনো স্বয়ংক্রিয় রিট্রাই বা exponential backoff নেই, এবং সেটা ইচ্ছাকৃত। স্ট্যাটাস এন্ডপয়েন্টই চূড়ান্ত রেকর্ড, তাই মিলিয়ে দেখার কাজটা হওয়া উচিত আপনার নিয়ন্ত্রণে থাকা পোলিংয়ে, চোখের আড়ালে চলা কোনো রিট্রাই শিডিউলে নয়। সঙ্গে সঙ্গে 200 ফেরত দিন, তারপর কাজটা নিজের queue-তে করুন।
পুনরাবৃত্তি-নিরাপদ হ্যান্ডলার
order_id-কে হ্যান্ডলারের key হিসেবে ব্যবহার করুন, যাতে একই কলব্যাক আবার এলে কিছুই না ঘটে। ডেলিভারি একবার হলেও আপনার নিজের ইনফ্রাস্ট্রাকচার মাঝে মাঝে একই মেসেজ দুটি ওয়ার্কারের হাতে তুলে দেবে। কাস্টমারকে দেরিতে ক্রেডিট দেওয়ার চেয়ে দুবার ক্রেডিট দেওয়া অনেক বড় বাগ।
রেজিস্ট্রেশন ও সিক্রেট
কলব্যাক URL রেজিস্টার করা, সাইন করা টেস্ট ডেলিভারি পাঠানো আর signing secret রোটেট করা, সবই হয় লগইন করা রিসেলার প্যানেল থেকে, পাবলিক API দিয়ে নয়। HTTPS বাধ্যতামূলক, আর প্রাইভেট অ্যাড্রেস গ্রহণ করা হয় না।
রিসেলার প্যানেলে API অ্যাক্সেস খুলুনরেট লিমিট ও এরর হ্যান্ডলিং
লিমিটগুলো প্রকাশ করা আছে, প্রতিটি অ্যাকাউন্টের জন্য আলাদা, আর রোলিং উইন্ডো ধরে প্রয়োগ হয়। ক্যাটালগ রিডের কাজে সবচেয়ে বেশি সীমা পাওয়া যায়। স্ট্যাটাস এন্ডপয়েন্টের সীমা ইচ্ছা করেই কম, কারণ সেগুলো রাখা হয়েছে মিলিয়ে দেখার ব্যাকআপ হিসেবে, বারবার পোলিং করার জন্য নয়।
| রুট | রিকোয়েস্ট | উইন্ডো | গণনার ভিত্তি |
|---|---|---|---|
| GET /catalog/* (5টিই) | 5000 | 60 সেকেন্ড | প্রতি অ্যাকাউন্টে |
| POST /orders/create | 5000 | 60 সেকেন্ড | প্রতি অ্যাকাউন্টে |
| GET /orders/:orderId | 3 | 60 সেকেন্ড | প্রতি অ্যাকাউন্ট + অর্ডার আইডি |
| POST /orders/batch | 2 | 60 সেকেন্ড | প্রতি অ্যাকাউন্টে |
| GET /orders | 2 | 60 সেকেন্ড | প্রতি অ্যাকাউন্টে |
| GET /account | 60 | 60 সেকেন্ড | প্রতি অ্যাকাউন্টে |
| POST /player/validate | দৈনিক প্লেয়ার চেক কোটা | 24 ঘণ্টা | প্রতি অ্যাকাউন্টে (X-RateLimit হেডার পাঠানো হয় না) |
ক্যাটালগ, অর্ডার আর অ্যাকাউন্ট এন্ডপয়েন্টের প্রতিটি রেসপন্সে X-RateLimit-Limit, X-RateLimit-Remaining ও X-RateLimit-Reset হেডার থাকে, তাই ঠিকভাবে বানানো ক্লায়েন্টকে লিমিট নিয়ে কখনো আন্দাজ করতে হয় না। Remaining কাউন্টারে নজর রাখুন এবং থ্রটল হওয়ার আগেই গতি কমান, পরে নয়। ব্যতিক্রম শুধু প্লেয়ার ভ্যালিডেশন: এটি চলে আপনার দৈনিক প্লেয়ার-চেক কোটায়, কোনো X-RateLimit হেডার পাঠায় না, আর কোটার তথ্য দেয় 429 রেসপন্সের body-তে।
HTTP/1.1 429 Too Many Requests
Retry-After: 45
X-RateLimit-Limit: 2
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1765000000
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again after retry_after seconds.",
"action": "RETRY_LATER",
"retryable": true,
"retry_after": 45,
"details": {
"limit": 2,
"remaining": 0,
"retry_after": 45,
"window_seconds": 60
}
}
}শুরুতেই যে এররগুলো পাবেন, আর সেগুলোর সমাধান
ব্যর্থ রিকোয়েস্টে HTTP স্ট্যাটাসের সঙ্গে সবসময় একটি স্থির কোড ফেরত আসে। নতুন ইন্টিগ্রেশনে প্রথম সপ্তাহেই এগুলো দেখা যায়। প্রতিটির পাশে দেওয়া আছে কী বদলালে সমস্যা মিটবে।
| কোড | HTTP স্ট্যাটাস কোড | মানে | সমাধান |
|---|---|---|---|
| INVALID_API_KEY | 401 | API key সঠিক নয়আপনার দেওয়া API key সিস্টেমে চালু থাকা কোনো key-এর সাথে মিলছে না। | আপনার API কী-এর আইডি আর সিক্রেট ঠিক আছে কি না মিলিয়ে দেখুন। দরকার হলে API অ্যাক্সেস প্যানেল থেকে কী নতুন করে তৈরি করুন।action: FIX_INPUT · retryable: false · retry_after: null |
| IP_NOT_ALLOWED | 403 | IP অ্যাড্রেস অনুমোদিত তালিকায় নেইরিকোয়েস্টটি এমন একটি IP অ্যাড্রেস থেকে এসেছে, যা আপনার API key-এর অনুমোদিত তালিকায় নেই। | API Access প্যানেলে গিয়ে আপনার সার্ভারের IP অনুমোদিত তালিকায় যোগ করুন, অথবা IP সীমাবদ্ধতা তুলে দিন।action: FIX_INPUT · retryable: false · retry_after: null |
| MISSING_REQUIRED_FIELD | 400 | প্রয়োজনীয় ফিল্ড দেওয়া হয়নিরিকোয়েস্ট বডিতে একটি প্রয়োজনীয় ফিল্ড নেই। প্লেয়ার রিকোয়ারমেন্টের ক্ষেত্রে details.field-এ বাদ পড়া ফিল্ডের নাম থাকে। | আগে এন্ডপয়েন্টের ডকুমেন্টেশন দেখে নিন। প্লেয়ারের তথ্য কোন কোন ফিল্ডে লাগবে, তা জানতে GET /catalog/category/:categoryId/requirements কল করুন। তারপর আবশ্যক প্রতিটি ফিল্ড রিকোয়েস্টে পাঠান, একটিও বাদ দেবেন না।action: FIX_INPUT · retryable: false · retry_after: null |
| INVALID_UUID_FORMAT | 400 | UUID ফরম্যাট সঠিক নয়অর্ডারের জন্য আপনার পাঠানো order_id সঠিক UUID ফরম্যাটে নেই। | UUID-এর যেকোনো বৈধ ভার্সন ব্যবহার করতে পারেন, যেমন v1, v4, v7 ইত্যাদি। আইডিটি অবশ্যই এই ফরম্যাটে হতে হবে: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx। এখানে প্রতিটি x মানে একটি হেক্সাডেসিমাল অক্ষর।action: FIX_INPUT · retryable: false · retry_after: null |
| PLAYER_NOT_FOUND | 400 | প্লেয়ার পাওয়া যায়নিগেম থেকে নিশ্চিত হওয়া গেছে যে এই Player ID-র কোনো অস্তিত্ব নেই। শুধু নিশ্চিত উত্তর পেলেই এই কোড আসে। ID যাচাই করা না গেলে এর বদলে PLAYER_CHECK_UNAVAILABLE পাবেন। | Player ID সঠিক আছে কিনা যাচাই করুন। এই গেমে zone_id লাগে কিনা সেটাও দেখে নিন।action: FIX_INPUT · retryable: false · retry_after: null |
| PLAYER_CHECK_UNAVAILABLE | 503 | এই গেমের Player ID এখন যাচাই করা যাচ্ছে নাএই মুহূর্তে এই গেমের Player ID যাচাই করা যাচ্ছে না। Player ID ভুল বলে শনাক্ত করা হয়নি। | একই রিকোয়েস্ট retry_after সেকেন্ড পর আবার পাঠান। আপনার কাস্টমারকে ID ভুল বলে জানাবেন না।action: RETRY_LATER · retryable: true · retry_after: 60 |
| REGION_MISMATCH | 400 | এই প্লেয়ারের রিজিয়নে প্রোডাক্টটি ডেলিভারি করা যাবে নাপ্লেয়ারের রিজিয়নে এই প্রোডাক্ট ডেলিভারি করা যায় না। এররে player_region ও player_name দেওয়া থাকে। | প্লেয়ারের রিজিয়ন অনুযায়ী প্রোডাক্ট বেছে নিন।action: CHOOSE_OTHER · retryable: false · retry_after: null |
| INSUFFICIENT_BALANCE | 400 | ওয়ালেটে যথেষ্ট ব্যালেন্স নেইএই অর্ডারটি সম্পন্ন করার মতো ব্যালেন্স আপনার ওয়ালেটে নেই। | অর্ডার দেওয়ার আগে রিলোড পেজ থেকে ওয়ালেট টপ আপ করুন।action: TOP_UP · retryable: false · retry_after: null |
| OUT_OF_STOCK | 400 | প্রোডাক্ট স্টকে নেইআপনি যে প্রোডাক্টটি চেয়েছেন, সেটি এখন স্টকে নেই। | পরে আবার চেষ্টা করুন অথবা অন্য একটি প্রোডাক্ট বেছে নিন।action: RETRY_LATER · retryable: true · retry_after: null |
| PRODUCT_UNAVAILABLE | 409 | এই প্রোডাক্ট সাময়িকভাবে পাওয়া যাচ্ছে নাপ্রোডাক্টটি আছে, তবে এই মুহূর্তে বিক্রি করা যাচ্ছে না। | পরে আবার চেষ্টা করুন, অথবা অন্য কোনো প্রোডাক্ট বেছে নিন।action: RETRY_LATER · retryable: true · retry_after: null |
| DUPLICATE_ORDER | 409 | ডুপ্লিকেট অর্ডার IDএই UUID-তে একটি অর্ডার আগে থেকেই আছে। এটি idempotency সুরক্ষার অংশ। | নতুন অর্ডারের জন্য নতুন UUID তৈরি করুন। রিট্রাই করার সময় এই এরর পেলে বুঝবেন প্রথম চেষ্টাতেই অর্ডার হয়ে গেছে: আরেকটি অর্ডার না বানিয়ে GET /orders/{orderId} দিয়ে সেই অর্ডারটি দেখে নিন।action: CHECK_ORDER · retryable: false · retry_after: null |
| PRICE_INCREASED | 400 | দাম প্রত্যাশার চেয়ে বেড়ে গেছেরিকোয়েস্টে আপনি expected_unit_price হিসেবে যে দাম দিয়েছিলেন, প্রতি ইউনিটের বর্তমান দাম তার চেয়ে বেশি। | সর্বশেষ দাম জানতে GET /catalog/subcategory/:id/price এন্ডপয়েন্টে রিকোয়েস্ট পাঠান, যেখানে :id হলো যে সাবক্যাটাগরির দাম দরকার তার আইডি। তারপর নতুন দাম অনুযায়ী আপনার রিকোয়েস্টের expected_unit_price মানটি আপডেট করুন।action: FIX_INPUT · retryable: false · retry_after: null |
| RATE_LIMIT_EXCEEDED | 429 | রেট লিমিট পেরিয়ে গেছেআপনি এই এন্ডপয়েন্টের জন্য নির্ধারিত রিকোয়েস্ট লিমিট ছাড়িয়ে গেছেন। যেসব লিমিট প্রতি মিনিট ধরে হিসাব হয়, অর্থাৎ ক্যাটালগ, অর্ডার ও অ্যাকাউন্ট বাকেট, সেগুলো প্রতিটি রেসপন্সের সাথেই X-RateLimit-Limit, X-RateLimit-Remaining ও X-RateLimit-Reset নামের তিনটি হেডার পাঠায়। এর পাশাপাশি রেসপন্সের details অবজেক্টে limit, remaining (এখানে 0), retry_after ও window_seconds ফিল্ডগুলো আরেকবার দেওয়া থাকে। তবে POST /player/validate এন্ডপয়েন্টটি প্রতি মিনিটের বাকেটে গোনা হয় না, এর বদলে এটি আপনার দৈনিক প্লেয়ার চেক কোটা থেকে কাটা হয়। এই এন্ডপয়েন্টটি ওপরের কোনো হেডারই পাঠায় না, আর কোটা শেষ হলে এর 429 রেসপন্সে error অবজেক্টের ভেতরেই limit, current, remaining, reset_at ও reset_in_seconds ফিল্ডগুলো পাওয়া যায়। রেট লিমিট যেভাবেই হিসাব হোক না কেন, আবার চেষ্টা করার আগে কত সেকেন্ড অপেক্ষা করতে হবে তা error.retry_after ফিল্ড এবং রেসপন্সের Retry-After হেডার, দুই জায়গা থেকেই জানতে পারবেন। | আবার চেষ্টা করার আগে retry_after বা Retry-After হেডারে যত সেকেন্ড দেওয়া আছে, ততক্ষণ অপেক্ষা করুন। প্রতি মিনিটের এন্ডপয়েন্টগুলোতে X-RateLimit-Remaining খেয়াল রাখুন, যাতে লিমিটে একেবারেই না পৌঁছান।action: RETRY_LATER · retryable: true · retry_after: সেকেন্ড পর রিসেট |
| INTERNAL_ERROR | 500 | ইন্টারনাল সার্ভার এররসার্ভারে একটি অপ্রত্যাশিত সমস্যা হয়েছে। | retry_after সেকেন্ড পর রিকোয়েস্টটি আবার পাঠান। POST /orders/create-এর ক্ষেত্রে একই order_id আবার ব্যবহার করুন, তাহলে অর্ডার কখনো দুবার তৈরি হবে না। সমস্যা থেকে গেলে সাপোর্টে যোগাযোগ করুন।action: RETRY_LATER · retryable: true · retry_after: 30 |
ইন্টিগ্রেশন নিয়ে প্রশ্ন ও উত্তর
ইন্টিগ্রেশনের সময় ডেভেলপাররা আসলে যে প্রশ্নগুলো করেন: অথেন্টিকেশন, রিট্রাই, idempotency, থ্রটলিং, কলব্যাক আর ব্যর্থতা সামলানো।
রিসেলার API রিকোয়েস্ট অথেন্টিকেট করে কীভাবে?
একটিমাত্র HTTP হেডার দিয়ে: Authorization: Bearer <keyId>.<secret>। এই key pair পাবেন আপনার রিসেলার প্যানেলের API Access পেজে। Secret শুধু তৈরির সময় একবারই দেখানো হয়, আর সার্ভার প্রতিটি কলে HMAC চেক দিয়ে সেটি যাচাই করে। কোনো OAuth রিডাইরেক্ট, সেশন কুকি বা রিফ্রেশ করার মতো টোকেন নেই। তাই প্রথম রিকোয়েস্ট থেকে শুরু করে পরের সব রিকোয়েস্টে একই হেডার কাজ করে।
আমার ক্লায়েন্ট কোন base URL আর API ভার্সনে কাজ করবে?
Base URL একটাই, আর সেটি লাইভ। আলাদা কোনো স্যান্ডবক্স হোস্ট নেই যেখানে বানিয়ে পরে বদলাতে হবে। ক্লায়েন্টকে ওপরের v1 base URL-এ সেট করুন, প্রতিটি রিকোয়েস্টে Authorization: Bearer <keyId>.<secret> পাঠান, আর সবার আগে GET /account দিয়ে শুরু করুন, যাতে key আর হেডারের ফরম্যাট ঠিক আছে কি না নিশ্চিত হওয়া যায়। টাকা কাটে শুধু POST /orders/create কলে, তাই এটি সবার শেষে রাখুন, আপনার সবচেয়ে ছোট ডিনমিনেশন দিয়ে চালান, আর ক্লায়েন্ট পুরোপুরি পরীক্ষা না হওয়া পর্যন্ত সঙ্গে expected_unit_price পাঠান।
POST /orders/create রিকোয়েস্ট পাঠানোর সময় আইডেমপোটেন্সি কী-টি কোন ফিল্ডে দিতে হয়?
order_id হলো একটি UUID, যা আপনি ক্লায়েন্ট-সাইডে তৈরি করে create বডিতে পাঠাবেন। আগে থেকে আছে এমন order_id দিয়ে create আবার পাঠালে API দ্বিতীয় অর্ডার খোলে না, বরং HTTP 409 ও DUPLICATE_ORDER ফেরত দেয়। ফলে retry কখনো দ্বিতীয় চার্জ হয়ে যায় না। মূল অর্ডারটি GET /orders/:orderId দিয়ে পড়ে নিন। প্রথম চেষ্টার আগেই UUID তৈরি করে সংরক্ষণ করুন, কারণ timeout-এর পরে তৈরি UUID একটি নতুন key, retry key নয়। একই অর্ডারের জন্য দুটি আলাদা order_id হলে সার্ভারের কোনো কিছুই সেগুলোকে এক করে না।
আমার রিকোয়েস্ট timeout হয়ে গেছে। অর্ডার কি তৈরি হয়েছে?
অনুমান না করে স্ট্যাটাস পড়ে নিশ্চিত হোন। যে UUID পাঠিয়েছিলেন, সেটি দিয়েই GET /orders/:orderId কল করুন। অর্ডার ফেরত এলে বুঝবেন, অর্ডার গ্রহণের পরে timeout হয়েছে। তখন আর আবার পাঠাবেন না। ORDER_NOT_FOUND পেলে বুঝবেন কোনো চার্জ হয়নি, আর একই UUID দিয়ে create রিকোয়েস্ট আবার পাঠানো নিরাপদ।
ওয়েবহুক কলব্যাকের সিগনেচার কীভাবে যাচাই করব?
রিকোয়েস্টের বডি ঠিক যেভাবে এসেছে, সেই কাঁচা বাইটের ওপর HMAC নতুন করে হিসাব করুন এবং ফলাফল X-Shop2Topup-Signature হেডারের সঙ্গে মিলিয়ে দেখুন। হেডারে মানটি থাকে sha256=<hex> আকারে: যে বাইটগুলো পেয়েছেন, হুবহু সেগুলোর ওপর আপনার ওয়েবহুক সিক্রেট দিয়ে HMAC-SHA256 চালান, সামনে sha256= জুড়ে দিন, তারপর টাইমিং-সেফ পদ্ধতিতে তুলনা করুন। JSON পার্স করে আবার স্ট্রিং বানানো অবজেক্টের সঙ্গে কখনো তুলনা করবেন না, কারণ তাতে বাইট বদলে যায় আর মিল কোনোদিনই পাওয়া যাবে না। না মিললে 401 দিয়ে বাতিল করুন। সঠিক ডেলিভারি এলে 10 সেকেন্ডের মধ্যে 200 দিন, আর সেটেলমেন্টের কাজ পরে নিজের কিউতে সারুন।
রেট লিমিটে পৌঁছালে কী হয়?
আপনি HTTP 429 পাবেন, সঙ্গে থাকবে RATE_LIMIT_EXCEEDED কোড আর একটি Retry-After হেডার। রেসপন্স বডির retry_after ফিল্ডে একই সংখ্যাটি আবার দেওয়া থাকে, পাশাপাশি থাকে limit, remaining ও window_seconds। ঠিক ততগুলো সেকেন্ড অপেক্ষা করুন, তারপর একবার আবার চেষ্টা করুন। সঙ্গে সঙ্গে রিট্রাই করবেন না, আর একই কল একাধিক API কী-তে ভাগ করে পাঠাবেন না, কারণ কাউন্টারটি কানেকশন ধরে নয়, পুরো অ্যাকাউন্ট ধরে গোনা হয়।
প্লেয়ার যাচাইয়ের এন্ডপয়েন্ট POST /player/validate-এর রেসপন্সে কোন কোন ফিল্ড থাকে?
রেসপন্সের data অবজেক্টের ভেতরে player_name থাকে। যেসব গেম zone_id ও region দেয়, সেখানে এগুলোও থাকে। অর্ডার তৈরির সময় সিস্টেম নিজেই প্লেয়ার খুঁজে রিজিয়ন যাচাই করে, আর ভিন্ন রিজিয়নের চেষ্টা ওয়ালেট ডেবিট হওয়ার আগেই REGION_MISMATCH (HTTP 400) দিয়ে বাতিল হয়। তাই আপনার ব্যালেন্সের সুরক্ষা এই কলের ওপর নির্ভর করে না। কলটি দরকার অন্য কারণে: player_name পড়ে দেখানোই ক্রেতাকে বোঝানোর একমাত্র কার্যকর উপায় যে তিনি কোন অ্যাকাউন্টে টপ আপ করতে যাচ্ছেন। কনফার্ম করার আগেই ভুল টাইপ করা আইডিও এতে ধরা পড়ে। নামটি সবসময় গেম থেকে আসে, ক্রেতার টাইপ করা কোনো কিছু থেকে নয়।
requirements রেসপন্সের ফিল্ডগুলো অর্ডারের পেলোডে কীভাবে বসাব?
প্রতিটি এন্ট্রির field_name-ই হবে প্রপার্টির নাম। GET /catalog/category/:categoryId/requirements যতগুলো এন্ট্রি ফেরত দেয়, তার প্রতিটি POST /orders/create-এ পাঠানো requirements অবজেক্টের একটি করে প্রপার্টি হয়। এন্ট্রিগুলোতে data_type-ও থাকে, ফলে আপনার ফর্ম জানে কী দেখাতে হবে আর সার্ভার জানে মানটিকে কোন টাইপে কাস্ট করতে হবে। single_select ও multi_select এন্ট্রির সঙ্গে অনুমোদিত মানগুলোও আসে, তাই সেখানে খোলা টেক্সট বক্স না রেখে ওই মানগুলো থেকে বেছে নিতে দিন। তালিকাটি প্রতিটি অর্ডারে নতুন করে না পড়ে ক্যাটাগরি অনুযায়ী ক্যাশ করে রাখুন। কোনো অর্ডার তৈরির কল MISSING_REQUIRED_FIELD নিয়ে ফিরে এলে তালিকাটি আবার পড়ে নিন।
PRICE_INCREASED রেসপন্স এলে, অর্থাৎ দাম বেড়ে গেলে, আমার ক্লায়েন্ট কী করবে?
আগে দাম আবার দেখুন, সিদ্ধান্ত নিন, তারপর নতুন করে পাঠান। একই বডি হুবহু আবার পাঠাবেন না। PRICE_INCREASED (HTTP 400) মানে লাইভ ইউনিট দাম আপনার পাঠানো expected_unit_price-এর চেয়ে বেশি, তাই অর্ডার তৈরি হয়নি এবং কিছুই চার্জ করা হয়নি। এররের বিস্তারিত অংশে expected_unit_price ও current_unit_price দুটোই থাকে, আর দাম না দেখে পুরোনো সংখ্যা দিয়েই রিট্রাই করলে প্রতিবারই তা ফিরিয়ে দেওয়া হবে। GET /catalog/subcategory/:itemId/price থেকে দাম পড়ুন, নতুন দামে আপনার এখনো পোষায় কি না ঠিক করুন, তারপর আপডেট করা expected_unit_price আর একই order_id দিয়ে অর্ডার তৈরির রিকোয়েস্ট আবার পাঠান। বাতিল হওয়া চেষ্টায় order_id খরচ হয় না। expected_unit_price বাদ দিলে এই সুরক্ষা পুরোপুরি বন্ধ হয়ে যায়, আর অটোমেটেড ইন্টিগ্রেশনে সেটা প্রায় কখনোই কাম্য নয়।
আংশিক ডেলিভারি হওয়া অর্ডার রেসপন্সে কীভাবে দেখায়?
অর্ডারের status হয় partial, আর কোন ভাগে কতটি ইউনিট গেল তা sub_transaction_summary-তে আলাদা করে দেওয়া থাকে। এই অবজেক্ট ইউনিটগুলোকে তিন ভাগে গোনে: completed (সম্পন্ন), refunded (রিফান্ড হয়েছে) আর pending (পেন্ডিং), এবং এই তিনটির যোগফল সবসময় total-এর সমান। sub_transactions-এ প্রতিটি ইউনিট তার নিজের স্ট্যাটাসসহ আলাদা লেনদেন হিসেবে তালিকায় থাকে। যে ইউনিট এখনো ডেলিভারি বা রিফান্ড কোনোটাই হয়নি, সেটি পেন্ডিং থাকে। pending চূড়ান্ত অবস্থা নয়, তাই অপেক্ষা চালিয়ে যান; completed, partial ও refunded চূড়ান্ত অবস্থা। failed (ব্যর্থ), processing ও retrying কাউন্টার শুধু কম্প্যাটিবিলিটির জন্য রাখা হয়েছে, এগুলোর মান সবসময় 0। order.completed ও order.refunded কলব্যাকেও একই সারসংক্ষেপ থাকে, তাই যাচাই করা পেলোড দিয়েই দ্বিতীয়বার না পড়ে সেটেল করা যায়।
allowlist-এর বাইরের কোনো অ্যাড্রেস থেকে রিকোয়েস্ট এলে কী ফেরত যায়?
উত্তর আসে HTTP 403, সঙ্গে IP_NOT_ALLOWED কোড, আর এটি কোনো বিজনেস লজিক চলার আগেই ফেরত যায়। API কী-তে IP অ্যালাউলিস্ট খালি না থাকলেই এই যাচাই চলে, ফলে কী-টি ফাঁস হয়ে গেলেও আপনার নিজের আউটগোয়িং ঠিকানাগুলো ছাড়া অন্য কোথাও সেটি কোনো কাজে আসে না। মেসেজের লেখা দেখে নয়, error.code দেখে লজিক সাজান। প্রোডাকশনে হঠাৎ IP_NOT_ALLOWED এলে ধরে নিন কী-টি নষ্ট হয়নি, বরং আউটগোয়িং ঠিকানা বদলে গেছে, যেমন নতুন NAT গেটওয়ে বা ওয়ার্কার সাবনেট। আউটগোয়িং ঠিকানা সত্যিই স্থির না থাকলে তবেই অ্যালাউলিস্ট খালি রাখুন।
একটি অর্ডার সম্পন্ন হতে কত সময় লাগে?
অর্ডার সম্পন্ন হওয়ার কাজ আলাদা প্রক্রিয়ায় হয়, তাই তাৎক্ষণিকভাবে শেষ হবে ধরে নেবেন না। অর্ডার গ্রহণ করে ওয়ালেট থেকে টাকা কেটে নেওয়ার পরই POST /orders/create সাড়া দেয়। তখন অর্ডারের অবস্থা সাধারণত পেন্ডিং থাকে। এরপর আলাদা ওয়ার্কার অর্ডার পূরণের কাজ চালায়। চূড়ান্ত অবস্থা webhook-এর মাধ্যমে জানানো হয়, অথবা পরেরবার অর্ডারের অবস্থা যাচাই করলে আপনি তা পাবেন। ক্রেতার HTTP রিকোয়েস্ট চূড়ান্ত অবস্থার অপেক্ষায় আটকে রাখবেন না। রিকোয়েস্ট গ্রহণ করে ক্রেতাকে উত্তর দিন, আর অর্ডার সম্পন্ন করার বাকি কাজ আলাদা প্রক্রিয়ায় চালান।
অ্যাকাউন্ট খুলুন, API key তৈরি করুন, লাইভে যান
সাইন আপ থেকে API কল করা পর্যন্ত কোনো অনুমোদনের লাইন বা অপেক্ষার সময় নেই। রিসেলার অ্যাকাউন্ট খুলুন, API Access পেজে একটি key তৈরি করুন, ওয়ালেটে ব্যালেন্স যোগ করুন, আর সেদিনই আপনার প্রথম অর্ডার পাঠাতে পারবেন।
এই প্রজেক্টের ডেভেলপার আপনি নন? ব্যবসায়িক দিকের বিস্তারিত পাবেন SHOP2TOPUP রিসেলার প্রোগ্রাম পেজে.