Sell subscriptions with in-app purchases

What you’ll build

By the end of this guide, your app will offer one or more subscription tiers on a native paywall, take payment through Apple’s App Store and Google Play, and unlock premium content the moment someone subscribes. You’ll set every bit of this up from WordPress — no app code required.

In MAM Suite, one plugin runs the whole in-app purchase (IAP) pipeline: mam-inapp-purchase-manager. It carries three jobs:

  1. Models your subscription tiers as WooCommerce products. Each IAP-flagged product becomes one buyable tier on the paywall.
  2. Records purchases. When the app reports a successful App Store / Google Play receipt, the plugin writes a completed WooCommerce order against the buying user, stamped with an expiration date. That order is the WordPress-side record of “this user is subscribed until X.”
  3. Gates content. The plugin adds a paywall configuration block to the mobile JSON payload — whether to show the paywall, which products to display, how to style it, and where the legal links point.

Source of truth. Apple and Google are the real billing systems here. The WooCommerce order is a convenience record of what the app reported — not a payment record, and not something MAM checks against Apple or Google. Treat it as a mirror of the purchase, not proof of one.


Before you start

Think of this as the map, not the manual. Each major step has its own dedicated recipe in the Plugin Reference — this article just puts them in the right order. Here’s what to have ready before you start:

  • WordPress admin access with the manage_options capability.
  • WooCommerce active. This guide models subscription tiers as WooCommerce products and records purchases as WooCommerce orders, so WooCommerce must be active for the workflow described here. (The plugin does not declare it as a hard dependency via mam_required_plugins — a GetPaid/GeoDirectory-backed path can run without it — but everything in this guide assumes WooCommerce.)
  • mam-inapp-purchase-manager activated under Mobile App Mgr → Software, on top of mam-main 1.9.1+.
  • App Store Connect access (for the iOS shared secret and to create iOS product IDs).
  • Google Play Console access (for the Android billing key and to create Android product IDs).
  • Your subscription products already created in App Store Connect and/or Google Play Console, each with a known product identifier (SKU). MAM does not create the store-side products for you — it maps to them.

Budget about 30–45 minutes for a first-time setup, once your store-side product IDs already exist.


Go to Mobile App Mgr → In-App Purchase Manager. The page opens on General Settings, where you’ll hand the app everything it needs: the credentials to validate purchases on-device, and the legal copy Apple and Google require on a paywall.

Fill in, at minimum:

  • iOS Shared Secret Key — the App-Specific Shared Secret from App Store Connect (not the Master secret). Sent to the app as inapp_shared_sec.
  • Android Key — the Base64 RSA public key from Google Play Console (Monetization setup → Licensing). Sent as inapp_and_key.
  • Android Product Group ID — your Play subscription group identifier. Sent as iap_sub_product.
  • iOS Terms of Service URL, Android Terms of Service URL, and Privacy Policy URL — Apple and Google each require these on a paywall, and the two ToS fields are kept separate because the stores sometimes want different wording. If yours are identical, paste the same URL in both.

Only leave a field blank if you have a specific reason to — say, an iOS build that already bundles its own secret.

For the full field-by-field reference, see Recipe: Configure IAP settings.


Step 2 — Style the paywall to match your brand

Still on General Settings:

  • Logo for Top of Purchase Screen — pick a logo from the media library. The plugin computes the aspect ratio at request time so the app sizes it correctly.
  • Text Color, Button Title Color, Button Background Color, Screen Background Color — four hex color pickers. The plugin sends each to the app as a hex value.

You can leave these at their defaults for now and come back later — styling never blocks the purchase flow.


Step 3 — Build your subscription tiers as products

Every subscription tier you offer is its own WooCommerce product — repeat this step once for each tier (Monthly and Yearly, for example).

The first time you open the plugin’s admin screen, it quietly creates four global product attributes for you, if they don’t exist yet — Duration, Default Product, IAP Discount Label, IAP Subtitle Label. Duration comes seeded with 1 Month and 1 Year; Default Product with Yes / No. Need other duration terms? Add them manually under Products → Attributes before you begin (see the table below).

For each tier:

  1. Create the product under Products → Add New. Whatever you put in the name and description becomes the paywall card’s title and body.

  2. Check “In APP Purchase” in the Product data panel. This flag is what makes the product visible to the IAP pipeline at all — leave it unchecked, and the product gets ignored.

  3. Set the SKU (Inventory tab) to match the App Store Connect / Google Play product identifier exactly. Think of this SKU as the bridge connecting the store, WooCommerce, and the receipt the app reports back — get it wrong, and the receipt vanishes silently with no order recorded. Triple-check spelling and case.

  4. Set the Regular price (General tab) to the display price string, e.g. 4.99. This number is for display only — Apple and Google are the ones actually charging the card, at whatever rate they’ve set.

  5. Set the Duration attribute to the term that matches this tier’s billing period. Behind the scenes, the plugin translates that term into both an expiration date and a price-suffix label:

    Duration term Expiration added What the app shows
    1 Day +1 day per 1 Day
    1 Month +1 month per 1 Month
    1 Quarter +3 months per 1 Quarter
    1 Year +1 year per 1 Year
    One Time none (treated as a one-off, not a subscription) One Time Purchase

    Matching here is case- and word-for-word exact1 Month works, 1 month or 30 days do not. Pick a term outside this list, and the order still gets created, but it never receives an expiration date — which means the buyer never reads as an active subscriber.

  6. (Optional) Mark one tier as the Default Product (Yes) so the app pre-selects it for buyers landing on the paywall. Just make sure only one product ever carries that Yes.

  7. (Optional) Add a Discount Label (e.g. Save 20%) and an IAP Subtitle Label (e.g. Best value) if you want a badge and a secondary line of text on the card.

  8. Set a featured image for the card.

  9. Publish. Want to reorder tiers on the paywall afterward? Drag them in the Products list — they display in menu order, ascending.

For the full walkthrough — gotchas included — see Recipe: Create an IAP product.


Step 4 — Choose the paywall presentation

The app supports two presentations:

  • Multi-product carousel (default) — one card per tier, the buyer picks one.
  • Single-screen UI — a feature/benefit list with a single call-to-action button, best when you sell one tier and want to “sell the benefits.”

Want the single-screen layout instead? Flip on the single-screen option, and a second tab — Screen Settings — appears, where you set the CTA copy, the bullet image, up to eight feature/benefit lines, the purchase button title, and the promo-code flag. For the details, see Recipe: Use the single-screen IAP UI.


Step 5 — Decide when the paywall appears

Two switches on General Settings decide when and whether the paywall shows:

  • Require IAP after login? — the master switch. yes shows the paywall (inapp_has_iap = 'yes') and gates content (inapp_is_required = 'yes'); no suppresses it.
  • Turn off IAP in Sandbox? — when yes, the app skips the paywall in sandbox / TestFlight builds, which smooths QA. Set it to no when you specifically need to test the purchase pipeline end-to-end.

Admins are always exempt. Every administrator account automatically gets inapp_has_iap = 'no' and inapp_is_required = 'no' — so when you’re testing the app as an admin, you’ll see premium content as though you’d already subscribed. If you want to see the real paywall, sign in with a non-admin account instead. Need that same exemption for other users? See Recipe: Bypass IAP for a specific user.


Step 6 — Test the full purchase flow

In a sandbox build:

  1. Sign in as a non-admin user — admins are exempt, so they won’t see the paywall.
  2. Confirm the paywall shows up, and that your tiers, styling, and legal links all render the way you expect.
  3. Trigger a sandbox purchase on one tier.
  4. Back in WordPress, confirm a new completed order shows up under WooCommerce → Orders for that user.
  5. Open that order and check the expiration metadata is set — the plugin writes both an expiration date and a reference to the purchased product.
  6. Refresh the app; the user should now see premium content, same as any subscriber.

If the order never shows up, three usual suspects are worth checking first: a SKU mismatch between WooCommerce and the store, the In APP Purchase checkbox left unchecked, or the user not being signed in at all (so MAM can’t resolve who’s buying). One more thing to know: the plugin deliberately skips duplicate receipts, because Apple often re-sends them — so a second identical receipt won’t quietly create a second order.


How a purchase becomes an unlock

It helps to know the data flow end-to-end, especially the day something doesn’t unlock the way it should:

  1. The buyer taps Buy on the paywall, and Apple or Google completes the transaction right there on-device.
  2. The app reports that receipt back to MAM: the product SKU, plus the expiration timestamp the receipt carries.
  3. The plugin figures out who’s signed in, looks up the matching WooCommerce product by SKU, and — assuming it isn’t a duplicate — creates a completed order stamped with the expiration date derived from that product’s Duration.
  4. Next time the app refreshes, the plugin reads that order to decide whether this user is an active subscriber, then reports inapp_is_required/inapp_has_iap accordingly so the app can unlock content.

If a tier’s Duration doesn’t map to an expiration — see Step 3 — the order still gets created, but the user never reads as subscribed. That’s the single most common cause behind an “I paid but it’s still locked” ticket.


Going further (developer hooks)

Most setups never need this section — it’s pure admin work end to end. But three filter hooks exist for when a developer wants to override behavior without touching core:

  • mam_iap_require_iap — has final say over whether the paywall shows and content gets gated. Reach for it to build per-role rules, feature flags, or time-based access.
  • mam_iap_active_subscriber_product — figures out which subscription a given user is on. Override it if you need to recognize entitlements that never came through a MAM receipt at all — bundles, B2B licenses, comped accounts.
  • mam_iap_purchase — the internal entry point that turns a reported receipt into a WooCommerce order. Worth understanding if you’re tracing the pipeline, though most developers won’t subscribe to it directly.

Each hook has its own reference article — see Related articles.


Security note

Worth knowing plainly: the plugin does not call Apple’s verifyReceipt or Google Play’s billing API. It trusts the product SKU and expiration timestamp the app reports at face value — which means a forged request really can create a WooCommerce order. That said, Apple and Google are still the real source of truth here: a forged order doesn’t unlock anything inside their systems, only inside WordPress. So never grant an out-of-band entitlement — a refund, a partner perk, external API access — just because a WooCommerce order exists.


Where to go next

  • Recipe: Configure IAP settings — every field on the General Settings tab.
  • Recipe: Create an IAP product — the full product build, with the SKU and Duration gotchas.
  • Recipe: Use the single-screen IAP UI — the feature-list paywall presentation.
  • Recipe: Bypass IAP for a specific user — exempt testers and comped accounts.
  • Plugin: mam-inapp-purchase-manager — the full plugin reference and mobile JSON contract.
  • Hook: mam_iap_require_iap · Hook: mam_iap_active_subscriber_product · Hook: mam_iap_purchase
Was this article helpful?
Contents

    Need Support?

    Can't find the answer you're looking for? Don't worry we're here to help!