مستندات API فروش اشتراک آپموی
نسخهٔ ۱ · آدرس پایه: https://api.upmovi.com/v1 (آدرس معادل: https://api.upmovi.ir/v1)
این API برای سایتهای همکار (مثل سایت شارژ) ساخته شده تا بتوانند اشتراک سایت فیلم را بفروشند و کاربر را روی سایت فیلم ثبتنام کنند. پرداخت کاربر در سایت شما انجام میشود؛ بعد از پرداخت موفق، یک درخواست به این API میفرستید تا اشتراک روی شمارهٔ موبایل او فعال شود.
جریان پیشنهادی
- کاربر در سایت شما وارد پنل میشود و صفحهٔ پلنها را باز میکند.
- سایت شما
GET /plansرا صدا میزند و پلنها و قیمتهای روز را نشان میدهد (دکمهٔ «بهروزرسانی» هم همین را دوباره میخواند). - کاربر پلن را انتخاب میکند، یک
operation_key(UUID) میسازید و در دیتابیس خودتان ذخیره میکنید، و پرداخت انجام میشود. - اگر پرداخت موفق بود:
POST /ordersبا همانoperation_key. - در پاسخ، تاریخ پایان اشتراک و (اگر حساب تازه ساخته شده) رمز عبور برمیگردد. رمز فقط همین یکبار نمایش داده میشود؛ آن را به کاربر نشان دهید یا پیامک کنید.
- اگر درخواست قطع شد یا پاسخی نگرفتید، همان درخواست را با همان
operation_keyدوباره بفرستید (امن است) یاGET /orders/{operation_key}را بخوانید.
احراز هویت
همهٔ درخواستها باید این هدر را داشته باشند:
Authorization: Bearer <API_KEY>
Accept: application/json
- کلید را مدیر سایت فیلم (آپموی) برای شما میسازد و فقط یکبار به شما نشان داده میشود (روی سرور فقط هش آن ذخیره میشود).
- کلید را فقط در سمت سرور (بکاند) سایت خود نگه دارید؛ هرگز در جاوااسکریپت صفحه یا اپلیکیشن قرار ندهید.
- میتوان کلید را به IPهای مشخص محدود کرد (لیست IP مجاز). درخواست از IP دیگر با خطای
403 ip_not_allowedرد میشود. - اگر کلید لو رفت، از مدیر سایت فیلم کلید جدید بخواهید؛ کلید قبلی همان لحظه از کار میافتد.
قواعد کلی
| مورد | توضیح |
|---|---|
| قالب | ورودی و خروجی JSON؛ برای POST هدر Content-Type: application/json بفرستید |
| پول | تومان، عدد صحیح (IRT) |
| زمان | UTC به قالب 2026-12-15T23:59:59Z |
| موبایل | 09123456789؛ ارقام فارسی، +98... و 98... هم پذیرفته و نرمال میشود |
| محدودیت | ۱۲۰ درخواست در دقیقه برای هر کلید؛ ۲۰ تلاش ناموفق در دقیقه برای هر IP |
| کش | پاسخها no-store هستند |
پاسخ موفق: {"ok": true, "data": {...}} — پاسخ خطا: {"ok": false, "error": {"code": "...", "message": "..."}}
۱) فهرست پلنها — GET /plans
قیمتها همیشه از خود سایت فیلم خوانده میشوند (با تخفیف فعال). پیشنهاد: هنگام نمایش به کاربر دوباره بخوانید یا حداکثر چند دقیقه کش کنید.
{
"ok": true,
"data": {
"currency": "IRT", "unit": "toman",
"version": "a1b2c3d4e5f60789",
"generated_at": "2026-11-15T06:00:00Z",
"plans": [
{
"id": 10, "name": "1-Month Subscription", "name_fa": "اشتراک 1 ماهه",
"duration_days": 30,
"original_price": 272000, "final_price": 190000, "discount_percent": 30,
"discount_until": null, "is_popular": false, "badge": null, "features": null
}
]
}
}
final_priceمبلغی است که باید از کاربر بگیرید.original_priceقیمت خطخورده است.versionفقط وقتی قیمت یا پلنی تغییر کند عوض میشود؛ برای تشخیص تغییر مناسب است.
۲) بررسی حساب — GET /accounts?mobile=09...
{ "ok": true, "data": { "exists": true, "mobile": "09123456789", "user_id": 123,
"status": "active", "has_password": true, "active_until": "2026-12-15T23:59:59Z" } }
اگر حساب نباشد: {"exists": false, "mobile": "..."}. برای نمایش پیام «اشتراک فعلی شما تا ... است» یا «حساب جدید ساخته میشود» مفید است.
۳) ثبت سفارش پرداختشده — POST /orders
فقط بعد از پرداخت موفق صدا بزنید.
| فیلد | الزامی | توضیح |
|---|---|---|
operation_key |
بله | UUID یکتا برای هر خرید (همان را در تکرار هم بفرستید) |
mobile |
بله | موبایل خریدار |
plan_id |
بله | شناسهٔ پلن از /plans |
amount |
بله | تومانی که کاربر پرداخته؛ نباید کمتر از final_price فعلی پلن باشد (وگرنه 409 amount_too_low) |
reference |
خیر | کد پیگیری پرداخت در سایت شما (برای گزارش) |
password |
خیر | فقط برای حساب تازه: اگر بفرستید همان رمز ثبت میشود، وگرنه رمز تصادفی ۱۰ حرفی ساخته و برگردانده میشود |
{
"operation_key": "5f7480c7-1be9-4f13-9f6a-69633840dab5",
"mobile": "09123456789", "plan_id": 10, "amount": 190000, "reference": "TRK-889123"
}
پاسخ موفق (201 بار اول):
{ "ok": true, "data": {
"operation_key": "5f7480c7-1be9-4f13-9f6a-69633840dab5",
"already_applied": false, "order_id": 3000000257,
"user": { "id": 3000000555, "mobile": "09123456789", "created": true, "has_password": true },
"subscription": { "plan_id": 10, "end_date": "2026-12-15T23:59:59Z" },
"password": "k7Hq2mXpWc"
} }
user.created = trueیعنی حساب تازه ساخته شد و فیلدpasswordفقط همینجا هست.- اگر موبایل از قبل حساب داشته باشد، اشتراک به همان حساب اضافه/تمدید میشود، رمز تغییر نمیکند و
passwordبرنمیگردد (کاربر با رمز قبلی یا «فراموشی رمز» وارد شود). - تکرار با همان
operation_keyو همان مشخصات:200باalready_applied: true(اشتراک دوباره داده نمیشود، رمز برنمیگردد). - همان
operation_keyبا موبایل/پلن دیگر:409 order_rejected. - حساب غیرفعال/مسدود:
403 account_not_active.
۴) وضعیت سفارش — GET /orders/{operation_key}
{ "ok": true, "data": { "operation_key": "...", "order_id": 3000000257, "user_id": 3000000555,
"plan_id": 10, "status": "paid", "amount": 190000, "applied": true, "end_date": "2026-12-15T23:59:59Z" } }
اگر سفارش نبود 404 not_found. برای بازبینی بعد از timeout یا خطای ۵۰۰ استفاده کنید.
خطاها
| HTTP | code | معنی |
|---|---|---|
| 401 | unauthorized | کلید نیست یا اشتباه است |
| 403 | ip_not_allowed | IP سرور شما در لیست مجاز نیست |
| 403 | account_not_active | حساب این موبایل فعال نیست |
| 404 | plan_not_found | پلن وجود ندارد یا برای فروش نیست |
| 404 | not_found | سفارشی با این operation_key نیست |
| 409 | amount_too_low | مبلغ کمتر از قیمت فعلی است؛ دوباره /plans را بخوانید |
| 409 | order_rejected | operation_key برای درخواست دیگری استفاده شده یا سفارش قبلی ناقص است |
| 422 | validation_failed / invalid_mobile / invalid_operation_key | ورودی نامعتبر |
| 429 | rate_limited / too_many_requests | درخواست زیاد؛ کمی صبر کنید |
| 500 | server_error | خطای سرور؛ با همان operation_key دوباره بفرستید یا وضعیت را بخوانید |
نکات مهم برای پیادهسازی
- Idempotency:
operation_keyرا قبل از پرداخت بسازید و ذخیره کنید؛ در هر تکرار همان را بفرستید. هیچوقت برای هر تلاش یک UUID جدید نسازید، وگرنه ممکن است اشتراک دوبار ثبت شود. - اگر
POST /ordersبعد از پرداخت موفق نشد (timeout/۵۰۰)، سفارش را «در انتظار تأیید» نگه دارید و با تلاش مجدد تکمیل کنید؛ پول کاربر نباید بیاشتراک بماند. - رمز تولیدشده را در لاگ ننویسید و بعد از نمایش/پیامک، دور بریزید.
- شمارهٔ موبایل خریدار همان شمارهٔ ورود او در سایت فیلم است.
نمونه کد
curl
curl -s https://api.upmovi.com/v1/plans \
-H "Authorization: Bearer $API_KEY" -H "Accept: application/json"
curl -s -X POST https://api.upmovi.com/v1/orders \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{"operation_key":"5f7480c7-1be9-4f13-9f6a-69633840dab5","mobile":"09123456789","plan_id":10,"amount":190000,"reference":"TRK-889123"}'
PHP
function upmoviApi(string $method, string $path, ?array $body = null): array
{
$ch = curl_init('https://api.upmovi.com/v1' . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 25,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('UPMOVI_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $body === null ? null : json_encode($body),
]);
$raw = curl_exec($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ['status' => $code, 'json' => json_decode((string) $raw, true)];
}
$plans = upmoviApi('GET', '/plans');
$order = upmoviApi('POST', '/orders', [
'operation_key' => $savedUuid, 'mobile' => $mobile,
'plan_id' => 10, 'amount' => 190000, 'reference' => $trackingCode,
]);
if (in_array($order['status'], [200, 201], true)) {
$password = $order['json']['data']['password'] ?? null; // فقط برای حساب تازه
}
چکلیست راهاندازی
- کلید ساخته شد و فقط در سرور نگهداری میشود
- IP سرور شما در «لیست IP مجاز» کلید ثبت شد
-
operation_keyقبل از پرداخت ساخته و ذخیره میشود - مبلغ ارسالی
amountهمانfinal_priceلحظهٔ نمایش (یا بیشتر) است - رمز بازگشتی فقط یکبار به کاربر نشان داده میشود
- تکرار خودکار برای خطای ۵۰۰/timeout با همان
operation_keyپیاده شد