Skip to content

Search the documentation by title, section, or page text.

Validate Google Play Store Purchase

POST https://api.botsi.com/v2/purchases/play-store/validate

Call this after a purchase completes in your app. It confirms the transaction with Google Play and records the revenue against the Profile and the Paywall that produced it.

Identify the user with profileId.

Request body#

ParameterTypeRequiredDescription
profileIdstringRequiredBotsi profile ID from Create a Profile
productIdstringRequiredMust match the Play Store Product ID configured in Botsi. Use sourceProductId from the Products response
tokenstringRequiredThe token uniquely identifying this purchase for an item and user pair
placementIdstringOptionalThe Placement used to fetch the Paywall
paywallIdnumberOptionalThe internal Paywall ID, from data.id on the Fetch Paywall response
abTestIdnumberOptionalThe A/B test that produced the Paywall
isExperimentbooleanOptionalThe value Fetch Paywall returned
aiPricingModelIdnumberOptionalThe model ID Fetch Paywall returned
environmentstringOptionalproduction for live apps, sandbox for testing. Read the warning below
isSubscriptionbooleanOptionalWhether the purchase is a subscription
subscriptionOfferDetailsobjectOne of the twoFor a subscription. From ProductDetails.getSubscriptionOfferDetails(), usually the first item
oneTimePurchaseOfferDetailsobjectOne of the twoFor a one-time purchase

isExperiment and aiPricingModelId are optional on the v2 API, though the SDK surface requires them.

Send one offer object, never both

subscriptionOfferDetails and oneTimePurchaseOfferDetails are mutually exclusive. Which one you send depends on whether you are validating a subscription or a one-time purchase.

Like the Apple endpoint, this one does not accept paywallSessionId. Keep passing paywallId, isExperiment, and aiPricingModelId.

subscriptionOfferDetails#

ParameterTypeDescription
basePlanIdstringThe base plan ID for the subscription Product
offerIdstringThe offer ID, if the Product has an offer configured in the store
pricingPhasesarrayThe pricing phases for the subscription

Each entry in pricingPhases:

ParameterTypeDescription
priceAmountMicrosnumberPrice in micro-units. 1,000,000 micro-units is one unit of currency
currencyCodestringISO 4217 currency code
billingPeriodstringThe billing period, in ISO 8601 format
recurrenceModenumberThe recurrence mode of the phase
billingCycleCountnumberHow many cycles the period applies for

oneTimePurchaseOfferDetails#

ParameterTypeDescription
priceAmountMicrosnumberPrice in micros, for example 9990000 for $9.99
currencyCodestringISO 4217 code, for example USD

Example requests#

Which offer object you send depends on what the user bought. A subscription with an introductory free trial carries two pricing phases: the zero-priced trial, then the phase that charges once it ends.

curl -X POST "https://api.botsi.com/v2/purchases/play-store/validate" \
  -H "Authorization: $BOTSI_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "algojlnkodhhjceoicbpjngh.AO-J1Owvcga8j2uUoKEpn5AnvpSFr8dxEFm0c6qk5",
    "placementId": "fresh_placement",
    "productId": "monthly",
    "paywallId": 569,
    "isExperiment": true,
    "aiPricingModelId": 32,
    "environment": "production",
    "profileId": "55bbd5f3-6de8-4d3b-8a55-006e6af6e3dd",
    "subscriptionOfferDetails": {
      "basePlanId": "1month-sub",
      "pricingPhases": [
        {
          "priceAmountMicros": 359990000,
          "currencyCode": "UAH",
          "billingPeriod": "P1M",
          "recurrenceMode": 1,
          "billingCycleCount": 0
        }
      ]
    }
  }'

Finding the token#

The purchase token comes from getPurchaseToken() in the Google Play Billing Library. Google's own documentation on verifying purchases before granting entitlements covers where it appears in the billing flow.

Response#

A successful validation returns the updated Profile, including its Access Levels, subscriptions, and total revenue. Read state and accessLevels to decide what the user now has access to.

{
  "ok": true,
  "data": {
    "profileId": "55bbd5f3-6de8-4d3b-8a55-006e6af6e3dd",
    "appUserId": "user-123",
    "state": "subscribed",
    "totalRevenueUsd": 8.72,
    "accessLevels": {},
    "subscriptions": {},
    "nonSubscriptions": {}
  }
}

accessLevels, subscriptions, and nonSubscriptions are keyed objects, empty when the Profile has no entries of that kind. The shape matches Validate Apple Store Purchase, so a single handler can process either store's response.