In-app purchases are the highest-margin revenue channel for mobile Ludo games, but integrating payment systems across multiple platforms is technically complex and compliance-intensive. This guide covers the complete IAP implementation: setting up product catalogs, implementing purchase flows, validating receipts server-side, handling subscription lifecycles, and managing refunds and chargebacks.
IAP Integration Architecture
Every in-app purchase follows a three-phase flow: the client initiates the purchase with the platform store (Google Play / App Store), the platform processes payment and returns a purchase token/receipt, and your server validates the token and credits the player's wallet. Never deliver purchased items before server-side validation — this is the most common IAP security vulnerability.
Google Play Billing Integration
Google Play uses the Play Billing Library (Kotlin/Java) for in-app purchases and the Google Play Developer API for server-side receipt validation. All Ludo game SKUs should be configured as Managed Products (consumable) or Subscriptions in the Google Play Console before integration.
// Initialize billing client
val billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(result: BillingResult) {
if (result.responseCode == BillingClient.BillingResponseCode.OK) {
queryAvailableProducts()
}
}
override fun onBillingServiceDisconnected() {
// Retry connection with exponential backoff
}
})
// Query available products
fun queryAvailableProducts() {
val params = QueryProductDetailsParams.newBuilder()
.setProductList(
listOf(
ProductDetails.newBuilder()
.setProductId("coins_100")
.setProductType(BillingClient.ProductType.INAPP)
.build(),
ProductDetails.newBuilder()
.setProductId("coins_1000")
.setProductType(BillingClient.ProductType.INAPP)
.build(),
ProductDetails.newBuilder()
.setProductId("vip_pass_monthly")
.setProductType(BillingClient.ProductType.SUBS)
.build()
)
)
.build()
billingClient.queryProductDetails(params) { result ->
val productDetailsList = result.productDetailsList
// Display products in your game's shop UI
}
}
Apple App Store Integration
Apple uses StoreKit 2 (Swift) for in-app purchases with a unified API for subscriptions and one-time purchases. App Store Connect hosts your product catalog, and App Store Server API handles server-side validation and subscription status notifications.
import StoreKit
// Request and process a purchase
@MainActor
func purchase(product: Product) async throws -> Transaction? {
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
await updateServerWithPurchase(transaction: transaction)
await transaction.finish()
return transaction
case .userCancelled:
return nil
case .pending:
return nil
@unknown default:
return nil
}
}
// Server-side validation
func updateServerWithPurchase(transaction: Transaction) async {
let body: [String: Any] = [
"player_id": currentPlayer.id,
"product_id": transaction.productID,
"transaction_id": String(transaction.id),
"original_transaction_id": String(transaction.originalID ?? transaction.id),
"app_account_token": transaction.appAccountToken?.uuidString ?? "",
"environment": "sandbox" // or "production"
]
guard let url = URL(string: "https://api.ludokingapi.com/v1/iap/validate/apple") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
let (_, response) = try await URLSession.shared.data(for: request)
// Handle response and credit player wallet
}
Stripe Integration for Web/Desktop
For web platforms and desktop Ludo games, Stripe Checkout or Stripe Elements provides a PCI-compliant payment flow without requiring App Store or Play Store commissions. This is particularly cost-effective for higher-priced items where the 30% platform cut becomes significant.
// Backend: Create Stripe Checkout Session
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
app.post('/api/v1/iap/create-checkout', async (req, res) => {
const { player_id, product_id, coin_amount } = req.body;
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [{
price_data: {
currency: 'usd',
product_data: {
name: `${coin_amount} Ludo Coins`,
description: 'Virtual currency for Ludo King game'
},
unit_amount: getPriceInCents(product_id) // e.g., 499 for $4.99
},
quantity: 1
}],
mode: 'payment',
success_url: `https://yourgame.com/shop/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: 'https://yourgame.com/shop',
metadata: {
player_id,
product_id,
coin_amount,
idempotency_key: `${player_id}_${product_id}_${Date.now()}`
},
customer_email: req.user?.email // if available
});
res.json({ checkout_url: session.url });
});
// Webhook: Handle payment confirmation
app.post('/webhook/stripe', express.raw({type: 'application/json'}), async (req, res) => {
const sig = req.headers['stripe-signature'];
const event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
await creditPlayerWallet({
player_id: session.metadata.player_id,
coins: parseInt(session.metadata.coin_amount),
transaction_ref: session.id
});
}
res.json({ received: true });
});
Server-Side Receipt Validation
Never trust purchase data from the client alone. Every purchase must be validated server-side before crediting the player's wallet. The Ludo King API provides a unified validation endpoint that handles Google Play, App Store, and Stripe receipts.
{
"player_id": "player_7x4k9m2p",
"platform": "google_play", // google_play | app_store | stripe
"receipt_data": "MIIXxAYJKoZIhvcNAQcCoIIXtTCCF7ECAQExDDA...",
"purchase_token": "gpak.token.here...",
"product_id": "coins_1000",
"client_txn_id": "purchase_client_1705768200",
"signature": "android_signature_here"
}
// Response — successful validation
{
"status": "valid",
"validated_at": "2025-01-20T10:35:42Z",
"product_id": "coins_1000",
"coins_awarded": 1000,
"wallet_new_balance": 6000,
"transaction_id": "txn_9f3k2m8p",
"duplicate": false
}
Refund and Chargeback Handling
Refund handling is critical for financial stability. Platform stores offer varying refund windows: Google Play allows refunds within 48 hours automatically and up to 3 days via user request, up to 30 days via developer API, and up to 70 days for subscription refunds. Apple App Store refunds are managed primarily through the user, with developer-initiated refunds available up to 180 days. Stripe allows chargebacks up to 120 days post-charge.
Configure webhook listeners for refund and chargeback events from all platforms, and always deduct the equivalent balance from the player's wallet when a refund is processed. Repeated refund patterns should trigger account review and potential suspension.
IAP Product Catalog Strategy
Structure your IAP catalog to serve different player segments and purchase motivations:
- Entry Products: Low-priced items ($0.99–$2.99) for casual spenders. High volume, low margin. Bundles of 100–500 coins.
- Value Products: Mid-tier bundles ($4.99–$9.99) with "most popular" badge. The highest revenue-generating tier. 1,000–3,000 coins.
- Premium Products: High-value bundles ($19.99–$49.99) with bonus multipliers (1.5x–2x). Target whales and committed players.
- Subscriptions: VIP passes at $4.99–$9.99/month providing ongoing value (daily bonuses, exclusive rooms, ad-free). Highest LTV channel.
Frequently Asked Questions
For mobile apps distributed through Google Play and the App Store, you must use their respective payment systems for digital goods and services — this is explicitly prohibited by both platforms' developer policies. However, for web-based Ludo games, desktop applications, and PWAs, you can use Stripe, Razorpay, PayU, or any PCI-compliant payment processor. Some developers use real-money IAP through Google Play but add alternative payment portals in their web store for items on other platforms to avoid the 30% platform fee.
Implement these layers of IAP security: (1) Server-side receipt validation for every purchase — never trust client data. (2) Signature verification for Google Play purchases using the Google Play Developer API. (3) App-Specific StoreKit 2 tokens linked to player accounts for App Store purchases. (4) Rate limiting on validation endpoints to prevent brute-force replay attacks. (5) Link large purchases ($50+) to verified player accounts with confirmed email and device binding. (6) Monitor for duplicate transaction IDs — a transaction ID should never be validated twice.
Google Play and Apple App Store both charge a standard 30% commission on digital goods and services revenue. This drops to 15% for developers who generated less than $1,000,000 in the previous 12 months and are enrolled in the reduced fee program. Real-money gaming platforms may face additional compliance reviews and potentially higher fee structures. Stripe typically charges 2.9% + $0.30 per transaction with no platform fee — making it significantly cheaper for web/desktop Ludo games with high-value purchases.
Store subscription status via platform webhook notifications (Google Play's SUBRIBUTIONS_ON_HOLD, SUBSCRIPTION_RENEWED, SUBSCRIPTION_CANCELED; App Store's SUBSCRIBED, DID_RENEW, DID_FAIL_TO_RENEW, DID_CHANGE_RENEWAL_PREF). When a player upgrades mid-cycle, prorate the remaining value and immediately grant the new tier's benefits. For downgrades, apply changes at the next billing cycle. For cancellations, maintain access until the end of the current billing period — never revoke access immediately upon cancellation.
Use consumable products (coins, gems, power-ups, game entry tokens) for items that are depleted through gameplay. Use non-consumable products (permanent board skins, token designs, VIP status) for permanent unlocks. Use subscriptions for time-limited VIP access with ongoing benefits. Never sell gameplay advantages — only cosmetic and convenience items. This preserves competitive integrity and reduces regulatory risk for real-money gaming integration.
Track first purchase eligibility server-side in the player's profile. When the IAP validation webhook fires, check if the player has any completed purchase history. If first purchase is confirmed, award a bonus multiplier (typically 1.5x–2x the base coin amount) from your server. Display the bonus clearly in the purchase confirmation screen. Mark the player as "has_purchased" in your database to prevent bonus abuse on subsequent purchases.