Skip to main content

Myket In-App Purchase Guide for Web Application

Sample project

For an example implementation of Myket in-app purchases in a web application, see the sample project on GitHub.

This page describes Myket's direct-purchase flow for web apps, PWAs, and similar platforms that cannot use the Myket IAB library. No SDK needs to be installed: the web app makes a few HTTP requests through its server and redirects the user's browser to complete the purchase.

Security

Keep the Myket access token on your server. Start, verify, and consume requests must be server-to-server. The purchase callback is a POST from the user's browser, not a server-to-server webhook; grant the product only after verifying the purchase with Myket's verification API.

Direct-purchase flow​

  1. Your server calls the purchase-start service and receives a payment URL.
  2. Redirect the user's browser to that URL. The user signs in to Myket, selects a payment method, and completes the transaction.
  3. After the flow ends, the user's browser POSTs a form to your callbackUrl.
  4. Your server inspects the callback and verifies the purchase with Myket's API.
  5. After delivering a consumable product, your server consumes it through the API.

1. Get the purchase-start URL​

Call this service only from your app's server. Calling it from a client can expose the developer access token. The Python examples use the requests library; install it with python -m pip install requests if needed.

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}"
}'

Get X-Access-Token from the in-app products section for the app in the Myket Developer Panel.

ParameterLocationDescription
X-Access-TokenHeaderThe app's verification token from the Developer Panel. Keep it secret.
PACKAGE_NAMEURL pathThe package name registered with Myket.
skuIdJSON bodyThe in-app product ID.
callbackUrlJSON bodyA public URL where the user's browser can POST the callback form after the purchase.
developerPayloadJSON bodyA non-empty application-defined value for correlating the purchase with your order or user; maximum 800 characters.
tip

The developerPayload string must not exceed 800 characters.

A successful 200 response contains the purchase-start URL:

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

An invalid URL may return 400 with messageCode set to InvalidUrl. Other errors can occur if the package name is invalid or not registered to the developer, the SKU is not found, callbackUrl is empty or invalid, or developerPayload is empty or longer than 800 characters.

2. Redirect the user's browser​

Redirect the user's browser (not your server) to the url from the previous response. This Myket page is where the user signs in, chooses a payment method, and completes the transaction. Use the URL returned by the service; do not guess or reconstruct it.

3. Receive and inspect the callback​

After the purchase flow ends, Myket displays a page containing an HTML form. The user's browser automatically POSTs it to callbackUrl with Content-Type: application/x-www-form-urlencoded. This request comes from the user's browser, not from Myket's server.

FieldDescription
statusSuccessful for a successful purchase, Failed for a failed purchase, or AlreadyPaid if the user has already completed the purchase.
invoiceA JSON string containing transaction details; empty for a failed purchase.
signatureA digital signature over the invoice string; empty for a failed purchase.

Example invoice structure:

{
"orderId": "60004211",
"packageName": "com.myket.sample",
"productId": "GEM2",
"purchaseTime": 1785081672000,
"developerPayload": "order=45157",
"purchaseToken": "60004211",
"purchaseState": 0,
"autoRenew": false
}
Invoice fieldDescription
orderIdOrder ID.
packageNameApp package name.
productIdIn-app product ID; the same value as skuId used to start the purchase.
purchaseTimeTransaction time as a Unix timestamp in milliseconds.
developerPayloadThe value sent when starting the purchase.
purchaseTokenToken used with the verify and consume APIs.
purchaseState0 for a successful purchase; 1 for an unsuccessful purchase.
autoRenewReserved for future use; do not rely on it currently.

Verify the digital signature​

The signature is calculated over the UTF-8 bytes of the raw invoice string after URL decoding, not over reserialized JSON. Do not parse and serialize the value again: changing key order, whitespace, or formatting can cause signature verification to fail. Verify the decoded string without modification as raw bytes.

Get the app's public key from the in-app products page in the Myket Developer Panel (the Public key button). It is provided as Base64-encoded DER in the X.509 SubjectPublicKeyInfo format. The signature algorithm is RSA with a 1,024-bit key, SHA-1, and PKCS#1 v1.5 padding (not PSS). The Python example uses the cryptography library; install it with python -m pip install cryptography if needed.

Signature verification examples:

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

A valid signature only indicates that the invoice data has not been altered; it is not sufficient to grant a product. Since the form comes from the user's browser, neither receiving it nor validating its signature replaces server-to-server purchase verification.

4. Verify the purchase with Myket's API​

Your server must verify the purchaseToken from the invoice with Myket's purchase-verification API. This API is server-to-server only; never put the access token in the client app.

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}"
}'
ParameterLocationDescription
X-Access-TokenHeaderThe app's verification token from the Developer Panel.
PACKAGE_NAMEURL pathThe app package name.
SKU_IDURL pathThe in-app product ID.
tokenIdJSON bodyThe purchaseToken received in the callback.

This API was already used to verify SDK purchases; it also verifies the purchaseToken from a direct purchase. A successful response contains these fields:

FieldDescription
purchaseState0 for a successful purchase; 1 for an unsuccessful purchase.
purchaseTimeTransaction time as a Unix timestamp.
kindPurchase type, for example androidpublisher#productPurchase.
developerPayloadThe value sent when starting the purchase.
consumptionState0 for the default/non-consumable state and 1 for a consumable product that has been consumed.

Example successful response:

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

A failed request can return 500 InternalError. Treat a purchase as valid and deliver the product or credit only when purchaseState is 0. Correlate the result with your own order to prevent duplicate delivery.

5. Consume a purchased product​

After delivering a consumable product, consume it with the API below. This API must also be called from your server. Do not consume non-consumable products (for example, an ad-removal purchase).

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'

A successful response has status 200:

{
"code": 200,
"messageCode": "Successful",
"translatedMessage": "Operation completed successfully"
}
HTTP statusmessageCodeMeaning
400SkuAlreadyConsumedThe product has already been consumed.
404PurchasedSkuNotFoundThe purchase or token was not found.
404SkuIdNotFoundThe in-app product was not found.
500InternalErrorInternal server error.