# Connect Google Play Play works differently from Stripe and Apple — a service-account key and a notification topic instead of an OAuth click. Set it up once, then add the one Play Billing line that says who's buying. Source: https://cross-deck.com/university/connect-google-play/ Verified Crossdeck University lesson — prose plus real, runnable code. ## Play's model: a service account and a notification topic Each rail has its own platform-side shape, and knowing Play's up front prevents the most common "why isn't this working" question. Stripe connects with an OAuth click; Apple uploads .p8 keys; Google Play uses a service-account JSON key you upload, plus a Pub/Sub topic that Google publishes purchase notifications to. One honest difference to expect: Play has no bulk history. Stripe lets Crossdeck list your past subscriptions; Google's stance is that a subscription belongs to the user-and-purchase-token relationship, not to an enumerable list, so Crossdeck can only fetch a purchase once it has a token to ask about. That means Play starts from a clean slate and fills in as purchases happen — expected, not a bug. ## Service account, notifications, then one line at purchase The dashboard walks you through the platform steps; the only code is the last one: 1 · Create a service account in Google Cloud, download its JSON key, and grant it access in Play Console (Users & permissions → the service account's client_email). Then upload the JSON in the Crossdeck dashboard. The key is a credential — Crossdeck stores it in Secret Manager. 2 · Wire Real-time Developer Notifications. In Play Console, point your app's RTDN topic at the Pub/Sub topic Crossdeck owns. Crossdeck receives Google's purchase notifications there. 3 · Tell Crossdeck who's buying. At purchase time, set the obfuscated account ID on the Play Billing flow to your user's ID — the same value you pass to identify(): ```kotlin // the same stable user ID you pass to identify() val params = BillingFlowParams.newBuilder() .setProductDetailsParamsList(/* … */) .setObfuscatedAccountId(currentUserId) .build() billingClient.launchBillingFlow(activity, params) ``` ## The obfuscated account ID is Android's identity hint setObfuscatedAccountId is Android's equivalent of Apple's appAccountToken — it stamps your user's ID onto the purchase. When Google's notification lands, Crossdeck re-fetches the purchase from the Play Developer API, reads the obfuscated account ID off it, and links the resulting customer to your existing developer user. Skip it and the Play purchase still arrives, but with no hint of who bought it, so it attaches to a rail-only customer that has to be reconciled later. Setting it to the value you already pass to identify() is what closes that gap — the same identity, carried through every rail, so a buyer on Android lands on the very same person who bought on the web or in your iOS app. ## The Play purchase lands on the right customer Make a test purchase. Google's notification arrives, Crossdeck re-fetches the purchase, reads the account ID, and the subscription appears on the customer you identified — entitlement active, ready for your gate. google sub · attached user_847's Play subscription, matched by obfuscatedAccountId — entitlement live. Same person, third rail.