← All @molecule/* packages · App templates
@molecule/api-payments-googleProvider bond · payments · API (Node) · v1.0.1 · Apache-2.0
Google Play In-App Purchase provider for molecule.dev.
npm install @molecule/api-payments-googlenpm · Source on GitHub · Implements @molecule/api-payments
@molecule/api-payments-google is a provider bond on the API (Node) side: it implements the payments core interface (@molecule/api-payments) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
Works with: @molecule/api-bond, @molecule/api-i18n, @molecule/api-secrets
Secrets: GOOGLE_API_SERVICE_KEY_OBJECT, GOOGLE_PLAY_PACKAGE_NAME
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Google Play In-App Purchase provider for molecule.dev.
Handles verification of Google Play purchases and subscriptions.
provider
npm install @molecule/api-payments-google @googleapis/androidpublisher @molecule/api-bond @molecule/api-i18n @molecule/api-payments @molecule/api-secrets
NormalizedPurchaseNormalized purchase information (for one-time purchases).
interface NormalizedPurchase {
/**
* The payment provider.
*/
provider: PaymentProviderName
/**
* The purchase/transaction ID.
*/
purchaseId: string
/**
* The product ID.
*/
productId: string
/**
* Whether the purchase is valid.
*/
isValid: boolean
/**
* When the purchase was made (Unix timestamp in ms).
*/
purchaseDate: number
/**
* Raw data from the provider.
*/
rawData: unknown
}
NormalizedSubscriptionNormalized subscription information.
Use this interface to abstract away provider-specific differences.
interface NormalizedSubscription {
/**
* The payment provider.
*/
provider: PaymentProviderName
/**
* The subscription ID from the provider.
*/
subscriptionId: string
/**
* The product/plan ID.
*/
productId: string
/**
* Current subscription status.
*/
status: SubscriptionStatus
/**
* Whether the subscription is currently active.
*/
isActive: boolean
/**
* When the current period started (Unix timestamp in ms).
*/
currentPeriodStart?: number
/**
* When the current period ends (Unix timestamp in ms).
*/
currentPeriodEnd?: number
/**
* Whether the subscription will auto-renew.
*/
willRenew?: boolean
/**
* When the subscription was canceled (if applicable).
*/
canceledAt?: number
/**
* Raw data from the provider.
*/
rawData: unknown
}
PaymentProviderPayment provider bond interface.
Each payment provider implements the methods relevant to its platform. All methods are optional since different platforms use different flows.
type PaymentProvider = PaymentProviderInterface
SubscriptionStatusSubscription status across providers.
type SubscriptionStatus =
'active' | 'canceled' | 'expired' | 'past_due' | 'trialing' | 'paused' | 'pending' | 'unknown'
acknowledgeProduct(productId, purchaseToken)Acknowledges a Google Play one-time product purchase. Required by Google to confirm that the server has processed the purchase; unacknowledged purchases are refunded after 3 days.
function acknowledgeProduct(productId: string, purchaseToken: string): Promise<void>
productId — The Google Play product ID for the one-time purchase.purchaseToken — The purchase token received from the Google Play client.acknowledgeSubscription(productId, purchaseToken)Acknowledges a Google Play subscription purchase. Required by Google to confirm that the server has processed the purchase; unacknowledged purchases are refunded after 3 days.
function acknowledgeSubscription(productId: string, purchaseToken: string): Promise<void>
productId — The Google Play subscription ID used as subscriptionId in the API call.purchaseToken — The purchase token received from the Google Play client.isSubscriptionActive(subscription)Checks whether a Google Play subscription is currently active by comparing its
subscriptionState against SUBSCRIPTION_STATE_ACTIVE and SUBSCRIPTION_STATE_IN_GRACE_PERIOD.
function isSubscriptionActive(
subscription: androidpublisher_v3.Schema$SubscriptionPurchaseV2 | null,
): boolean
subscription — The Google Play SubscriptionPurchaseV2 object, or null.Returns: true if the subscription state is active or in a grace period.
normalizeSubscription(subscription, productId)Normalizes a Google Play SubscriptionPurchaseV2 to the provider-agnostic NormalizedSubscription interface.
Maps Google's subscriptionState enum to standard status values and extracts period/renewal info from lineItems[0].
function normalizeSubscription(
subscription: androidpublisher_v3.Schema$SubscriptionPurchaseV2,
productId: string,
): NormalizedSubscription
subscription — The raw Google Play subscription purchase object.productId — The product ID to include in the normalized result (not present in the subscription data itself).Returns: A NormalizedSubscription with provider set to 'google' and dates converted to millisecond timestamps.
verifyProduct(productId, purchaseToken)Verifies a Google Play one-time (non-subscription) product purchase.
function verifyProduct(
productId: string,
purchaseToken: string,
): Promise<androidpublisher_v3.Schema$ProductPurchase | null>
productId — The Google Play product ID for the one-time purchase.purchaseToken — The purchase token received from the Google Play client.Returns: The raw ProductPurchase data from Google.
verifySubscription(productId, purchaseToken)Verifies a Google Play subscription purchase via the Android Publisher API v3
(purchases.subscriptionsv2.get, returning a SubscriptionPurchaseV2).
function verifySubscription(
productId: string,
purchaseToken: string,
): Promise<androidpublisher_v3.Schema$SubscriptionPurchaseV2 | null>
productId — The Google Play subscription product ID (used for context; the token is the lookup key).purchaseToken — The purchase token received from the Google Play client.Returns: The raw SubscriptionPurchaseV2 data from Google.
paymentProviderPaymentProvider-compatible object for Google Play purchases.
const paymentProvider: PaymentProviderInterface
paymentsGoogleSecretDefinitionsSecret definitions required by the Google Play payments bond.
const paymentsGoogleSecretDefinitions: SecretDefinition[]
androidpublisher_v3Implements @molecule/api-payments interface.
Setup function to register this provider with the bond system:
import { bond } from '@molecule/api-bond'
import { paymentProvider } from '@molecule/api-payments-google'
export function setupPaymentsGoogle(): void {
bond('payments', 'google', paymentProvider)
}
Peer dependencies:
@molecule/api-bond ^1.0.1@molecule/api-i18n ^1.0.1@molecule/api-payments ^1.0.1@molecule/api-secrets ^1.0.1GOOGLE_API_SERVICE_KEY_OBJECT (required) — Google service account key (JSON)
{"type":"service_account",...}GOOGLE_PLAY_PACKAGE_NAME (required) — Google Play package name
com.example.app@googleapis/androidpublisher@molecule/api-bond@molecule/api-i18n@molecule/api-payments@molecule/api-secretsparseNotification handles all three RTDN kinds — subscriptionNotification
(the primary flow: verified via purchases.subscriptionsv2.get, mapped to
renewed/canceled/expired/etc.), oneTimeProductNotification (verified via
purchases.products.get, mapped to purchased/canceled — no expiresAt is
ever set, since a one-time purchase doesn't expire; a generic subscription-plan
handler correctly no-ops on 'purchased', so apps selling one-time products
must handle that type explicitly), and voidedPurchaseNotification (a
refund/chargeback for EITHER kind — mapped to refund; NOT re-verified against
Google's API, since the payload carries no product id to verify against, only
used to look up an EXISTING payment record by transactionId/orderId and
revoke it, so a forged one can only no-op, never grant). testNotification
(Play Console's "Send test notification") logs at info and returns null.
A missing GOOGLE_API_SERVICE_KEY_OBJECT/GOOGLE_PLAY_PACKAGE_NAME is NOT
the same as "invalid purchase." Both throw a tagged config-not-configured
error; verifyPurchase on {@link paymentProvider} detects that tag
(isConfigNotConfiguredError from @molecule/api-payments) and RETHROWS it
instead of swallowing it into the same null a genuine bad-token verification
returns — so a caller can tell "the operator forgot to set the secret" apart
from "this purchase isn't valid" and surface the actionable 503.
Integration checklist — drive the real UI (live preview, no mocks; use the provider's TEST mode — test cards/sandbox accounts, never a live charge), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
read_activity tool (filter type 'webhook'); never
mock the event or modify production code to fake an entitlement.Translation strings are provided by @molecule/api-locales-payments-google.