پرش به مطلب اصلی

راهنمای پرداخت درون‌برنامه‌ای در Web Application

پروژه نمونه

برای مشاهده نمونه پیاده‌سازی پرداخت درون‌برنامه‌ای مایکت در وب‌اپلیکیشن، به پروژه نمونه در GitHub مراجعه کنید.

این صفحه جریان خرید مستقیم مایکت را برای برنامه‌های وب، PWA و پلتفرم‌هایی توضیح می‌دهد که امکان استفاده از کتابخانه‌ی IAB مایکت را ندارند. در این روش هیچ SDKای نصب نمی‌شود؛ برنامه‌ی وب با چند فراخوانی HTTP از طریق سرور خود و هدایت مرورگر کاربر، خرید را انجام می‌دهد.

نکته‌ی امنیتی

توکن دسترسی مایکت را در سرور نگه دارید. شروع خرید، صحت‌سنجی و مصرف محصول باید فقط از سرور شما به سرور مایکت فراخوانی شوند. کال‌بک خرید، درخواست POST مرورگر کاربر است و وب‌هوک سرور-به-سرور محسوب نمی‌شود؛ محصول را تنها پس از تأیید خرید با API صحت‌سنجی تحویل دهید.

جریان خرید مستقیم​

۱. سرور شما سرویس شروع خرید را فراخوانی می‌کند و URL پرداخت را می‌گیرد.
۲. مرورگر کاربر به URL دریافتی هدایت می‌شود تا کاربر در سایت مایکت وارد شود، روش پرداخت را انتخاب کند و تراکنش را تکمیل کند.
۳. پس از پایان فرایند، مرورگر کاربر یک فرم را به callbackUrl برنامه‌ی شما POST می‌کند.
۴. سرور شما اطلاعات کال‌بک را بررسی می‌کند و با API مایکت خرید را صحت‌سنجی می‌کند.
۵. پس از تحویل محصول مصرف‌شدنی، سرور شما آن را با API مصرف می‌کند.

۱. دریافت URL شروع خرید​

این سرویس فقط باید از سرور برنامه فراخوانی شود؛ فراخوانی از کلاینت می‌تواند توکن دسترسی توسعه‌دهنده را افشا کند. نمونه‌های Python از کتابخانه‌ی requests استفاده می‌کنند؛ در صورت نیاز آن را با python -m pip install requests نصب کنید.

curl -X POST \
--location 'https://developer.myket.ir/api/partners/applications/{PACKAGE_NAME}/purchases/iap/start' \
--header 'X-Access-Token: {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"skuId": "{SKU_ID}",
"callbackUrl": "{CALLBACK_URL}",
"developerPayload": "{DEVELOPER_PAYLOAD}"
}'

X-Access-Token را از پنل توسعه‌دهندگان مایکت، بخش محصولات درون‌برنامه‌ای همان برنامه دریافت کنید.

پارامترمحلتوضیح
X-Access-Tokenهدرتوکن صحت‌سنجی برنامه از پنل توسعه‌دهندگان؛ آن را محرمانه نگه دارید.
PACKAGE_NAMEمسیر URLنام پکیج برنامه‌ی ثبت‌شده در مایکت.
skuIdبدنه‌ی JSONشناسه‌ی محصول درون‌برنامه‌ای.
callbackUrlبدنه‌ی JSONURL عمومی برنامه که مرورگر کاربر پس از پایان خرید، فرم کال‌بک را به آن POST می‌کند.
developerPayloadبدنه‌ی JSONمقدار غیرخالی و دلخواه برای تطبیق خرید با سفارش یا کاربر شما؛ حداکثر ۸۰۰ کاراکتر.
نکته

توجه کنید که مقدار طول رشته‌ی developerPayload نمی‌تواند بیش‌تر از ۸۰۰ کاراکتر باشد.

پاسخ موفق با وضعیت 200 شامل URL شروع خرید است:

{
"url": "https://iap.myket.ir/purchases/{PURCHASE_ID}/start"
}

در صورت نامعتبر بودن URL، ممکن است پاسخ 400 با messageCode برابر InvalidUrl دریافت شود. نام پکیج نامعتبر یا ثبت‌نشده برای توسعه‌دهنده، SKU ناشناخته، callbackUrl خالی یا نامعتبر و developerPayload خالی یا بیش از ۸۰۰ کاراکتر نیز باعث خطا می‌شوند.

۲. هدایت مرورگر کاربر​

مرورگر کاربر (نه سرور برنامه) را به مقدار url پاسخ مرحله‌ی قبل هدایت کنید. این صفحه‌ی مایکت محل ورود کاربر، انتخاب روش پرداخت و تکمیل تراکنش است. URL را از پاسخ سرویس بگیرید و آن را از پیش حدس نزنید یا بازسازی نکنید.

۳. دریافت و بررسی کال‌بک​

پس از پایان خرید، مایکت صفحه‌ای حاوی فرم HTML نمایش می‌دهد و مرورگر کاربر آن را به‌صورت خودکار با Content-Type: application/x-www-form-urlencoded به callbackUrl ارسال می‌کند. این درخواست از مرورگر کاربر می‌آید، نه از سرور مایکت.

فیلدتوضیح
statusSuccessful برای خرید موفق، Failed برای خرید ناموفق یا AlreadyPaid اگر کاربر قبلاً خرید را انجام داده باشد.
invoiceرشته‌ی JSON جزئیات تراکنش؛ در حالت ناموفق خالی است.
signatureامضای دیجیتال روی رشته‌ی invoice؛ در حالت ناموفق خالی است.

نمونه‌ی ساختار invoice:

{
"orderId": "60004211",
"packageName": "com.myket.sample",
"productId": "GEM2",
"purchaseTime": 1785081672000,
"developerPayload": "order=45157",
"purchaseToken": "60004211",
"purchaseState": 0,
"autoRenew": false
}
فیلدتوضیح
orderIdشناسه‌ی سفارش.
packageNameنام پکیج برنامه.
productIdشناسه‌ی محصول درون‌برنامه‌ای؛ همان skuId مرحله‌ی شروع خرید.
purchaseTimeزمان تراکنش به‌صورت Unix timestamp بر حسب میلی‌ثانیه.
developerPayloadهمان مقداری که در مرحله‌ی شروع خرید ارسال شده است.
purchaseTokenتوکنی که برای APIهای صحت‌سنجی و مصرف استفاده می‌شود.
purchaseState0 برای خرید موفق و 1 برای خرید ناموفق.
autoRenewبرای استفاده‌ی آتی رزرو شده است؛ فعلاً به آن استناد نکنید.

تأیید امضای دیجیتال​

امضا روی بایت‌های UTF-8 رشته‌ی خام invoice پس از URL-decode محاسبه می‌شود، نه روی JSON بازتولیدشده. مقدار را parse و دوباره serialize نکنید؛ تغییر ترتیب کلیدها، فاصله‌ها یا قالب‌بندی می‌تواند بررسی امضا را ناموفق کند. رشته‌ی decode‌شده را بدون تغییر به‌عنوان raw bytes بررسی کنید.

کلید عمومی همان برنامه را از صفحه‌ی محصولات درون‌برنامه‌ای پنل توسعه‌دهندگان (دکمه‌ی «کلید عمومی») دریافت کنید. کلید به‌صورت Base64 و در قالب DER استاندارد X.509 SubjectPublicKeyInfo ارائه می‌شود. الگوریتم امضا RSA با کلید ۱۰۲۴ بیتی، هش SHA-1 و padding از نوع PKCS#1 v1.5 است (نه PSS). نمونه‌ی Python از کتابخانه‌ی cryptography استفاده می‌کند؛ در صورت نیاز با python -m pip install cryptography نصب کنید.

نمونه‌های بررسی امضا:

import base64
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding


def verify_signature(raw_invoice, signature_base64, public_key_base64):
public_key = serialization.load_der_public_key(
base64.b64decode(public_key_base64)
)
signature = base64.b64decode(signature_base64)

try:
public_key.verify(
signature,
raw_invoice.encode("utf-8"),
padding.PKCS1v15(),
hashes.SHA1(),
)
return True
except InvalidSignature:
return False

اعتبار امضا فقط نشان می‌دهد داده‌ی invoice دستکاری نشده است؛ به‌تنهایی برای تحویل محصول کافی نیست. چون فرم از مرورگر کاربر می‌آید، دریافت آن یا تأیید امضا را جایگزین صحت‌سنجی سرور-به-سرور نکنید.

۴. صحت‌سنجی خرید با API مایکت​

سرور شما باید purchaseToken دریافتی از invoice را با API صحت‌سنجی بررسی کند. این API فقط برای فراخوانی سرور-به-سرور است؛ توکن دسترسی را در برنامه‌ی کاربر قرار ندهید.

curl -X POST \
--location 'https://developer.myket.ir/api/partners/applications/{PACKAGE_NAME}/purchases/products/{SKU_ID}/verify' \
--header 'X-Access-Token: {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"tokenId": "{PURCHASE_TOKEN}"
}'
پارامترمحلتوضیح
X-Access-Tokenهدرتوکن صحت‌سنجی برنامه از پنل توسعه‌دهندگان.
PACKAGE_NAMEمسیر URLنام پکیج برنامه.
SKU_IDمسیر URLشناسه‌ی محصول درون‌برنامه‌ای.
tokenIdبدنه‌ی JSONمقدار purchaseToken دریافتی از کال‌بک.

این API پیش‌تر برای خریدهای SDK نیز استفاده می‌شده و همان سرویس، purchaseToken خرید مستقیم را هم صحت‌سنجی می‌کند. پاسخ موفق شامل فیلدهای زیر است:

فیلدتوضیح
purchaseState0 برای خرید موفق و 1 برای خرید ناموفق.
purchaseTimeزمان تراکنش به‌صورت Unix timestamp.
kindنوع خرید؛ برای مثال androidpublisher#productPurchase.
developerPayloadمقداری که هنگام شروع خرید ارسال شده است.
consumptionState0 برای حالت پیش‌فرض/محصول مصرف‌نشدنی و 1 برای محصول مصرف‌شدنی که مصرف شده است.

نمونه‌ی پاسخ موفق:

{
"kind": "androidpublisher#productPurchase",
"purchaseTime": 1492839267000,
"developerPayload": "",
"purchaseState": 0,
"consumptionState": 1
}

پاسخ ناموفق می‌تواند شامل 500 InternalError باشد. فقط زمانی خرید را معتبر بدانید و محصول یا امتیاز را تحویل دهید که purchaseState برابر 0 باشد. نتیجه را برای جلوگیری از تحویل تکراری با سفارش داخلی خود تطبیق دهید.

۵. مصرف محصول خریداری‌شده​

پس از تحویل محصول مصرف‌شدنی، آن را با API زیر مصرف کنید. این API نیز فقط باید از سرور فراخوانی شود. محصولات غیرمصرف‌شدنی (برای مثال حذف تبلیغات) را مصرف نکنید.

curl -X PUT \
--location 'https://developer.myket.ir/api/partners/applications/{PACKAGE_NAME}/purchases/products/{SKU_ID}/tokens/{PURCHASE_TOKEN}/consume' \
--header 'X-Access-Token: {ACCESS_TOKEN}' \
--header 'Content-Type: application/json'

پاسخ موفق با وضعیت 200:

{
"code": 200,
"messageCode": "Successful",
"translatedMessage": "عملیات با موفقیت انجام شد"
}
کد وضعیتmessageCodeمعنی
400SkuAlreadyConsumedمحصول قبلاً مصرف شده است.
404PurchasedSkuNotFoundخرید یا توکن پیدا نشد.
404SkuIdNotFoundمحصول درون‌برنامه‌ای پیدا نشد.
500InternalErrorخطای داخلی سرور.