Build it into your app
Xsolla subscriptions
For apps that sell subscriptions through Xsolla and have no backend of their own. Your app sends the Xsolla user ID to the gateway with an API key; the gateway asks Xsolla with your Xsolla API key and answers whether that user has an active subscription. The check is free.
#Before you start
- An Xsolla Publisher Account with a project that sells subscriptions (Subscriptions → Subscription plans).
- The user ID your app puts into the payment token (user.id). Keep it the same for a user every time, for example an ID the app creates once and stores.
#1. Get the IDs and an API key
- 1
Open Publisher Account
publisher.xsolla.comSign in with a user who may manage API keys.
- 2
Note the merchant ID and project ID
Open the project. The address reads publisher.xsolla.com/<merchant ID>/projects/<project ID>, for example 746076 and 273204.
- 3
Create an API key
Company settings → API keys → Create API key, or the project’s Settings → API keysCopy it right away: Xsolla shows it only once. Anyone with it can read your subscriptions, so do not put it in the app.
#2. Put them on an API key
- 1
Open the key your app uses
Console → API keys → EditUse a separate key for the app, limited to the models it uses and with a quota ceiling.
- 2
Fill in Xsolla subscription check
Set up Xsolla checkEnter the merchant ID, the project ID and the API key, then save. The gateway tries the key with Xsolla before storing it; it is stored encrypted and never shown again.
#3. Check from the app
Check when the payment window closes, and again when the app opens. Send the same user ID the app put into the payment token. Do not block app start on the answer: show the last saved result and update it when the check returns. Each key may check 120 times per minute by default.
import 'dart:convert';
import 'package:http/http.dart' as http;
const apiKey = 'sk-YOUR_KEY';
Future<Map<String, dynamic>?> checkXsolla(String userId) async {
final res = await http.post(
Uri.parse('https://www.aisplice.store/v1/xsolla/verify'),
headers: {
'Authorization': 'Bearer $apiKey',
'Content-Type': 'application/json',
// Request signing on for this key? Then this call must be signed too:
// ...signedHeaders('POST', '/v1/xsolla/verify'),
},
body: jsonEncode({'user_id': userId}),
);
if (res.statusCode != 200) return null; // keep the last saved answer
return jsonDecode(res.body) as Map<String, dynamic>;
}
Future<void> refreshEntitlement(String userId) async {
final status = await checkXsolla(userId);
if (status == null) return;
await saveEntitlement(status); // your local storage
if (status['active'] == true) unlockPaidFeatures();
}#4. Decide what to unlock
| Check answer | What the app does | Typical cause |
|---|---|---|
| active: true, in_trial: true | Unlock. Show “Trial ends” with expiry_time. | Free trial, not charged yet |
| active: true | Unlock until expiry_time. | Paid, including subscriptions with auto-renew off that have not run out yet |
| subscribed: true, active: false | Lock and show a renew screen. | Canceled or frozen |
| subscribed: false | Lock and show the subscribe screen. | No subscription for this user yet, or the payment is not finished |
| HTTP 502 / 503 | Keep the last answer you saved and retry later. | Xsolla or the API key is not answering |
Save expiry_time with the answer. Between checks, treat the subscription as usable until that time, so a phone that is offline keeps working.
#5. Protect the key in the app
A key inside an app can be read from captured traffic. Turn on request signing for that key so a copied key cannot be used on its own, obfuscate the build, and keep a quota ceiling on the key.
#6. Check what happened
Every check, including cached answers and failures, is listed in Console → Request logs as a free xsolla-verify row on the key.
- The save says Xsolla does not accept the key: the key was copied incompletely, was deleted, or belongs to another company. Create a new one.
- xsolla_credentials_invalid: the key was deleted in Publisher Account after it was saved. Create a new key and enter it on the API key.
- Every check answers subscribed: false: the user ID differs from the one in the payment token, or the project ID on the key is another project.
Still stuck?
Send us the request ID or the error message and we will look into it.