Affiliate program for an Android app: a Play install referrer becoming a creator commission

Affiliate Program for an Android App: Click to Payout

Google Play hands your app the string that sent the install. Apple hands you nothing. Building an affiliate program for an Android app is therefore three jobs rather than four: put a creator id in the Play Store URL, read it back on first launch, and bind it to the user id your billing provider reports. The fourth job, reconstructing a click the store refused to carry, is the one iOS cannot escape and Android mostly can. Every Google claim below was checked against Google's own pages on 2026-09-14.

#Why the hard part of iOS is already solved on Android

Because Play carries a value you chose from the click to the installed app, and the App Store carries nothing. That one difference removes the guessing step. On Android the question is what to put in the referrer; on iOS the question is how to live without one.

The Play Store URL takes a referrer query parameter. Google's Play Games user acquisition guide shows the shape, a URL-encoded string appended to a normal listing link:

https://play.google.com/store/apps/details?id=com.sample.package&referrer=utm_source%3Dsearch%26utm_medium%3Dcpc%26utm_campaign%3Dsummerpromo

Swap the marketing parameters for a creator id and the same mechanism is an affiliate link. After the install, the Install Referrer API returns that string to your app, along with the click and install timestamps.

We wrote the iOS half of this problem first, in how to start an affiliate program for an iOS app, and the contrast is the reason both posts exist. Android wins on the install. It ties on everything after it.

The mirror image: one store carries the click, the other drops it.
What reaches your appAndroid, via Google PlayiOS, via the App Store
An id attached to the install itselfThe referrer string you set, returned after installNothing
How long it stays readable90 days, unchanged unless reinstalledNot applicable
ExactnessExact: the value you set is the value you getNo equivalent exists
Direct link into an installed appApp Links, verified by `assetlinks.json`Universal Links
Typed creator codeExact, no store involvementExact, no store involvement
Fallback when the store carries nothingDeferred match on a hashed IP, probabilisticDeferred match on a hashed IP, probabilistic
Store fee on an auto-renewing subscription10% + 5% billing fee, or 15% by marketCovered in the iOS post

#What the install referrer gives a creator program, at program level

An exact creator id on installs that came through a Play link, readable once, on first launch. It is not an attribution system. It is one field you control both ends of, which is more than Apple offers and less than a ledger needs.

Two rules from Google's library guide shape the integration. The information "will be available for 90 days and won't change unless the application is reinstalled", and you "should invoke the API only once during the first execution after install". Read early, store the result, never poll.

We do not bundle the Install Referrer library. You add com.android.installreferrer:installreferrer:2.2 yourself, read the string, and pass it in:

kotlin
MyAppAffiliate.applyInstallReferrer(referrerString)

That call hands the string to the same query-string reader our SDK uses for links, so a referrer of maa_code=JESS20&utm_source=podcast yields the code JESS20, and a tracked link's claim_token yields the token. At most one of the two comes back, which keeps a link that carries both from attributing twice.

The connection lifecycle, the ReferrerDetails fields, the response codes and the install paths that quietly return nothing are a separate subject, written up in the Install Referrer API for affiliate attribution. Read that one before you write the code.

#Where the creator id has to be before the purchase happens

On your server, already bound to the user id your billing provider will report. The referrer solves the install. It does nothing for the subscription, which arrives later as a webhook carrying a user id and a price, from a system that has never heard of the creator.

On Android the first call can be zero lines of Kotlin. Put the key in the manifest and the SDK starts when the process does, using the ContentProvider that Android instantiates before Application.onCreate:

xml
<application>
  <meta-data android:name="com.myappaffiliate.API_KEY" android:value="pk_live_…" />
</application>

That start persists a device id in SharedPreferences, with no advertising id and no fingerprinting, and retries a referral an earlier launch captured but failed to send. Then one call, once you know who the user is:

kotlin
MyAppAffiliate.identify(userId)

The string must be the one your billing provider sends back: RevenueCat's logIn id, Adapty's customer user id, Superwall's app user id, or your Stripe or Paddle custom data. Get it wrong and both halves look healthy forever while no commission is created. That is the most common broken setup we see, on either platform.

There is no third step. You do not set an affiliate attribute on the subscriber; attribution joins on the user id, which is the whole argument of the RevenueCat setup.

#What about installs that never touched a Play link?

They arrive with nothing, and there are more of them than the referrer documentation suggests. Sideloads, other Android stores, preloads, and anyone who watched the creator's video and then typed your app name into Play search all produce an install with no referrer.

Three answers, in the order we recommend reaching for them.

A typed creator code is exact and needs no store cooperation. Ship a "Got a code?" field wired to MyAppAffiliate.applyCode("LUMI"). A code survives a screenshot, a repost that strips your URL, a viewer who installs on a tablet instead, and a Play search. For creator traffic specifically, we think this is the most underrated route on either platform.

App Links catch users who already have the app. A creator link then opens your app directly, and you pass the Intent to MyAppAffiliate.attribute(intent). This needs a verified Digital Asset Links file: Android "queries the corresponding websites for the Digital Asset Links file at https://hostname/.well-known/assetlinks.json", and android:autoVerify="true" on the intent filter. Skip it and codes still work.

The deferred match is the last resort, and it is a guess. On a fresh install with no link and no code, our SDK asks the API to match the install against recent unattributed clicks using a salted hash of the click IP inside a short window. One distinct creator means attribute. Two or more means nothing.

Be clear with yourself about which is which. The referrer is deterministic: the value you put in the URL is the value that comes back. The IP match is probabilistic and fails where you would expect, on office NAT and carrier-grade NAT, and it misses anyone who installs after the window closes. Every row records the method it used. If your dashboard shows mostly IP matches, your codes are not visible enough and your links are not going through Play.

#What Google actually takes, and what that does to "twenty percent"

Not one number, and Google says so first: "There isn't a single service fee as Google Play takes into account that developers operate in different situations." For a subscription app, two entries in the service fees table matter, both verified on 2026-09-14.

For transactions with users in the EEA, UK or US starting 30 June 2026, auto-renewing subscriptions are listed at 10% plus a 5% billing fee, for new and existing installs alike, on the first $1M of annual earnings and on the standard row. The billing fee applies when the purchase goes through Google Play Billing. For all remaining markets, until the updated fees roll out globally, the table lists 15% for automatically renewing subscription products purchased by subscribers, regardless of revenue earned by the developer each year.

So a subscription app selling through Play Billing keeps 85 cents on the dollar in both regimes today, assembled differently. Note what the table does not contain: no rate that steps at the subscriber's first anniversary. Apple's does, as the iOS post sets out, which is why a net-based commission drifts there and holds still here.

On a $9.99 monthly plan, you receive $8.49 (9.99 x 0.85 = 8.4915, truncated to the cent). Here is what the base choice costs you at a 20% rate.

StructureCreator earns on one $9.99 monthForecastableThe Android catch
Flat bounty per paying installA fixed amount, onceYesThe referrer makes bulk attempts cheap, because one URL scales to as many device ids as someone can create
20% of gross$2.00YesThat is 23.6% of the $8.49 you actually keep
20% of net after Play's fee$1.70YesRevenueCat's `commission_percentage` is an estimate and can be null, so keep a configured fallback
20% of net, capped at 12 periods$1.70 per period, 12 at mostYesThe cap must live on the commission row, not on the creator record
Tiered by creator volumeRate rises at thresholdsNoA retroactive tier change rewrites a ledger you already paid out

We default to 20% of net, capped at 12 billed periods, behind a 14 day hold. The cap turns an open-ended liability into a number you can write on one line. The base question in full is in net versus gross on the app stores, and the structure argument in affiliate commissions and payouts.

#Which billing events move a creator's balance, and what a refund does

Four events matter and the rest are noise. A first payment and a renewal create commission. A trial start is worth zero. A refund reverses. Cancellations and expirations change nothing, because the charges that already happened still stand.

RevenueCat sends no event called REFUND. A Play refund arrives as CANCELLATION carrying cancel_reason: CUSTOMER_SUPPORT, which RevenueCat's event reference documents as covering the case where "a Google Play subscription was refunded through RevenueCat". Key a mapping on a REFUND event and that branch never fires: every refund is filed as ordinary churn and the creator keeps money you no longer have.

One asymmetry is Android-specific. RevenueCat's REFUND_REVERSED event is listed for the App Store only, with Google Play marked unsupported. If Google reverses a refund on Play, nothing reaches your ledger and the commission stays reversed until somebody fixes it by hand.

Google's own rules set the window you are insuring against. Google "doesn't give refunds for most Google Play purchases", but within 48 hours a user "may be able to get a refund depending on the details of the purchase", and after 48 hours the refund policy sends them to the developer, who "can process refunds according to their policies and applicable laws".

The number that should set your hold period sits on Play Console's order processing page: "Any orders processed, refunded, or charged-back from the 1st of a given month to the end of the month will get paid out around the 15th of the following month." Google pays you in arrears, netting the month's refunds first. Pay a creator faster than that and you are financing the program from your own balance, then chasing the difference. Raising the hold from its 14 day default to 30 is not caution here, it is matching your outflow to your inflow.

#A worked example on a $9.99 plan, with one refund

One subscriber, 20% of net, a 30 day hold raised from the 14 day default, four charges and one refund. The creator ends with $5.10 and you keep $20.37. Our engine computes in integer cents, as amount_usd_cents x rate_bps / 10000, with the rate snapshotted onto the row so a later rate change cannot rewrite history.

Per charge: Google takes 15%, you receive 849 cents, and 849 x 2000 / 10000 = 169.8, so $1.70. On gross it would be 999 x 2000 / 10000 = 199.8, or $2.00, which is 23.6% of what you keep rather than 20%.

Four charges, one refund inside the hold, and the balance that survives.
DateEventGrossYour netCommissionCreator balance
1 Mar`INITIAL_PURCHASE`$9.99$8.49+$1.70 pending$1.70
31 MarHold expires, no webhookMatures$1.70
1 Apr`RENEWAL`$9.99$8.49+$1.70 pending$3.40
1 May`RENEWAL`$9.99$8.49+$1.70 pending$5.10
12 May`CANCELLATION`, `cancel_reason: CUSTOMER_SUPPORT`-$9.99-$8.49-$1.70$3.40
1 Jun`RENEWAL`$9.99$8.49+$1.70 pending$5.10

Three charges survive, so three commissions do: 3 x $1.70 = $5.10. You keep 3 x $8.49 = $25.47, of which $20.37 stays with you.

Watch the dates rather than the totals. The 1 May charge is refunded on 12 May, so the commission it created is still pending and simply flips status. Google settles May's orders and May's refunds together around 15 June, so that charge never reaches your bank as revenue either. The hold and the payout schedule line up, which is the point.

Run the same subscriber for three years and the Android number stays at $1.70 a month, because Play's subscription rate does not step at the anniversary. The iOS equivalent moves when Apple's does, which is one more reason the two programs need one ledger and not two. Windows and late conversions are their own subject, in how attribution windows work.

#What has to exist before you pay anybody

Six things, and only one is code: written terms, an approval step, a hold period, a statement a creator can read without asking you, a payout rail, and a record of what you sent. Self-referral and bidding on your brand name are the two exclusions to write down on day one, before anyone tests them.

Payout rails are the boring part. Our customers use PayPal and Wise for cross-border creators and a plain bank transfer when everyone shares a country. We do not move money: the export matures the due commissions, records a payout, marks them paid and returns a CSV that reconciles to the total, which is the input to payout automation.

Then the part that is not ours to answer. Creators are usually engaged as independent contractors rather than employees, and what a payer has to collect or report depends on your country, the creator's country and the amounts. Programs commonly collect a tax form before the first payment. That is common practice, not a statement about your obligations, and nothing here is tax or legal advice. Ask an accountant before the first payout.

Four things break Android programs around month six, in our experience of cleaning them up:

  • The referrer is read too late. It lasts 90 days, but the app has to ask, and an app that asks on a screen most users never reach attributes a fraction of what it could.
  • A code and a referrer both attribute one install, from a creator who published both, so the ledger shows two sources for one device and nobody trusts it.
  • Rates stored on the creator instead of snapshotted on the commission, so raising a rate silently repays history.
  • No hold period, which turns every refund into a recovery email and costs you the creator as well as the money.

#The Android setup, in order

Twelve steps, assuming the app ships on Play and bills through RevenueCat, Adapty, Superwall, Stripe or Paddle. Steps 8 and 11 are the ones that catch everything above.

  1. Fix the offer before you touch Gradle. Rate, base, window, hold, cap. Five numbers.
  2. Write the terms on one page, with the two exclusions and what a refund does.
  3. Add the SDK: implementation("com.myappaffiliate:sdk:0.2.0"), a plain Kotlin library with no Play Services dependency and minSdk 24.
  4. Put the API key in the manifest and let the SDK start itself, or call start from Application.onCreate if your key comes from a secret store.
  5. Call identify(userId) with the exact string your billing provider reports back.
  6. Register the billing webhook and paste the signing secret.
  7. Ship the "Got a code?" field wired to applyCode. This is the route that needs nothing from Google.
  8. Run one real purchase end to end on a test track and confirm a single commission row, at the rate and the creator you expect.
  9. Add the Install Referrer library and pass the string to applyInstallReferrer on first launch, once.
  10. Verify assetlinks.json so creator links open the installed app instead of bouncing to the browser.
  11. Recruit five creators, not fifty, and pay the first round by hand against the Play payout report.
  12. Only then change the rate. Cheap now, expensive once two hundred creators have terms.

#Where MyAppAffiliate fits

We sit between the click and the payout, and we are specific about which parts are exact. The Android SDK is one manifest line and one call, plus two optional extras: a creator code field and the install referrer string. Revenue from RevenueCat, Adapty, Superwall, Stripe or Paddle normalizes to one event vocabulary, and a commission is created pending with its rate snapshotted, maturing after the hold so a refund inside the window never pays out.

Two things we do not do. We read no advertising id and fingerprint nothing, which is why the deferred match will always be the weakest route in the stack rather than a secret weapon. And we do not send money; we produce the ledger and the export your bank or PayPal acts on.

Pricing is a flat monthly fee with no share of tracked revenue, so flat pricing means a creator program that works does not quietly raise its own bill.

Does Google Play come with an affiliate program?

No. Play gives you a `referrer` parameter on the store URL and returns its value to your app after the install. Play Console reports installs and revenue. Nothing in either place knows a creator exists, computes a rate, or pays anyone.

What happens if the user does not install from a Play link?

Then no referrer exists. Sideloads, other Android stores, preloads and installs from an untracked Play search all arrive with nothing attached. A typed creator code covers those exactly, and our deferred IP match covers some of them approximately.

What is Google's cut on an auto-renewing subscription?

It depends on the market. For EEA, UK and US transactions from 30 June 2026, Play's [fee table](https://support.google.com/googleplay/android-developer/answer/112622) lists 10% plus a 5% billing fee for auto-renewing subscriptions on new and existing installs. Remaining markets list 15%. Verified 2026-09-14.

How long can I wait before reading the install referrer?

Google says the information is available for 90 days and will not change unless the app is reinstalled, and advises calling the API only once, on the first run after install. Read it on first launch and store the result yourself.

If Google reverses a refund, does the commission come back?

Not on its own. RevenueCat's `REFUND_REVERSED` event is listed for the App Store only, so a reversed Google Play refund sends nothing. We treat that as a manual adjustment and check reversals against the Play payout report each month.

#Ship the code field, then the referrer

Put the "Got a code?" field in front of your first five creators this week. It is exact, it needs nothing from Google, and it will carry the program while you wire the referrer properly behind it. The manifest line and the two calls are in the Android SDK documentation, and the referrer itself is a one-evening job once you have read the Install Referrer API post.