Docs
In-App Purchases
Generate revenue with In-App Purchases (IAP) which allow users to buy digital goods and virtual currency within your application.
Users can use TikTok Beans to acquire goods within your mini game. Beans are in-app virtual credits that can be used to unlock or access content within your mini game. Beans can be converted into any virtual currency that's exclusively used within your mini game. Beans are currently availble for purchase in fixed pricing tiers.
Prerequisites
Before setting up in-app ads for your mini game, you must have completed the following:
- Completed business verification and industry qualification review for your organization
- Enabled In-App Purchases for your mini game
- Reviewed the TikTok Beans Pricing Tiers table to confirm SKUs exist for your pricing model
Virtual currency and pricing tiers
TikTok Beans
Beans are in-app virtual credits that can be used to unlock or access content within your mini app. Beans can be converted into any virtual currency that's exclusively used within your mini app.
If you need to display the number of Beans consumed in the game, you can use the following image as the icon for Beans.
Pricing tiers
TikTok Beans exist in fixed pricing tiers. Refer to this pricing tiers chart for more information on the packages available.
IAP setup for mini games
This flow allows users to make purchases within your mini game using Beans, the virtual currency of TikTok. This requires users to complete the login process first.
Note: Silent login is required for in-game payment operations.
Step 1: User login
The IAP flow only requires the user's open ID, which you can obtain through TikTok's silent login capability:
- Frontend calls
TTMinis.game.login()to get anauthorization_code. - Frontend sends the
authorization_codeto your backend, - Backend calls
POST /v2/oauth/token/to exchange foraccess_tokenandopen_id. - Backend can optionally save the
open_idand token for future use.
No explicit user authorization popup is required. The login process can be completed silently without requesting user profile information.
Step 2: Create a prepayment order
First, create an order by calling TikTok's backend Payment API:
POST https://open.tiktokapis.com/v2/minis/trade_order/create/
POST https://open.tiktokapis.com/v2/minis/trade_order/create/
curl --location 'https://open.tiktokapis.com/v2/minis/trade_order/create' \
--header 'Authorization: Bearer {access token}' \
--header 'Content-Type: application/json' \
--data '{
"token_type": "BEANS", // Required
"token_amount": 100, // Required
"order_info": {
"order_id": "external_order_id_003", // Required
"product_name": "Wake up dad! wedding time", // Required; displayed on the user's order list page
"order_url": "/profile/order_history/external_product_id",
"quantity": 1,
"quantity_unit": "relive", // Pass the unit of the item being sold as appropriate; e.g., "episode" is the unit for a series
"image_url": "https//your.domain/pics/wake_up_dad.jpg"
}
}'The server creates a payment order through the open API and obtains the order's trade_order_id:
response:
{
"data": {
"trade_order_id": "xxxx"
},
"error": {
"code": "ok",
"message": "",
"log_id": "20241125114034036EE8AEADBAF91D5E93"
}
}Implementation tips
- Anti-tampering check: The
token_amount(price) must be calculated by your backend based on database configurations. Never directly use price values sent from the frontend to prevent malicious tampering. - Order association: Store the returned
trade_order_id(TikTok order ID) and associate it with your internalorder_id. Both IDs will be included in subsequent W - webhook callbacks to help you identify which user purchased which entitlement.
- Token management: If you receive a 401 error, the
access_tokenhas expired. Your backend should trigger a silent refresh or notify the frontend to re-login.
Step 3: Initiate payment flow on frontend
Call the Mini Games SDK payment endpoint to initiate the payment process:
TTMinis.game.pay({
trade_order_id: "{your_trade_order_id}",
success: () => {
// Do something if succeeded
},
fail: (error) => {
// Do something if failed
},
complete: () => {
// Do something if completed
}
})Poll for payment result
After the success or complete callback is triggered, the frontend should poll your backend for order status every 1-2 seconds until the backend confirms receipt of the webhook and successful update of the user's assets.
/**
* Independent async polling function
*/
async function checkVirtualAssetsStatus(order_id) {
let retryCount = 0;
const maxRetries = 12; // Poll up to 12 times (~24 seconds)
const poll = async () => {
if (retryCount >= maxRetries) {
showModal("Notice", "There is a slight delay in entitlement delivery. Please refresh your profile later to check.");
return;
}
try {
// requestBackendStatus should be your encapsulated network request tool
const res = await requestBackendStatus(order_id);
if (res && res.isProcessed) {
showToast("Virtual item has been delivered!");
updateGameUI(); // Refresh game coin or item display
} else {
retryCount++;
// Next poll after 2 seconds
setTimeout(poll, 2000);
}
} catch (err) {
console.error("Polling request exception", err);
retryCount++;
setTimeout(poll, 2000);
}
};
poll(); // Start first check
}Important: The frontend success callback only indicates that the payment process has finished on the client side. Never use frontend callbacks as the basis for delivering virtual entitlements. Always wait for the server-side webhook confirmation.
Step 4: Register a callback URL for payment completion
Payment results are delivered asynchronously to your backend via webhook. This is the only valid basis for delivering entitlements. Webhook URLs must be registered in the Developer Portal.
If you need to verify the source of webhooks, refer to the webhook verification documentation.
Webhook types
Event | Description |
| Order payment successful |
| Order refund successful (currently unavailable) |
| Order refund failed (currently unavailable) |
| The order amount partially recovered due to the user initiating a refund in the store, with the value of refund_amount being the recovered amount |
Webhook payload example
{
"client_key": "your_client_key",
"event": "minis.trade_order.redeem.success",
"create_time": 1615338610,
"user_openid": "",
"content": "{\"trade_order_id\":\"TOID667700996\",\"order_id\":\"order_id_as_in_your_system\",\"is_sandbox\":true}"
}
{
"client_key": "your_client_key",
"event": "minis.trade_order.redeem.refund_traceback",
"create_time": 1744946518,
"user_openid": "",
"content": "{\"trade_order_id\":\"TOID2157c5ba03\",\"order_id\":\"order_id_as_in_your_system\",\"is_sandbox\":true,\"refund_amount\":80}"
}Webhook validation and processing flow
- Signature verification:
- Extract
t(timestamp) ands(signature) from theTikTok-Signatureheader - Use the raw request body (do not re-serialize) to generate
signed_payload = t + '.' + raw_body - Use your
client_secretas the key to compute HMAC-SHA256 hash ofsigned_payloadto getlocal_signature - Reject the request if
local_signature != s - Timestamp validation:
- Check if
current time - tis within the allowed window (e.g., 5 minutes) - Reject expired requests to prevent replay attacks
- Idempotency check:
- Check if the
trade_order_idor your internalorder_idhas already been processed (fulfilled) - Return 200 OK immediately for duplicate webhooks to avoid repeated delivery
- Optional secondary order verification:
- For additional security, call
POST /v2/minis/trade_order/query/to confirm the order status directly from TikTok - Only deliver entitlements if the order status is
SUCCESS - Deliver entitlements:
- Add virtual currency, unlock content, or deliver items to the user
- Update order status to
FULFILLEDin your database (preferably in an atomic transaction with entitlement delivery) - Return 200 OK to confirm receipt
Debug In-App Purchases configuration
- Enable simulation: Turn on the Enable IAP Mock setting in the developer options section of DevTool and scan code to preview.
- Sandbox configuration: Backend recognizes the
is_sandbox: trueflag, which is used to distinguish between real revenue and test data.
Start local debugging service
Enter the project root directory and open the debugging page through ttmg dev.
Set up payment development options
In DevTool, the Enable IAP Mock toggle does the following:
- Turn on Enable IAP Mock: The payment link does not need to be charged to complete the subsequent payment process.
- Turn off Enable IAP Mock: The payment chain follows the real deduction process, and subsequent steps can only be completed after payment.
Scan QR code to debug payment process
Method 1: Select remote debugging
In this mode, the in-game JS logs will be available in the browser console. You can operate the game within the TikTok Client, enter the actual payment process, view and debug the logs, and complete the development and debugging of the IAA feature.
If you find it laggy during debugging (remote debugging is affected by the quality of your Wi-Fi network), you can switch to the real device preview mode for development and debugging.
Method 2: Select real device preview
- Enable IAP Mock: This allows you to test the payment process without paying real money.
- Show vConsole: In the real machine preview mode, the game running log needs to be turned on after this switch is turned on, and the log can be viewed through vConsole in the terminal
Sandbox identification
Always check the is_sandbox field in webhook callbacks or order query responses:
true: Test order (should not be counted in production revenue)false: Real production order
Note: Ensure your backend properly handles the is_sandbox flag to avoid treating test orders as real transactions in production.
Revenue data
IAP data can only be viewed after the application is launched and goes live. After launch, you can review your revenue analytics in the In-App Purchases (IAPs) module on your app page.
Frequently Asked Questions
Login and credentials
Do I need to guide users to click an authorization popup before accessing payment?
- No. IAP only requires the user's OpenID, which can be obtained through silent login without requesting user profile permissions.
What should I do if the access token expires?
- When you receive a 401 error from TikTok Open API, notify the frontend to re-run the
TTMinis.game.loginflow to get a new authorization code, then exchange for a valid token on the backend.
Order management
Do I need to pre-configure product IDs and prices in the TikTok Developer Portal?
- No. TikTok does not require pre-registration of products. You only need to pass the
token_amount(price in Beans) when calling the create order API.
Can the same trade_order_id be used for multiple payments?
- No. Each trade_order_id corresponds to a single transaction. If payment fails or is canceled, you must create a new order to get a new trade_order_id for re-attempts.
Payment errors
Why do I get "An error occurred, please try again later" on the payment page?
- This is usually related to regional settings. Payments are currently not available in Mainland China (CN) and Hong Kong (HK) regions. Ensure your device system region, app store account region, and IP address are all in supported regions (for example: US, JP).
My polling API returns errors in the real device environment.
- Add your business domain to the domain whitelist in the TikTok Developer Portal. Otherwise,
tt.requestwill fail directly in the real device environment.
Payment security
The frontend shows payment success. Can I deliver items immediately?
- No. Frontend callbacks only indicate the client-side flow is complete. Always wait for the server-side Webhook confirmation with valid signature verification before delivering entitlements.
How to prevent duplicate entitlement delivery?
- Always check if the order has already been processed using
trade_order_idor your internalorder_idbefore delivering entitlements. This handles duplicate webhook deliveries caused by network fluctuations.