Wrapply logoWrapply Tutorials
💳 Store payments

In-App Purchase Integration: from website to Flutter app

This guide explains how to prepare native in-app purchases for a Wrapply app: store products, product IDs, purchase calls, restore flow and backend receipt validation.

In-app purchase bridge commands

When the in-app purchase module is enabled, the generated app exposes real JavaScript commands inside the WebView. Your website calls these commands, then your backend should verify and unlock the paid feature.

CommandUsePayloadTypical response
window.Wrapply.iap.getProducts(payload)Read products configured in Google Play or App Store{ productIds: ['premium_monthly'] }{ ok, available, products, notFoundIds }
window.Wrapply.iap.purchase(payload)Start a native purchase flow{ productId, consumable, applicationUserName }{ ok, purchaseStarted, product }
window.Wrapply.iap.restorePurchases(payload)Restore non-consumable purchases or subscriptions{ applicationUserName }{ ok, restoreStarted, latestPurchases }
window.Wrapply.iap.getStatus(payload)Check if store billing is available and read latest events{}{ ok, available, latestPurchases }

Required store setup

Create the products in Google Play Console and App Store Connect using the same product IDs used by the website. Test on real devices and validate receipts server-side before unlocking subscriptions, credits or premium content.

function onWrapplyReady(callback) {
if (window.Wrapply?.__nativeBridgeReady) return callback(window.Wrapply);
window.addEventListener('wrapplyNativeBridgeReady', () => callback(window.Wrapply), { once: true });
}

onWrapplyReady(async (Wrapply) => {
if (!Wrapply.iap) return;
const products = await Wrapply.iap.getProducts({ productIds: ['premium_monthly', 'credits_10'] });
console.log(products);
});
async function buyPremium(currentUser) {
if (!window.Wrapply?.iap) return showWebCheckoutFallback();
const result = await window.Wrapply.iap.purchase({
productId: 'premium_monthly',
consumable: false,
applicationUserName: currentUser.id
});
await fetch('/api/iap/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(result)
});
}
async function restoreNativePurchases(currentUser) {
if (!window.Wrapply?.iap) return;
const restored = await window.Wrapply.iap.restorePurchases({
applicationUserName: currentUser.id
});
await fetch('/api/iap/restore', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(restored)
});
}

Integration examples

Use the same bridge pattern in plain JavaScript, React, Vue, Angular, PHP/Laravel, WordPress or jQuery. The important part is to wait for the native bridge before calling it.

JavaScript

async function runNativeFeature(currentUser) {
if (!window.Wrapply?.iap) return;
const result = await window.Wrapply.iap.purchase({
productId: 'premium_monthly',
consumable: false,
applicationUserName: currentUser.id
});
await fetch('/api/wrapply-event', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(result)
});
}

React hook

import { useEffect, useState } from 'react';
export function useWrapplyBridge() {
const [ready, setReady] = useState(false);
useEffect(() => {
if (window.Wrapply?.__nativeBridgeReady) return setReady(true);
const onReady = () => setReady(true);
window.addEventListener('wrapplyNativeBridgeReady', onReady);
return () => window.removeEventListener('wrapplyNativeBridgeReady', onReady);
}, []);
return { ready, Wrapply: ready ? window.Wrapply : null };
}

Vue composable

import { ref, onMounted } from 'vue';
export function useWrapplyBridge() {
const ready = ref(false);
onMounted(() => {
if (window.Wrapply?.__nativeBridgeReady) ready.value = true;
else window.addEventListener('wrapplyNativeBridgeReady', () => ready.value = true, { once: true });
});
return { ready };
}

Angular service

@Injectable({ providedIn: 'root' })
export class WrapplyBridgeService {
async callNative(payload = {}) {
if (!window.Wrapply?.iap) return { ok: false, error: 'Bridge unavailable' };
return window.Wrapply.iap.getStatus(payload);
}
}

jQuery

$('#wrapply-action').on('click', async function () {
if (!window.Wrapply?.iap) return;
const result = await window.Wrapply.iap.getStatus({});
$('#wrapply-output').text(JSON.stringify(result, null, 2));
});

CMS / backend template

<button onclick="runNativeFeature(window.currentUser)">Open app feature</button>
<script>
window.addEventListener('wrapplyNativeBridgeReady', function () {
document.body.classList.add('wrapply-native-app');
});
</script>
Important: the bridge works inside the generated app WebView, not on the public browser version of the website.
When native features are enabled, the generated ZIP also includes WRAPPLY_NATIVE_BRIDGE_GUIDE.pdf with the enabled modules, exact calls and implementation notes for the customer.

When native in-app purchase may be required

Store rules are especially important when the app gives access to digital value consumed inside the app. Physical goods, real-world services and some external business flows are different cases, but digital subscriptions and premium features often need native billing.

Subscriptions
Premium plans, paid memberships, recurring access or paid account upgrades.
Digital content
Courses, files, templates, AI credits, unlockable features or downloadable digital assets.
App-only access
Features consumed inside the app after payment.
Website payment links
If the website checkout is shown inside the app, store review may ask for a native purchase flow instead.
Store approval decisions belong to Google and Apple. This guide is technical guidance, not legal advice. Before publishing, review the latest Google Play and App Store rules for your product type.

Recommended architecture

The clean approach is to keep the website and backend as the source of truth, then add a native purchase layer in Flutter and synchronize purchase status with your existing user account system.

User signs in

The Flutter app identifies the same user used by your website, usually through Firebase Auth, your existing API token, OAuth or a custom login endpoint.

App loads products

The app requests product identifiers configured in Google Play Console and App Store Connect.

User purchases natively

Flutter starts the native billing flow with Google Play Billing or StoreKit through a Flutter package.

Backend validates receipt

The purchase token or receipt is sent to your backend, where it is verified server-side with Google or Apple.

Backend unlocks entitlement

Your system updates the user subscription, premium flag, credit balance or access level.

Website and app stay in sync

The web app reads the same entitlement state, so access is coherent across mobile app and website.

Flutter source-code implementation

From the Wrapply source code you can add native purchase logic using a package such as in_app_purchase. The exact implementation depends on your backend, login system, product catalog and subscription model.

Product IDs
Create matching product identifiers in Google Play Console and App Store Connect, then map them in Flutter.
Purchase service
Add a Dart service that loads products, starts checkout, listens for purchase updates and handles restore purchases.
Backend endpoint
Create an HTTPS endpoint such as /iap/verify that receives user ID, platform and purchase token.
Entitlement sync
Store the validated entitlement in your database and expose it to both the app and website.
Suggested flow:
Flutter purchase completed
→ send purchase token to backend
→ backend validates with Google/Apple
→ backend updates user entitlement
→ app reloads premium state

Sync with your existing system

The goal is not to create a second disconnected payment system. The native purchase should update the same account, subscription or access model that your website already uses.

Same user identity
Connect purchases to your existing user ID, not only to a device.
Server-side validation
Do not trust only client-side purchase status for premium access.
Webhook handling
Use Google and Apple subscription notifications when recurring subscription status changes.
Restore purchases
Let users restore purchases after reinstalling the app or changing device.
Access API
Your website should read entitlement status from the backend so app and web stay aligned.
Migration logic
If users already paid on the website, define how existing paid accounts map to app access.

What Wrapply can provide

Wrapply generates the app foundation and can provide Flutter source code. In-app purchase integration is a custom source-code and backend task because every business has different products, accounts and access rules.

Source-code base
Start from the generated Flutter project instead of rebuilding the mobile app from zero.
Integration planning
Define which product IDs, backend endpoints and entitlement states are needed.
Custom development
Add purchase screens, restore flow, validation calls and app-side premium state.
Publishing support
Prepare notes for store review and align the payment flow with app submission requirements.
Best conversion path: generate the app first, review the Flutter source code, then add native purchases only when your store use case really requires them.

FAQ

Can I keep Stripe on my website?

For physical goods or real-world services, external payment flows may be acceptable. For digital products consumed in the app, stores may require native in-app purchase. Your exact case must be reviewed before publishing.

Is this included in the standard APK or AAB?

No. Native purchase integration requires source-code work, store product setup and backend synchronization.

Can the mobile app and website share the same subscription?

Yes, if your backend validates native purchases and updates the same user entitlement used by the website.

Ready to plan a compliant app payment flow?

Start with your website URL and source-code package, then evaluate whether native in-app purchase integration is needed for Google Play or App Store approval.