مستندات API همکاری
این API امکان مشاهدهی محصولات با قیمت همکاری، ثبت سفارش از محل کیف پول و دریافت خودکار سفارش را فراهم میکند.
معرفی
https://api.cubepass.ir/v1application/json; UTF-8ISO 8601, UTCپیش از شروع، در تنظیمات API نشانی IP سرور، توکن و نشانی وبهوک را ثبت کنید. سپس اتصال را از همان سرور بررسی کنید.
curl https://api.cubepass.ir/v1/ping \
-H "Authorization: Bearer YOUR_TOKEN"$ch = curl_init('https://api.cubepass.ir/v1/ping');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_TOKEN'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);import requests
response = requests.get(
"https://api.cubepass.ir/v1/ping",
headers={"Authorization": "Bearer YOUR_TOKEN"},
timeout=30,
)
print(response.json())const response = await fetch('https://api.cubepass.ir/v1/ping', {
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_TOKEN',
},
});
console.log(await response.json());package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.cubepass.ir/v1/ping", nil)
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(string(data))
}
{
"success": true,
"data": {
"partner_id": 12,
"your_ip": "203.0.113.10",
"discount_percent": 10.0,
"webhook_set": true
}
}
احراز هویت
هر درخواست باید هر دو شرط زیر را داشته باشد.
| توکن | در هدر Authorization: Bearer YOUR_TOKEN ارسال شود. توکن تنها در زمان ساخت نمایش داده میشود و باید در سرور نگهداری شود. |
|---|---|
| IP مجاز | درخواست باید از IP ثبتشده در پنل ارسال شود. برای هر حساب یک IP ثبت میشود و بازهی IP پذیرفته نمیشود. |
این API تنها برای ارتباط سرور با سرور طراحی شده است و از مرورگر قابل فراخوانی نیست.
محدودیت درخواست
| گروه | حداکثر در دقیقه |
|---|---|
| خواندن (محصولات، کیف پول، جزئیات سفارش) | ۱۲۰ |
| سفارش (ثبت، فهرست، لغو) | ۳۰ |
| رزرو موجودی | ۲۰ |
وضعیت مصرف در هدرهای X-RateLimit-Limit، X-RateLimit-Remaining و X-RateLimit-Reset بازگردانده میشود. در پاسخ 429، مدت انتظار در هدر Retry-After قرار دارد.
خطاها
در پاسخ ناموفق، مقدار code ثابت است و برای پردازش در برنامه مناسب است. مقدار message تنها برای نمایش است.
{
"success": false,
"error": {
"code": "insufficient_balance",
"message": "موجودی کیف پول برای این سفارش کافی نیست.",
"required": 450000,
"balance": 120000
}
}
| HTTP | code | شرح |
|---|---|---|
| 400 | bad_json | بدنهی درخواست JSON معتبر نیست. |
| 401 | unauthorized | توکن ارسال نشده یا معتبر نیست. |
| 402 | insufficient_balance | موجودی کافی نیست؛ مبلغی کسر نشده است. |
| 403 | ip_required، ip_not_allowed | IP ثبت نشده یا مجاز نیست. |
| 403 | webhook_required | نشانی وبهوک ثبت نشده است. |
| 403 | partner_inactive | حساب همکاری فعال نیست. |
| 404 | product_not_found، order_not_found، hold_not_found | منبع یافت نشد. |
| 409 | product_unavailable | محصول قابل خرید نیست. |
| 409 | price_changed | قیمت با expected_total مطابقت ندارد. |
| 409 | cannot_cancel | سفارش قابل لغو نیست. |
| 410 | hold_expired | مهلت رزرو به پایان رسیده است. |
| 422 | bad_item، no_items، variant_required، inputs_required، client_order_id_required | مقادیر درخواست معتبر نیست. |
| 429 | rate_limited، hold_limit | از سقف مجاز عبور کردهاید. |
| 503 | unavailable | سرویس موقتاً در دسترس نیست. |
کیف پول
/walletموجودیcurl https://api.cubepass.ir/v1/wallet \
-H "Authorization: Bearer YOUR_TOKEN"$ch = curl_init('https://api.cubepass.ir/v1/wallet');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_TOKEN'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);import requests
response = requests.get(
"https://api.cubepass.ir/v1/wallet",
headers={"Authorization": "Bearer YOUR_TOKEN"},
timeout=30,
)
print(response.json())const response = await fetch('https://api.cubepass.ir/v1/wallet', {
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_TOKEN',
},
});
console.log(await response.json());package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.cubepass.ir/v1/wallet", nil)
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(string(data))
}
{
"success": true,
"data": { "balance": 1250000, "currency": "IRT" }
}
/transactions?page=1&limit=50&kind=orderگزارش تراکنشهامقادیر kind: topup، order، refund، admin_add، admin_sub
{
"success": true,
"data": {
"page": 1,
"transactions": [
{
"id": 81,
"amount": -450000,
"balance_after": 800000,
"kind": "order",
"reason": "خرید Game Pass Ultimate 1 ماهه × 1 — سفارش CP-9F21A07C3D (ORD-1042)",
"order_id": "CP-9F21A07C3D",
"created_at": "2026-11-01T10:12:05+00:00"
}
]
}
}
محصولات
/products?page=1&limit=100فهرست محصولات/products/{id}جزئیات محصولcurl https://api.cubepass.ir/v1/products?page=1&limit=100 \
-H "Authorization: Bearer YOUR_TOKEN"$ch = curl_init('https://api.cubepass.ir/v1/products?page=1&limit=100');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_TOKEN'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);import requests
response = requests.get(
"https://api.cubepass.ir/v1/products?page=1&limit=100",
headers={"Authorization": "Bearer YOUR_TOKEN"},
timeout=30,
)
print(response.json())const response = await fetch('https://api.cubepass.ir/v1/products?page=1&limit=100', {
method: 'GET',
headers: {
Authorization: 'Bearer YOUR_TOKEN',
},
});
console.log(await response.json());package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.cubepass.ir/v1/products?page=1&limit=100", nil)
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(string(data))
}
محصولی که قابل خرید نیست تنها با available: false و علت آن در فهرست قرار میگیرد.
{
"success": true,
"data": {
"page": 1,
"total": 2,
"products": [
{
"id": 17,
"name": "Game Pass Ultimate",
"available": true,
"category": "gamepass",
"brand": "Xbox",
"variants": [
{
"id": 41,
"name": "1 ماهه",
"available": true,
"price": 450000,
"site_price": 500000,
"delivery": "account",
"in_stock": true,
"required_fields": []
}
]
},
{
"id": 23,
"name": "PS Plus",
"available": false,
"unavailable_reason": "out_of_stock"
}
]
}
}
| فیلد | شرح |
|---|---|
available | قابل خرید بودن |
unavailable_reason | hidden یا out_of_stock |
price | قیمت شما پس از اعمال درصد همکاری |
site_price | قیمت فروش در سایت |
in_stock | true: تحویل فوری. false: قابل خرید است و پس از تکمیل از طریق وبهوک تحویل میشود. |
delivery | account، link، code یا customer |
required_fields | فیلدهای لازم در inputs برای تحویل customer |
محصول بدون ظرفیت یک variant با id: 0 دارد.
رزرو موجودی
با این درخواست، تعداد موردنیاز به مدت 5 دقیقه برای شما رزرو میشود تا در این مدت خرید را نهایی کنید. پاسخ تنها مشخص میکند که تعداد درخواستی قابل رزرو است یا خیر. پس از پایان مهلت، رزرو خودکار آزاد میشود.
/holdsثبت رزروcurl -X POST https://api.cubepass.ir/v1/holds \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"product_id": 17,
"variant_id": 41,
"quantity": 14
}
]
}'$ch = curl_init('https://api.cubepass.ir/v1/holds');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_TOKEN', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => '{"items": [{"product_id": 17, "variant_id": 41, "quantity": 14}]}',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);import requests
response = requests.post(
"https://api.cubepass.ir/v1/holds",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={"items": [{"product_id": 17, "variant_id": 41, "quantity": 14}]},
timeout=30,
)
print(response.json())const response = await fetch('https://api.cubepass.ir/v1/holds', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({"items": [{"product_id": 17, "variant_id": 41, "quantity": 14}]}),
});
console.log(await response.json());package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"items": [{"product_id": 17, "variant_id": 41, "quantity": 14}]}`)
req, _ := http.NewRequest("POST", "https://api.cubepass.ir/v1/holds", body)
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(string(data))
}
{
"success": true,
"data": {
"available": true,
"hold_id": "hold_8c1f0e2ab45d6f7e90a1b2c3",
"expires_in": 300,
"expires_at": "2026-11-01T10:17:05+00:00",
"total": 6300000,
"wallet_sufficient": true
}
}
{
"success": true,
"data": { "available": false }
}
- قیمت در مدت رزرو ثابت میماند.
- حداکثر 50 واحد بهصورت همزمان قابل رزرو است.
- محصولات با تحویل
customerقابل رزرو نیستند.
/holds/{hold_id}آزادسازی رزروسفارش
/ordersثبت سفارش| فیلد | نوع | شرح | |
|---|---|---|---|
client_order_id | string | الزامی | شناسهی یکتای سفارش در سیستم شما. ارسال دوبارهی همین شناسه، همان سفارش را بازمیگرداند و خرید تکراری انجام نمیشود. |
items | array | الزامی* | حداکثر 20 قلم و هر قلم تا 50 عدد، شامل product_id، variant_id و quantity |
hold_id | string | اختیاری | *بهجای items، برای خرید یک رزرو |
expected_total | integer | اختیاری | اگر مبلغ نهایی با این مقدار برابر نباشد، سفارش ثبت نمیشود. |
dry_run | boolean | اختیاری | با true درخواست بررسی میشود بدون اینکه خریدی انجام شود یا مبلغی کسر گردد. |
curl -X POST https://api.cubepass.ir/v1/orders \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"client_order_id": "ORD-1042",
"items": [
{
"product_id": 17,
"variant_id": 41,
"quantity": 1
}
],
"expected_total": 450000
}'$ch = curl_init('https://api.cubepass.ir/v1/orders');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_TOKEN', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => '{"client_order_id": "ORD-1042", "items": [{"product_id": 17, "variant_id": 41, "quantity": 1}], "expected_total": 450000}',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);import requests
response = requests.post(
"https://api.cubepass.ir/v1/orders",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={"client_order_id": "ORD-1042", "items": [{"product_id": 17, "variant_id": 41, "quantity": 1}], "expected_total": 450000},
timeout=30,
)
print(response.json())const response = await fetch('https://api.cubepass.ir/v1/orders', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({"client_order_id": "ORD-1042", "items": [{"product_id": 17, "variant_id": 41, "quantity": 1}], "expected_total": 450000}),
});
console.log(await response.json());package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"client_order_id": "ORD-1042", "items": [{"product_id": 17, "variant_id": 41, "quantity": 1}], "expected_total": 450000}`)
req, _ := http.NewRequest("POST", "https://api.cubepass.ir/v1/orders", body)
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(string(data))
}
مبلغ کل سفارش هنگام ثبت از کیف پول کسر میشود. سفارش یکی از دو حالت زیر را دارد:
completed | همهی اقلام آماده است و در همین پاسخ تحویل میشود. |
|---|---|
pending | سفارش در حال تکمیل است. پس از تکمیل، همهی اقلام با هم از طریق وبهوک order.completed تحویل میشوند. |
{
"success": true,
"data": {
"order_id": "CP-9F21A07C3D",
"client_order_id": "ORD-1042",
"status": "completed",
"status_label": "تحویل شده",
"total": 450000,
"created_at": "2026-11-01T10:12:05+00:00",
"items": [
{
"product_id": 17,
"variant_id": 41,
"name": "Game Pass Ultimate",
"variant": "1 ماهه",
"quantity": 1,
"unit_price": 450000,
"delivery": "account",
"accounts": [
{ "kind": "account", "login": "user@example.com", "password": "S3cret" }
]
}
]
}
}
{ "kind": "account", "login": "...", "password": "..." }
{ "kind": "link", "link": "https://..." }
{ "kind": "code", "code": "XXXX-XXXX" }
{ "kind": "customer", "note": "..." }
برای محصول با تحویل customer، مشخصات اکانت مشتری را به تعداد quantity ارسال کنید.
{
"product_id": 30,
"variant_id": 0,
"quantity": 1,
"inputs": [{ "email": "customer@example.com", "password": "..." }]
}
/orders?page=1&limit=30فهرست سفارشها/orders/{order_id}جزئیات سفارشاین مسیر با order_id یا client_order_id کار میکند و وضعیت سفارش یکی از pending، completed یا cancelled است.
/orders/{order_id}/cancelلغو سفارشcurl -X POST https://api.cubepass.ir/v1/orders/CP-9F21A07C3D/cancel \
-H "Authorization: Bearer YOUR_TOKEN"$ch = curl_init('https://api.cubepass.ir/v1/orders/CP-9F21A07C3D/cancel');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_TOKEN'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);import requests
response = requests.post(
"https://api.cubepass.ir/v1/orders/CP-9F21A07C3D/cancel",
headers={"Authorization": "Bearer YOUR_TOKEN"},
timeout=30,
)
print(response.json())const response = await fetch('https://api.cubepass.ir/v1/orders/CP-9F21A07C3D/cancel', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_TOKEN',
},
});
console.log(await response.json());package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.cubepass.ir/v1/orders/CP-9F21A07C3D/cancel", nil)
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(string(data))
}
تنها سفارش pending قابل لغو است و مبلغ آن به کیف پول بازمیگردد.
وبهوک
وبهوک برای دریافت سفارشهایی که پس از ثبت تکمیل میشوند و اطلاع از تغییر محصولات الزامی است. نشانی باید https روی پورت ۴۴۳ و دارای IP عمومی باشد.
| رویداد | شرح |
|---|---|
order.pending | سفارش ثبت شد و در حال تکمیل است. |
order.completed | سفارش تکمیل شد؛ اقلام تحویلی در accounts قرار دارد. |
order.cancelled | سفارش لغو و مبلغ به کیف پول بازگردانده شد. |
product.unavailable | محصول یا ظرفیت از فروش خارج شد؛ علت در reason با مقدار out_of_stock یا hidden قرار دارد. |
product.available | محصول یا ظرفیت قابل خرید شد. |
product.price_changed | قیمت تغییر کرد. |
ping | پیام آزمایشی از پنل |
{
"id": "evt_5b1c9e2f7a",
"event": "order.completed",
"created_at": 1793500000,
"data": {
"order_id": "CP-9F21A07C3D",
"client_order_id": "ORD-1042",
"status": "completed",
"items": []
}
}
تغییر محصولات
تغییرات محصولات حداکثر یک تا دو دقیقه پس از رخ دادن ارسال میشود. اقلام هر رویداد در آرایهی items قرار دارد و هر ردیف مربوط به یک ظرفیت است. قیمتها به تومان و price قیمت شما پس از اعمال درصد همکاری است.
{
"id": "evt_8a3d1f6c02",
"event": "product.price_changed",
"created_at": 1793500000,
"data": {
"items": [
{
"product_id": 17,
"variant_id": 41,
"name": "Game Pass Ultimate",
"variant": "1 ماهه",
"old_price": 450000,
"price": 495000,
"old_site_price": 500000,
"site_price": 550000
}
]
}
}
{
"id": "evt_c41e7b9a35",
"event": "product.unavailable",
"created_at": 1793500000,
"data": {
"items": [
{
"product_id": 17,
"variant_id": 41,
"name": "Game Pass Ultimate",
"variant": "1 ماهه",
"reason": "out_of_stock"
}
]
}
}
پیام product.available همین ساختار را با فیلدهای price و site_price دارد. محصولی که in_stock: false شود همچنان قابل خرید است و پیامی برای آن ارسال نمیشود. تغییر درصد همکاری نیز پیام جداگانه ندارد و قیمت جدید از مسیر محصولات دریافت میشود.
بررسی امضا
هر پیام هدرهای X-Cube-Event، X-Cube-Timestamp و X-Cube-Signature را دارد. امضا برابر با HMAC-SHA256 رشتهی timestamp.body با کلید امضای پنل است و باید پیش از پردازش پیام بررسی شود.
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_CUBE_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_CUBE_SIGNATURE'] ?? '';
$good = hash_hmac('sha256', $ts . '.' . $body, getenv('CUBE_WEBHOOK_SECRET'));
if (!hash_equals($good, $sig) || abs(time() - (int) $ts) > 300) {
http_response_code(401);
exit;
}
$event = json_decode($body, true);
http_response_code(200);import hashlib, hmac, time
def is_valid(body: bytes, ts: str, sig: str, secret: str) -> bool:
good = hmac.new(secret.encode(), ts.encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(good, sig) and abs(time.time() - int(ts)) <= 300const crypto = require('crypto');
function isValid(rawBody, ts, sig, secret) {
const good = crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
const same = good.length === sig.length && crypto.timingSafeEqual(Buffer.from(good), Buffer.from(sig));
return same && Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
}func isValid(body []byte, ts, sig, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(ts + "."))
mac.Write(body)
good := hex.EncodeToString(mac.Sum(nil))
sent, err := strconv.ParseInt(ts, 10, 64)
if err != nil || math.Abs(float64(time.Now().Unix()-sent)) > 300 {
return false
}
return hmac.Equal([]byte(good), []byte(sig))
}
پاسخ و تلاش مجدد
سرور شما باید ظرف ۱۰ ثانیه با کد 2xx پاسخ دهد. در غیر این صورت، پیام تا ۲۴ ساعت حداکثر ۸ بار دوباره ارسال میشود. به همین دلیل ممکن است یک پیام بیش از یک بار برسد؛ با ذخیرهی مقدار id از پردازش تکراری جلوگیری کنید. سفارش تحویلشده همیشه از مسیر جزئیات سفارش و صفحهی سفارش در پنل نیز قابل دریافت است.
نمونهی کامل
رزرو ۱۴ عدد و سپس خرید همان رزرو:
import uuid
import requests
API = "https://api.cubepass.ir/v1"
HEADERS = {"Authorization": "Bearer YOUR_TOKEN"}
hold = requests.post(f"{API}/holds", headers=HEADERS, timeout=20, json={
"items": [{"product_id": 17, "variant_id": 41, "quantity": 14}],
}).json()["data"]
if not hold["available"]:
print("Not available")
elif not hold["wallet_sufficient"]:
requests.delete(f"{API}/holds/{hold['hold_id']}", headers=HEADERS, timeout=20)
print("Insufficient balance")
else:
order = requests.post(f"{API}/orders", headers=HEADERS, timeout=30, json={
"client_order_id": str(uuid.uuid4()),
"hold_id": hold["hold_id"],
}).json()["data"]
print(order["status"], order["items"])const API = 'https://api.cubepass.ir/v1';
const headers = { Authorization: 'Bearer YOUR_TOKEN', 'Content-Type': 'application/json' };
const post = async (path, body) =>
(await (await fetch(API + path, { method: 'POST', headers, body: JSON.stringify(body) })).json()).data;
const hold = await post('/holds', { items: [{ product_id: 17, variant_id: 41, quantity: 14 }] });
if (!hold.available) {
console.log('Not available');
} else if (!hold.wallet_sufficient) {
await fetch(`${API}/holds/${hold.hold_id}`, { method: 'DELETE', headers });
console.log('Insufficient balance');
} else {
const order = await post('/orders', { client_order_id: crypto.randomUUID(), hold_id: hold.hold_id });
console.log(order.status, order.items);
}