@onesub/server 0.18.1 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store.ts","../src/providers/apple-root-ca.ts","../src/logger.ts","../src/http.ts","../src/cache.ts","../src/providers/mock.ts","../src/providers/apple.ts","../src/providers/google.ts","../src/errors.ts","../src/apps.ts","../src/routes/validate.ts","../src/routes/status.ts","../src/webhook-events.ts","../src/routes/webhook-apple.ts","../src/routes/webhook-google.ts","../src/routes/webhook.ts","../src/routes/purchase.ts","../src/routes/entitlements.ts","../src/routes/secret-compare.ts","../src/routes/admin.ts","../src/routes/metrics.ts","../src/routes/apple-offer.ts","../src/stores/schema.ts","../src/stores/postgres.ts","../src/stores/redis.ts","../src/webhook-queue.ts","../src/openapi.ts","../src/tracing.ts","../src/index.ts"],"names":["err","MOCK_RECEIPT_PREFIX","createHash","SUBSCRIPTION_STATUS","X509Certificate","decodeJwt","decodeProtectedHeader","importX509","jwtVerify","importPKCS8","SignJWT","randomUUID","createSign","ONESUB_ERROR_CODE","z","Router","ROUTES","NO_SUB","response","createRemoteJWKSet","existing","notificationType","purchaseToken","PURCHASE_TYPE","timingSafeEqual","ADMIN_SECRET_HEADER","filtered","raws","createRequire","express","DEFAULT_PORT"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { SubscriptionInfo, PurchaseInfo, SubscriptionStatus, Platform } from '@onesub/shared';\n\n/** Filter options for SubscriptionStore.listFiltered. All fields optional. */\nexport interface ListFilteredOptions {\n userId?: string;\n status?: SubscriptionStatus;\n productId?: string;\n platform?: Platform;\n /** Max items to return. Defaults to 50, capped at 200 by the route handler. */\n limit?: number;\n /** Pagination cursor. Defaults to 0. */\n offset?: number;\n}\n\n/** Result of SubscriptionStore.listFiltered. */\nexport interface ListFilteredResult {\n items: SubscriptionInfo[];\n /** Total matches before limit/offset — used for pagination UI. */\n total: number;\n limit: number;\n offset: number;\n}\n\n/**\n * Pluggable subscription store interface.\n * Default is in-memory. Replace with a PostgreSQL/Redis implementation\n * by passing `store` in OneSubServerConfig.\n */\nexport interface SubscriptionStore {\n save(sub: SubscriptionInfo): Promise<void>;\n /**\n * Returns the most recent subscription for the user, or null if none exist.\n * Use this for legacy single-product checks (`/onesub/status`); for\n * entitlements that span multiple productIds, prefer `getAllByUserId`.\n */\n getByUserId(userId: string): Promise<SubscriptionInfo | null>;\n getByTransactionId(txId: string): Promise<SubscriptionInfo | null>;\n /**\n * Returns every subscription record in the store. Used by metrics\n * aggregation. Hosts shouldn't expose this through unauthenticated routes —\n * the built-in `/onesub/metrics/*` endpoints gate it behind `adminSecret`.\n */\n listAll(): Promise<SubscriptionInfo[]>;\n /**\n * Filtered, paginated subscription list. Used by the dashboard's\n * subscriptions page and any admin tool that needs to enumerate records\n * without pulling the entire table.\n *\n * Filter semantics: each non-undefined field is an AND condition.\n * Sorting: most-recently-updated first (PostgresStore uses `updated_at DESC`;\n * InMemoryStore approximates via insertion order with newest at the front).\n */\n listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult>;\n /**\n * Returns every subscription record for the user (across all productIds),\n * ordered most-recent-first. Used by entitlement evaluation, which needs to\n * see all of a user's active subscriptions to decide whether any of them\n * grants the requested entitlement.\n *\n * Implementations: in-memory currently coalesces by userId so this returns\n * at most one row; Postgres returns the full history.\n */\n getAllByUserId(userId: string): Promise<SubscriptionInfo[]>;\n}\n\n/**\n * In-memory implementation — suitable for development and testing.\n * Data is lost on process restart.\n */\nexport class InMemorySubscriptionStore implements SubscriptionStore {\n // Multiple records per user (different originalTransactionIds) — required\n // for entitlement evaluation across multiple productIds. Ordering is\n // last-written-first so getByUserId returns \"most recent\" naturally.\n private readonly byUserId = new Map<string, SubscriptionInfo[]>();\n private readonly byTransactionId = new Map<string, SubscriptionInfo>();\n\n async save(sub: SubscriptionInfo): Promise<void> {\n // If this transaction was previously bound to a different userId (the\n // validate route rebinds ownership on re-validation), remove the stale\n // copy from the old user's index — otherwise webhooks (which update by\n // transactionId) can never touch it and the old user keeps a frozen\n // 'active' record forever.\n const prev = this.byTransactionId.get(sub.originalTransactionId);\n if (prev && prev.userId !== sub.userId) {\n const oldList = (this.byUserId.get(prev.userId) ?? []).filter(\n (s) => s.originalTransactionId !== sub.originalTransactionId,\n );\n if (oldList.length) this.byUserId.set(prev.userId, oldList);\n else this.byUserId.delete(prev.userId);\n }\n\n this.byTransactionId.set(sub.originalTransactionId, sub);\n\n const existing = this.byUserId.get(sub.userId) ?? [];\n // Replace any prior record with the same originalTransactionId, then\n // unshift to the front so the latest write is index 0 (= getByUserId result).\n const filtered = existing.filter((s) => s.originalTransactionId !== sub.originalTransactionId);\n filtered.unshift(sub);\n this.byUserId.set(sub.userId, filtered);\n }\n\n async getByUserId(userId: string): Promise<SubscriptionInfo | null> {\n const list = this.byUserId.get(userId);\n return list?.[0] ?? null;\n }\n\n async getAllByUserId(userId: string): Promise<SubscriptionInfo[]> {\n return [...(this.byUserId.get(userId) ?? [])];\n }\n\n async getByTransactionId(txId: string): Promise<SubscriptionInfo | null> {\n return this.byTransactionId.get(txId) ?? null;\n }\n\n async listAll(): Promise<SubscriptionInfo[]> {\n return [...this.byTransactionId.values()];\n }\n\n async listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult> {\n const limit = opts.limit ?? 50;\n const offset = opts.offset ?? 0;\n // Iterate via byUserId so insertion-order is \"newest first\" within each\n // user (matches PostgresStore's `updated_at DESC` semantic). Across users\n // we collect in Map iteration order — stable for a given run.\n const all: SubscriptionInfo[] = [];\n for (const list of this.byUserId.values()) {\n for (const s of list) all.push(s);\n }\n const filtered = all.filter((s) => {\n if (opts.userId && s.userId !== opts.userId) return false;\n if (opts.status && s.status !== opts.status) return false;\n if (opts.productId && s.productId !== opts.productId) return false;\n if (opts.platform && s.platform !== opts.platform) return false;\n return true;\n });\n return {\n items: filtered.slice(offset, offset + limit),\n total: filtered.length,\n limit,\n offset,\n };\n }\n}\n\n/**\n * Pluggable purchase store interface for consumables and non-consumables.\n */\nexport interface PurchaseStore {\n savePurchase(purchase: PurchaseInfo): Promise<void>;\n getPurchasesByUserId(userId: string): Promise<PurchaseInfo[]>;\n getPurchaseByTransactionId(txId: string): Promise<PurchaseInfo | null>;\n /** Returns every purchase record. Used by metrics aggregation; admin-gated. */\n listAll(): Promise<PurchaseInfo[]>;\n /** For non-consumables: check if a user has already purchased a product. */\n hasPurchased(userId: string, productId: string): Promise<boolean>;\n /**\n * Admin: delete all purchases matching userId + productId.\n * Used by the admin reset endpoint to allow re-testing non-consumables.\n * Returns the number of rows deleted.\n */\n deletePurchases(userId: string, productId: string): Promise<number>;\n /**\n * Delete a single purchase by its transactionId.\n * Used by the refund/voided-purchase webhook path to revoke entitlement\n * for the exact transaction that was refunded — without touching sibling\n * consumable purchases of the same user/product.\n * Returns true if a row was deleted.\n */\n deletePurchaseByTransactionId(transactionId: string): Promise<boolean>;\n /**\n * Reassign a transaction's owner to a new userId.\n * Used when the validate route encounters TRANSACTION_BELONGS_TO_OTHER_USER\n * for a genuinely-signed JWS — the Apple receipt proves the caller owns the\n * original Apple account, so it's safe to transfer ownership (device\n * reinstall, account migration).\n * Returns true if a row was updated, false if the transactionId was not found.\n */\n reassignPurchase(transactionId: string, newUserId: string): Promise<boolean>;\n}\n\n/**\n * In-memory implementation of PurchaseStore — suitable for development and testing.\n * Data is lost on process restart.\n */\nexport class InMemoryPurchaseStore implements PurchaseStore {\n private readonly byTransactionId = new Map<string, PurchaseInfo>();\n private readonly byUserId = new Map<string, PurchaseInfo[]>();\n\n async savePurchase(purchase: PurchaseInfo): Promise<void> {\n const existing = this.byTransactionId.get(purchase.transactionId);\n if (existing) {\n if (existing.userId !== purchase.userId) {\n const err = new Error('TRANSACTION_BELONGS_TO_OTHER_USER') as Error & { code?: string };\n err.code = 'TRANSACTION_BELONGS_TO_OTHER_USER';\n throw err;\n }\n return; // same user — idempotent\n }\n this.byTransactionId.set(purchase.transactionId, purchase);\n const list = this.byUserId.get(purchase.userId) ?? [];\n list.push(purchase);\n this.byUserId.set(purchase.userId, list);\n }\n\n async getPurchasesByUserId(userId: string): Promise<PurchaseInfo[]> {\n return this.byUserId.get(userId) ?? [];\n }\n\n async getPurchaseByTransactionId(txId: string): Promise<PurchaseInfo | null> {\n return this.byTransactionId.get(txId) ?? null;\n }\n\n async hasPurchased(userId: string, productId: string): Promise<boolean> {\n const purchases = this.byUserId.get(userId);\n if (!purchases) return false;\n return purchases.some((p) => p.productId === productId);\n }\n\n async reassignPurchase(transactionId: string, newUserId: string): Promise<boolean> {\n const existing = this.byTransactionId.get(transactionId);\n if (!existing) return false;\n const oldUserId = existing.userId;\n if (oldUserId === newUserId) return true;\n const updated = { ...existing, userId: newUserId };\n this.byTransactionId.set(transactionId, updated);\n // remove from old userId index\n const oldList = (this.byUserId.get(oldUserId) ?? []).filter((p) => p.transactionId !== transactionId);\n if (oldList.length) this.byUserId.set(oldUserId, oldList);\n else this.byUserId.delete(oldUserId);\n // add to new userId index\n const newList = this.byUserId.get(newUserId) ?? [];\n newList.push(updated);\n this.byUserId.set(newUserId, newList);\n return true;\n }\n\n async deletePurchases(userId: string, productId: string): Promise<number> {\n const list = this.byUserId.get(userId) ?? [];\n const kept = list.filter((p) => p.productId !== productId);\n const deleted = list.length - kept.length;\n this.byUserId.set(userId, kept);\n for (const p of list) {\n if (p.productId === productId) this.byTransactionId.delete(p.transactionId);\n }\n return deleted;\n }\n\n async listAll(): Promise<PurchaseInfo[]> {\n return [...this.byTransactionId.values()];\n }\n\n async deletePurchaseByTransactionId(transactionId: string): Promise<boolean> {\n const existing = this.byTransactionId.get(transactionId);\n if (!existing) return false;\n this.byTransactionId.delete(transactionId);\n const list = (this.byUserId.get(existing.userId) ?? []).filter(\n (p) => p.transactionId !== transactionId,\n );\n if (list.length) this.byUserId.set(existing.userId, list);\n else this.byUserId.delete(existing.userId);\n return true;\n }\n}\n","/**\n * Apple Root CA certificates used to verify StoreKit 2 signed transactions.\n *\n * Apple signs JWS transactions with an ECDSA key; the signing certificate\n * chain is embedded in the JWS `x5c` header. To trust the leaf key we must\n * validate the chain all the way up to one of Apple's published root CAs.\n *\n * Source: https://www.apple.com/certificateauthority/\n * These certificates are public and static. Update this list if Apple\n * publishes a new root — existing JWS signed with older roots continue to\n * validate against their original root.\n */\n\n/**\n * Apple Root CA - G3 (current root for StoreKit 2 / App Store Server API).\n * SHA-256 fingerprint: 63343ABF B89A6A03 EB994052 C5ADBBF8 A87E2C46 81F6F0FC C8A4DA48 95F0FBBF\n * Valid: 2014-04-30 — 2039-04-30\n */\nexport const APPLE_ROOT_CA_G3_PEM = `-----BEGIN CERTIFICATE-----\nMIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwS\nQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u\nIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN\nMTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBS\nb290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y\naXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49\nAgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtf\nTjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517\nIDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySr\nMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gA\nMGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4\nat+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM\n6BgD56KyKA==\n-----END CERTIFICATE-----`;\n\n/**\n * All Apple root CAs accepted for StoreKit 2 JWS validation.\n * Currently only G3; add G4 here when Apple publishes it.\n */\nexport const APPLE_ROOT_CA_PEMS = [APPLE_ROOT_CA_G3_PEM];\n","import type { OneSubLogger } from '@onesub/shared';\n\n/**\n * Process-wide logger used by @onesub/server's providers and routes.\n *\n * Defaults to `console`. `createOneSubMiddleware()` calls `setLogger()` once\n * during setup if `config.logger` is provided. All internal call sites should\n * import `log` from this module instead of calling `console.*` directly so\n * operators can redirect logs (pino / winston / bunyan) with a single config\n * setting.\n */\n\nlet current: OneSubLogger = console;\n\nexport function setLogger(logger: OneSubLogger | undefined): void {\n if (logger) current = logger;\n}\n\nexport const log: OneSubLogger = {\n info: (...args) => current.info(...args),\n warn: (...args) => current.warn(...args),\n error: (...args) => current.error(...args),\n};\n","/**\n * Outbound HTTP helpers shared by Apple/Google providers.\n *\n * Why timeouts: Node's global fetch has no default timeout. If the upstream\n * (Apple App Store Server API, Google Play Developer API, Google OAuth) hangs,\n * webhook handlers and validate routes hang with it — Apple/Google retry on\n * their side, so a hung outbound call cascades into request pile-up here.\n */\n\n/** Default timeout for outbound API calls (10s). */\nexport const DEFAULT_FETCH_TIMEOUT_MS = 10_000;\n\n/**\n * fetch with an `AbortController`-based timeout. Throws an `AbortError`-like\n * Error when the timer fires. Caller is expected to catch and decide\n * (return null, log, propagate as 5xx, etc.).\n */\nexport async function fetchWithTimeout(\n input: RequestInfo | URL,\n init?: RequestInit,\n timeoutMs = DEFAULT_FETCH_TIMEOUT_MS,\n): Promise<Response> {\n const controller = new AbortController();\n // Allow caller-provided signal to compose with our timeout.\n const callerSignal = init?.signal;\n if (callerSignal) {\n if (callerSignal.aborted) controller.abort(callerSignal.reason);\n else callerSignal.addEventListener('abort', () => controller.abort(callerSignal.reason), { once: true });\n }\n\n const timer = setTimeout(() => {\n controller.abort(new Error(`[onesub] fetch timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n try {\n return await fetch(input, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * Pluggable cache adapter.\n *\n * Used internally by the Apple JWT minter, Google OAuth token minter, and any\n * future per-process cache. The default `InMemoryCacheAdapter` matches the\n * pre-adapter behavior — module-level state, lost on restart, not shared\n * between cluster nodes.\n *\n * For multi-instance deployments, pass a `RedisCacheAdapter` in\n * `OneSubMiddlewareConfig.cache` so all nodes share the same JWKS / token\n * cache and avoid every node re-minting on its own schedule.\n */\nexport interface CacheAdapter {\n get<T = unknown>(key: string): Promise<T | null>;\n /** TTL in seconds. Pass 0 / undefined for no expiry. */\n set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void>;\n del(key: string): Promise<void>;\n}\n\ninterface Entry {\n value: unknown;\n /** ms-since-epoch expiry; 0 = never expires */\n expiresAt: number;\n}\n\n/** Sweep expired entries once every this many set() calls. */\nconst SWEEP_EVERY_N_SETS = 256;\n\n/**\n * Process-local in-memory cache. Default for single-instance deployments.\n */\nexport class InMemoryCacheAdapter implements CacheAdapter {\n private readonly entries = new Map<string, Entry>();\n private setsSinceSweep = 0;\n\n async get<T>(key: string): Promise<T | null> {\n const entry = this.entries.get(key);\n if (!entry) return null;\n if (entry.expiresAt !== 0 && entry.expiresAt < Date.now()) {\n this.entries.delete(key);\n return null;\n }\n return entry.value as T;\n }\n\n async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {\n const expiresAt = ttlSeconds && ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : 0;\n this.entries.set(key, { value, expiresAt });\n // Lazy sweep: get() only evicts the key it touches, so write-once-never-\n // read keys (e.g. webhook event dedup markers) would otherwise accumulate\n // forever. Amortized O(1) per set — the O(n) scan runs once per\n // SWEEP_EVERY_N_SETS writes.\n if (++this.setsSinceSweep >= SWEEP_EVERY_N_SETS) {\n this.sweepExpired();\n }\n }\n\n async del(key: string): Promise<void> {\n this.entries.delete(key);\n }\n\n /** Evict every entry whose TTL has elapsed. */\n private sweepExpired(): void {\n this.setsSinceSweep = 0;\n const now = Date.now();\n for (const [key, entry] of this.entries) {\n if (entry.expiresAt !== 0 && entry.expiresAt < now) this.entries.delete(key);\n }\n }\n\n /** Test helper — clear everything. Not part of the public CacheAdapter contract. */\n clear(): void {\n this.entries.clear();\n this.setsSinceSweep = 0;\n }\n\n /** Test helper — number of entries currently held (including not-yet-swept expired ones). */\n get size(): number {\n return this.entries.size;\n }\n}\n\n/**\n * Module-level singleton used by providers when no per-call adapter is\n * supplied. Keeps the existing single-instance behavior with zero config.\n */\nlet defaultAdapter: CacheAdapter = new InMemoryCacheAdapter();\n\nexport function getDefaultCache(): CacheAdapter {\n return defaultAdapter;\n}\n\nexport function setDefaultCache(adapter: CacheAdapter): void {\n defaultAdapter = adapter;\n}\n\n","import { createHash } from 'node:crypto';\nimport type { SubscriptionInfo } from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS, MOCK_RECEIPT_PREFIX } from '@onesub/shared';\nimport { log } from '../logger.js';\n\n/**\n * Mock provider — returns deterministic receipt-validation results based on\n * the receipt string pattern. Used when `config.{apple,google}.mockMode` is\n * true so developers / CI / AI agents can exercise the full onesub flow\n * without App Store Connect or Play Developer API credentials.\n *\n * ──────────────────────────────────────────────────────────────────────\n * Receipt prefix (see `MOCK_RECEIPT_PREFIX` in @onesub/shared)\n * ──────────────────────────────────────────────────────────────────────\n * REVOKED → null (revoked/refunded)\n * EXPIRED → null (> 72h old)\n * INVALID / BAD_SIG → null (signature / integrity fail)\n * NETWORK_ERROR → throws (simulates upstream failure)\n * SANDBOX → valid; subscription has a shorter expiry (~1h)\n * <anything else> → valid; transactionId = sha256(receipt)[:24]\n *\n * transactionId is derived from sha256(receipt) so the same receipt always\n * produces the same id — useful for replay / idempotency testing.\n */\n\nexport type MockReceiptOutcome =\n | { kind: 'valid' }\n | { kind: 'sandbox' }\n | { kind: 'revoked' }\n | { kind: 'expired' }\n | { kind: 'invalid' }\n | { kind: 'network-error' };\n\nexport function classifyMockReceipt(receipt: string): MockReceiptOutcome {\n if (receipt.startsWith(MOCK_RECEIPT_PREFIX.NETWORK_ERROR)) return { kind: 'network-error' };\n if (receipt.startsWith(MOCK_RECEIPT_PREFIX.REVOKED)) return { kind: 'revoked' };\n if (receipt.startsWith(MOCK_RECEIPT_PREFIX.EXPIRED)) return { kind: 'expired' };\n if (\n receipt.startsWith(MOCK_RECEIPT_PREFIX.INVALID) ||\n receipt.startsWith(MOCK_RECEIPT_PREFIX.BAD_SIG)\n ) {\n return { kind: 'invalid' };\n }\n if (receipt.startsWith(MOCK_RECEIPT_PREFIX.SANDBOX)) return { kind: 'sandbox' };\n return { kind: 'valid' };\n}\n\n/**\n * Gate the outcome against `valid` / `sandbox`. Throws for network-error,\n * logs + returns false for rejection cases, returns true for pass-through.\n * Every mock validator uses this so their rejection behavior stays in sync.\n */\nfunction outcomePasses(outcome: MockReceiptOutcome, tag: string): boolean {\n if (outcome.kind === 'network-error') {\n throw new Error(`[onesub/mock/${tag}] simulated upstream network error`);\n }\n if (outcome.kind === 'valid' || outcome.kind === 'sandbox') return true;\n log.warn(`[onesub/mock/${tag}] receipt rejected`, { outcome: outcome.kind });\n return false;\n}\n\nfunction deterministicTransactionId(prefix: string, receipt: string): string {\n const digest = createHash('sha256').update(receipt).digest('hex').slice(0, 24);\n return `${prefix}_${digest}`;\n}\n\n/**\n * Test convention: `...#token=<value>` in a mock receipt surfaces the store's\n * account-binding field (Apple appAccountToken / boundAccountId, Google\n * obfuscatedExternalAccountId), exercising the account-binding guard in the\n * validate routes. Returns undefined when no token is baked into the receipt.\n */\nfunction extractBoundToken(receipt: string): string | undefined {\n return receipt.match(/#token=([^#]+)/)?.[1];\n}\n\nconst HOURS = 60 * 60 * 1000;\nconst DAYS = 24 * HOURS;\n\nexport function mockValidateAppleSubscription(receipt: string): SubscriptionInfo | null {\n const outcome = classifyMockReceipt(receipt);\n if (!outcomePasses(outcome, 'apple')) return null;\n const now = Date.now();\n const expiresAt = outcome.kind === 'sandbox' ? now + 1 * HOURS : now + 30 * DAYS;\n const boundToken = extractBoundToken(receipt);\n return {\n userId: '',\n productId: 'mock_subscription',\n platform: 'apple',\n status: SUBSCRIPTION_STATUS.ACTIVE,\n expiresAt: new Date(expiresAt).toISOString(),\n originalTransactionId: deterministicTransactionId('mock_apple_orig', receipt),\n purchasedAt: new Date(now).toISOString(),\n willRenew: true,\n ...(boundToken ? { boundAccountId: boundToken } : {}),\n };\n}\n\nexport function mockValidateAppleProduct(\n receipt: string,\n expectedProductId?: string,\n): { transactionId: string; productId: string; purchasedAt: string; appAccountToken?: string } | null {\n const outcome = classifyMockReceipt(receipt);\n if (!outcomePasses(outcome, 'apple')) return null;\n const productId = expectedProductId ?? 'mock_product';\n const boundToken = extractBoundToken(receipt);\n return {\n transactionId: deterministicTransactionId(`mock_apple_${productId}`, receipt),\n productId,\n purchasedAt: new Date().toISOString(),\n ...(boundToken ? { appAccountToken: boundToken } : {}),\n };\n}\n\nexport function mockValidateGoogleSubscription(\n receipt: string,\n productId: string,\n): SubscriptionInfo | null {\n const outcome = classifyMockReceipt(receipt);\n if (!outcomePasses(outcome, 'google')) return null;\n const now = Date.now();\n const expiresAt = outcome.kind === 'sandbox' ? now + 1 * HOURS : now + 30 * DAYS;\n const boundToken = extractBoundToken(receipt);\n return {\n userId: '',\n productId,\n platform: 'google',\n status: SUBSCRIPTION_STATUS.ACTIVE,\n expiresAt: new Date(expiresAt).toISOString(),\n // Same keying as production: Google records are keyed by the purchaseToken\n // (the receipt itself) so mock webhooks can find them by token.\n originalTransactionId: receipt,\n purchasedAt: new Date(now).toISOString(),\n willRenew: true,\n ...(boundToken ? { boundAccountId: boundToken } : {}),\n };\n}\n\nexport function mockValidateGoogleProduct(\n receipt: string,\n productId: string,\n): { transactionId: string; purchasedAt: string; obfuscatedExternalAccountId?: string } | null {\n const outcome = classifyMockReceipt(receipt);\n if (!outcomePasses(outcome, 'google')) return null;\n const boundToken = extractBoundToken(receipt);\n return {\n transactionId: deterministicTransactionId(`mock_google_${productId}`, receipt),\n purchasedAt: new Date().toISOString(),\n ...(boundToken ? { obfuscatedExternalAccountId: boundToken } : {}),\n };\n}\n","import { decodeJwt, decodeProtectedHeader, importPKCS8, importX509, jwtVerify, SignJWT } from 'jose';\nimport { X509Certificate, createSign, randomUUID } from 'node:crypto';\nimport type {\n SubscriptionInfo,\n AppleNotificationPayload,\n AppleConsumptionRequest,\n OneSubServerConfig,\n} from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS } from '@onesub/shared';\nimport { APPLE_ROOT_CA_PEMS } from './apple-root-ca.js';\nimport { log } from '../logger.js';\nimport { fetchWithTimeout } from '../http.js';\nimport { getDefaultCache } from '../cache.js';\nimport {\n mockValidateAppleSubscription,\n mockValidateAppleProduct,\n} from './mock.js';\n\ntype AppleConfig = NonNullable<OneSubServerConfig['apple']>;\n\n/**\n * Decoded Apple signed transaction (JWS payload).\n * Mirrors the fields from App Store Server API / StoreKit 2.\n */\ninterface AppleTransactionPayload {\n bundleId?: string;\n productId?: string;\n originalTransactionId?: string;\n transactionId?: string;\n purchaseDate?: number; // ms since epoch\n originalPurchaseDate?: number;\n expiresDate?: number; // ms since epoch\n inAppOwnershipType?: string; // 'PURCHASED' | 'FAMILY_SHARED'\n appAccountToken?: string; // UUID linked at purchase time (= userId for onesub)\n type?: string; // 'Auto-Renewable Subscription' | 'Consumable' | 'Non-Consumable'\n isUpgraded?: boolean;\n revocationDate?: number;\n [key: string]: unknown;\n}\n\n/**\n * Decoded Apple signed renewal info (JWS payload).\n */\ninterface AppleRenewalPayload {\n autoRenewStatus?: number; // 1 = will renew, 0 = canceled\n expirationIntent?: number; // 1=Customer canceled, 2=Billing error, etc.\n originalTransactionId?: string;\n productId?: string;\n [key: string]: unknown;\n}\n\nfunction derBase64ToPem(der: string): string {\n return (\n '-----BEGIN CERTIFICATE-----\\n' +\n (der.match(/.{1,64}/g) ?? []).join('\\n') +\n '\\n-----END CERTIFICATE-----'\n );\n}\n\nconst APPLE_ROOT_CERTS = APPLE_ROOT_CA_PEMS.map((pem) => new X509Certificate(pem));\n\n/**\n * BasicConstraints enforcement for certificates acting as ISSUERS in an x5c\n * chain. Without it, any end-entity (leaf) certificate — which Apple issues\n * WITHOUT CA privileges — could be abused to sign a forged \"leaf\" and splice\n * it into an otherwise signature-valid chain.\n *\n * We enforce `basicConstraints CA=true` via node's `X509Certificate.ca`.\n * The KeyUsage keyCertSign bit is NOT checked because node:crypto does not\n * expose the KeyUsage bit string: `X509Certificate.keyUsage` actually returns\n * the Extended Key Usage OIDs (it reads NID_ext_key_usage), so `.ca` is the\n * only reliably available signal in Node 20/22. `.ca` alone is sufficient to\n * block leaf-as-issuer splicing.\n *\n * The LEAF certificate (index 0) must NOT be passed here — leaves are not\n * CAs and are never issuers.\n *\n * Exported for unit testing.\n */\nexport function assertIssuerCanSign(cert: Pick<X509Certificate, 'ca'>, index: number): void {\n if (!cert.ca) {\n throw new Error(\n `[onesub/apple] cert[${index}] is used as an issuer but is not a CA (basicConstraints CA=false)`,\n );\n }\n}\n\n/**\n * Validate that the x5c chain from the JWS terminates at one of Apple's\n * bundled root CAs. Each cert in the chain must be signed by the next (or\n * the bundled root), every issuing cert must be a CA (BasicConstraints), and\n * all certs must currently be within their validity window.\n *\n * Returns the leaf certificate PEM on success. Throws on any failure.\n */\nfunction verifyAppleCertChain(x5c: string[]): string {\n if (x5c.length === 0) {\n throw new Error('[onesub/apple] empty x5c');\n }\n\n const chain = x5c.map((der) => new X509Certificate(derBase64ToPem(der)));\n const now = new Date();\n\n // Validity window + leaf→intermediate→... signature chain\n for (let i = 0; i < chain.length; i++) {\n const cert = chain[i];\n if (new Date(cert.validFrom) > now || new Date(cert.validTo) < now) {\n throw new Error(`[onesub/apple] cert[${i}] outside validity window`);\n }\n // Every cert above the leaf signs the cert below it → must be a CA.\n if (i >= 1) assertIssuerCanSign(cert, i);\n if (i + 1 < chain.length) {\n if (!cert.checkIssued(chain[i + 1]) || !cert.verify(chain[i + 1].publicKey)) {\n throw new Error(`[onesub/apple] cert[${i}] not signed by cert[${i + 1}]`);\n }\n }\n }\n\n // Final cert in chain (typically intermediate) must be signed by an Apple root.\n // Accept either (a) explicit signature match or (b) the cert itself being one\n // of our bundled Apple roots (happens when Apple embeds the full chain).\n const top = chain[chain.length - 1];\n const topDer = top.raw.toString('base64');\n const trustsRoot = APPLE_ROOT_CERTS.some((root) => {\n if (root.raw.toString('base64') === topDer) return true;\n // A bundled root acting as the issuer of `top` must itself be a CA\n // (always true for Apple's roots; kept as defense in depth).\n if (!root.ca) return false;\n if (!top.checkIssued(root)) return false;\n try { return top.verify(root.publicKey); } catch { return false; }\n });\n if (!trustsRoot) {\n throw new Error('[onesub/apple] cert chain does not terminate at a trusted Apple root');\n }\n\n return chain[0].toString();\n}\n\n/**\n * Decode and verify a StoreKit 2 signed transaction JWS.\n *\n * 1. Extract the x5c certificate chain from the JWS header.\n * 2. Validate the chain terminates at a bundled Apple Root CA (G3).\n * 3. Use the leaf certificate's public key to verify the JWS signature.\n *\n * skipVerification=true skips all of the above and just decodes the payload\n * (dev/test only).\n */\nexport async function decodeJws<T>(jws: string, skipVerification = false): Promise<T> {\n if (skipVerification) {\n if (process.env['NODE_ENV'] === 'production') {\n log.warn(\n '[onesub/apple] WARNING: skipJwsVerification is enabled in production. ' +\n 'JWS signatures are NOT being verified. This is a security risk.',\n );\n }\n return decodeJwt(jws) as T;\n }\n\n const header = decodeProtectedHeader(jws) as { x5c?: string[]; alg?: string };\n const x5c = header.x5c;\n if (!x5c || x5c.length === 0) {\n throw new Error('[onesub/apple] JWS header missing x5c certificate chain');\n }\n\n const leafPem = verifyAppleCertChain(x5c);\n const alg = header.alg ?? 'ES256';\n const key = await importX509(leafPem, alg);\n\n const { payload } = await jwtVerify(jws, key);\n return payload as T;\n}\n\n/**\n * Derive a SubscriptionStatus from the transaction + renewal payloads.\n */\nfunction deriveStatus(\n tx: AppleTransactionPayload,\n renewal: AppleRenewalPayload | null\n): SubscriptionInfo['status'] {\n if (tx.revocationDate) return SUBSCRIPTION_STATUS.CANCELED;\n\n const now = Date.now();\n const expires = tx.expiresDate ?? 0;\n\n if (expires > now) return SUBSCRIPTION_STATUS.ACTIVE;\n\n // Expired — check if it was voluntarily canceled\n if (renewal?.autoRenewStatus === 0) return SUBSCRIPTION_STATUS.CANCELED;\n\n return SUBSCRIPTION_STATUS.EXPIRED;\n}\n\n/**\n * Validate an Apple receipt string.\n *\n * StoreKit 2 receipts are JWS-encoded signed transactions (the `signedTransaction`\n * field from the client). For legacy base64 receipts from StoreKit 1, you would\n * POST to verifyReceipt — that path is left as a stub here because Apple is\n * deprecating it.\n */\nexport async function validateAppleReceipt(\n receipt: string,\n config: AppleConfig\n): Promise<SubscriptionInfo | null> {\n if (config.mockMode) return mockValidateAppleSubscription(receipt);\n let tx: AppleTransactionPayload;\n\n try {\n tx = await decodeJws<AppleTransactionPayload>(receipt, config.skipJwsVerification);\n } catch (err) {\n const preview = receipt.slice(0, 60);\n const parts = receipt.split('.').length;\n log.warn(\n '[onesub/apple] Failed to decode receipt as JWS:',\n (err as Error)?.message ?? err,\n `| preview: \"${preview}...\" (len=${receipt.length}, parts=${parts})`,\n );\n return null;\n }\n\n if (!tx.originalTransactionId || !tx.productId || !tx.expiresDate) {\n return null;\n }\n\n // Validate bundle ID — missing or mismatched both rejected\n if (!tx.bundleId || tx.bundleId !== config.bundleId) {\n log.warn('[onesub/apple] Bundle ID mismatch:', tx.bundleId, '!==', config.bundleId);\n return null;\n }\n\n // Reject Sandbox receipts in production unless explicitly allowed.\n // Set ONESUB_ALLOW_SANDBOX=true to permit TestFlight sandbox receipts on\n // production servers (useful during QA before App Store release).\n if (\n process.env['NODE_ENV'] === 'production' &&\n tx.environment !== 'Production' &&\n process.env['ONESUB_ALLOW_SANDBOX'] !== 'true'\n ) {\n log.warn('[onesub/apple] Sandbox receipt rejected in production:', tx.environment);\n return null;\n }\n\n const status = deriveStatus(tx, null);\n const purchasedAt = tx.originalPurchaseDate ?? tx.purchaseDate ?? Date.now();\n\n return {\n userId: '', // caller fills this in from the request body\n productId: tx.productId,\n platform: 'apple',\n status,\n expiresAt: new Date(tx.expiresDate).toISOString(),\n originalTransactionId: tx.originalTransactionId,\n purchasedAt: new Date(purchasedAt).toISOString(),\n willRenew: status === SUBSCRIPTION_STATUS.ACTIVE, // refined by renewal info in webhook\n ...(tx.appAccountToken ? { boundAccountId: tx.appAccountToken } : {}),\n };\n}\n\n/**\n * Result of validating an Apple consumable or non-consumable receipt.\n */\nexport interface AppleProductResult {\n /** Per-transaction unique ID (transactionId, not originalTransactionId) */\n transactionId: string;\n productId: string;\n purchasedAt: string;\n /**\n * appAccountToken baked into the JWS at purchase time (UUID the client passed\n * to StoreKit). When present it binds the purchase to a stable account\n * identity; the validate route rejects attributing/reassigning the purchase to\n * a userId that doesn't match it. Absent for purchases made before the client\n * started setting appAccountToken (backward-compatible).\n */\n appAccountToken?: string;\n}\n\n/**\n * Validate an Apple StoreKit 2 JWS signedTransaction for a consumable or\n * non-consumable product.\n *\n * Differences from validateAppleReceipt (subscriptions):\n * - Checks tx.type is 'Consumable' or 'Non-Consumable' (not a subscription)\n * - Uses tx.transactionId (per-purchase unique) instead of originalTransactionId\n * - Enforces a 72-hour receipt age limit to block replay attacks\n * - Does NOT check expiresDate (one-time purchases don't expire)\n */\nexport async function validateAppleConsumableReceipt(\n signedTransaction: string,\n config: AppleConfig,\n expectedProductId?: string,\n): Promise<AppleProductResult | null> {\n if (config.mockMode) return mockValidateAppleProduct(signedTransaction, expectedProductId);\n let tx: AppleTransactionPayload;\n\n try {\n tx = await decodeJws<AppleTransactionPayload>(signedTransaction, config.skipJwsVerification);\n } catch (err) {\n const preview = signedTransaction.slice(0, 60);\n const parts = signedTransaction.split('.').length;\n const looksLikeJws = parts === 3;\n log.warn(\n '[onesub/apple] Failed to decode consumable JWS:',\n (err as Error)?.message ?? err,\n `| receipt preview: \"${preview}...\" (len=${signedTransaction.length}, parts=${parts}, looksLikeJws=${looksLikeJws})`,\n );\n return null;\n }\n\n // bundleId must be present and match\n if (!tx.bundleId || tx.bundleId !== config.bundleId) {\n log.warn('[onesub/apple] Bundle ID mismatch:', tx.bundleId, '!==', config.bundleId);\n return null;\n }\n\n // Must be a one-time purchase type (not a subscription)\n if (tx.type !== 'Consumable' && tx.type !== 'Non-Consumable') {\n log.warn('[onesub/apple] Invalid purchase type for product validation:', tx.type);\n return null;\n }\n\n // Reject Sandbox receipts in production unless explicitly allowed.\n // Set ONESUB_ALLOW_SANDBOX=true to permit TestFlight sandbox receipts on\n // production servers (useful during QA before App Store release).\n if (\n process.env['NODE_ENV'] === 'production' &&\n tx.environment !== 'Production' &&\n process.env['ONESUB_ALLOW_SANDBOX'] !== 'true'\n ) {\n log.warn('[onesub/apple] Sandbox receipt rejected in production:', tx.environment);\n return null;\n }\n\n if (!tx.productId) {\n log.warn('[onesub/apple] No productId in transaction');\n return null;\n }\n\n if (expectedProductId && tx.productId !== expectedProductId) {\n log.warn('[onesub/apple] Product ID mismatch:', tx.productId, '!==', expectedProductId);\n return null;\n }\n\n // Reject refunded purchases\n if (tx.revocationDate) {\n log.warn('[onesub/apple] Purchase was revoked/refunded');\n return null;\n }\n\n // Reject receipts older than the configured window (replay attack\n // prevention). Raise productReceiptMaxAgeHours when validating historical\n // receipts on purpose (migrations from another IAP backend, e2e tests).\n const maxAgeMs = (config.productReceiptMaxAgeHours ?? 72) * 60 * 60 * 1000;\n if (tx.purchaseDate && Date.now() - tx.purchaseDate > maxAgeMs) {\n log.warn(`[onesub/apple] Consumable receipt too old (>${config.productReceiptMaxAgeHours ?? 72}h)`);\n return null;\n }\n\n // For consumables, transactionId is per-purchase unique.\n // originalTransactionId is shared across re-purchases, so it must not be used\n // as the deduplication key for consumables.\n const transactionId = tx.transactionId ?? tx.originalTransactionId;\n if (!transactionId) {\n log.warn('[onesub/apple] No transactionId in consumable transaction');\n return null;\n }\n\n return {\n transactionId,\n productId: tx.productId,\n purchasedAt: tx.purchaseDate\n ? new Date(tx.purchaseDate).toISOString()\n : new Date().toISOString(),\n appAccountToken: tx.appAccountToken ?? undefined,\n };\n}\n\n/**\n * Decode an Apple Server Notification V2 payload.\n * Returns the derived subscription update to be merged into the store.\n */\nexport async function decodeAppleNotification(\n payload: AppleNotificationPayload,\n skipJwsVerification = false\n): Promise<{\n originalTransactionId: string;\n /** Per-transaction id (different from originalTransactionId for consumables / re-purchases). */\n transactionId: string | null;\n /** 'Auto-Renewable Subscription' | 'Consumable' | 'Non-Consumable' | 'Non-Renewing Subscription' */\n type: string | null;\n productId: string | null;\n bundleId: string | null;\n /** 'Production' | 'Sandbox' — drives which Apple API host to call back. */\n environment: 'Production' | 'Sandbox';\n status: SubscriptionInfo['status'];\n willRenew: boolean;\n /** May be null for non-subscription notifications (consumable refund). */\n expiresAt: string | null;\n /**\n * UUID the app set via setAppAccountToken at purchase time. For onesub this\n * maps directly to userId. Present for both PURCHASED and FAMILY_SHARED\n * ownership types when the host linked a user account at checkout time.\n * Null when not set (sandbox, or host didn't call setAppAccountToken).\n */\n appAccountToken: string | null;\n /**\n * 'PURCHASED' — the user bought this directly.\n * 'FAMILY_SHARED' — a family member of the purchaser. The originalTransactionId\n * and appAccountToken belong to the family member, not the purchaser.\n */\n inAppOwnershipType: string | null;\n} | null> {\n const { signedTransactionInfo, signedRenewalInfo } = payload.data;\n\n let tx: AppleTransactionPayload;\n let renewal: AppleRenewalPayload | null = null;\n\n try {\n tx = await decodeJws<AppleTransactionPayload>(signedTransactionInfo, skipJwsVerification);\n } catch {\n return null;\n }\n\n try {\n renewal = await decodeJws<AppleRenewalPayload>(signedRenewalInfo, skipJwsVerification);\n } catch {\n // renewal info is optional for some notification types\n }\n\n // expiresDate is only required for subscription notifications.\n // Consumable / non-consumable refund notifications (REFUND for IAP) have no expiry.\n if (!tx.originalTransactionId) return null;\n\n const status = deriveStatus(tx, renewal);\n const willRenew = renewal?.autoRenewStatus === 1;\n\n // environment is on the notification data envelope but Apple also includes\n // it inside the signed transaction payload. Prefer the JWS-protected one.\n const txEnv = (tx as { environment?: string }).environment;\n const dataEnv = payload.data.environment;\n const environment: 'Production' | 'Sandbox' =\n txEnv === 'Sandbox' || dataEnv === 'Sandbox' ? 'Sandbox' : 'Production';\n\n return {\n originalTransactionId: tx.originalTransactionId,\n transactionId: tx.transactionId ?? null,\n type: tx.type ?? null,\n productId: tx.productId ?? null,\n bundleId: tx.bundleId ?? payload.data.bundleId ?? null,\n environment,\n status,\n willRenew,\n expiresAt: tx.expiresDate ? new Date(tx.expiresDate).toISOString() : null,\n appAccountToken: tx.appAccountToken ?? null,\n inAppOwnershipType: tx.inAppOwnershipType ?? null,\n };\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// App Store Server API — outbound calls (consumption response, etc.)\n// ───────────────────────────────────────────────────────────────────────────\n\n/**\n * JWT cache for the App Store Server API. Apple caps token TTL at 20 minutes;\n * we mint with that TTL and re-use until 60 seconds before expiry (so\n * long-running webhook bursts don't pay the ECDSA-sign cost on every request).\n *\n * Cache backend: `getDefaultCache()` (in-memory by default, swappable with\n * Redis / Memcached via `setDefaultCache()`). When the cache is shared across\n * cluster nodes only one node mints per TTL window.\n *\n * Keyed by `${issuerId}|${keyId}` since rotating either invalidates the JWT.\n * Concurrent mints inside a single process are deduped via `appleJwtMintPromises`.\n */\nconst appleJwtMintPromises = new Map<string, Promise<string>>();\n\nconst APPLE_JWT_TTL_MS = 20 * 60 * 1000;\nconst APPLE_JWT_REFRESH_BEFORE_MS = 60 * 1000;\n\n/**\n * Mint a JWT for the App Store Server API (audience `appstoreconnect-v1`).\n * Requires keyId, issuerId, and privateKey (PKCS8 ES256 from App Store Connect).\n * Token TTL is capped at 20 minutes per Apple's spec.\n */\nasync function makeAppleApiJwt(config: AppleConfig): Promise<string> {\n const { issuerId, keyId, privateKey, bundleId } = config;\n if (!issuerId || !keyId || !privateKey) {\n throw new Error('[onesub/apple] App Store Server API requires issuerId, keyId, and privateKey');\n }\n\n const cache = getDefaultCache();\n const cacheKey = `apple:jwt:${issuerId}|${keyId}`;\n const now = Date.now();\n\n const cached = await cache.get<{ token: string; expiresAt: number }>(cacheKey);\n if (cached && cached.expiresAt - now > APPLE_JWT_REFRESH_BEFORE_MS) {\n return cached.token;\n }\n\n const inflight = appleJwtMintPromises.get(cacheKey);\n if (inflight) return inflight;\n\n const promise = (async () => {\n const key = await importPKCS8(privateKey, 'ES256');\n const issuedAt = Math.floor(Date.now() / 1000);\n const token = await new SignJWT({ bid: bundleId })\n .setProtectedHeader({ alg: 'ES256', kid: keyId, typ: 'JWT' })\n .setIssuer(issuerId)\n .setAudience('appstoreconnect-v1')\n .setIssuedAt(issuedAt)\n .setExpirationTime(issuedAt + Math.floor(APPLE_JWT_TTL_MS / 1000))\n .sign(key);\n const expiresAt = Date.now() + APPLE_JWT_TTL_MS;\n // TTL slightly under the 20m spec so cache eviction matches token expiry.\n await cache.set(cacheKey, { token, expiresAt }, 19 * 60);\n return token;\n })().finally(() => {\n appleJwtMintPromises.delete(cacheKey);\n });\n appleJwtMintPromises.set(cacheKey, promise);\n return promise;\n}\n\n/** Test-only: clear the Apple JWT cache. Not exported. */\nfunction clearAppleJwtCacheForTests(): void {\n const cache = getDefaultCache();\n // Best-effort wipe — tests use the in-memory adapter, which exposes clear().\n if ('clear' in cache && typeof (cache as { clear?: () => void }).clear === 'function') {\n (cache as { clear: () => void }).clear();\n }\n appleJwtMintPromises.clear();\n}\n// Expose for the test suite without polluting the public API surface.\nexport const __testing = { clearAppleJwtCacheForTests };\n\n/**\n * PUT a ConsumptionRequest to Apple's\n * /inApps/v1/transactions/consumption/{transactionId} endpoint.\n *\n * Apple sends CONSUMPTION_REQUEST notifications when a customer asks for a\n * refund on a consumable. Without a response Apple has no usage signal and\n * tends to grant the refund. This call provides the data Apple uses to weigh\n * the refund decision.\n *\n * Fire-and-forget: failures are logged, never thrown — the webhook should\n * still 200 to Apple even if our outbound call fails.\n */\nexport async function sendAppleConsumptionResponse(\n transactionId: string,\n body: AppleConsumptionRequest,\n config: AppleConfig,\n options?: { sandbox?: boolean },\n): Promise<void> {\n if (config.mockMode) return;\n\n let jwt: string;\n try {\n jwt = await makeAppleApiJwt(config);\n } catch (err) {\n log.warn('[onesub/apple] Cannot send consumption response — JWT mint failed:', err);\n return;\n }\n\n const host = options?.sandbox\n ? 'api.storekit-sandbox.itunes.apple.com'\n : 'api.storekit.itunes.apple.com';\n const url = `https://${host}/inApps/v1/transactions/consumption/${encodeURIComponent(transactionId)}`;\n\n try {\n const resp = await fetchWithTimeout(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${jwt}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n if (!resp.ok) {\n const text = await resp.text();\n log.warn(`[onesub/apple] Consumption response API error ${resp.status}: ${text}`);\n }\n } catch (err) {\n log.warn('[onesub/apple] Consumption response network error:', err);\n }\n}\n\n/**\n * Apple's status code in the GET /inApps/v1/subscriptions/{originalTxId} response.\n * https://developer.apple.com/documentation/appstoreserverapi/status\n */\nconst APPLE_SUBSCRIPTION_STATUS_CODE = {\n ACTIVE: 1,\n EXPIRED: 2,\n BILLING_RETRY: 3,\n GRACE_PERIOD: 4,\n REVOKED: 5,\n} as const;\n\nfunction mapAppleStatusCode(code: number): SubscriptionInfo['status'] {\n switch (code) {\n case APPLE_SUBSCRIPTION_STATUS_CODE.ACTIVE:\n return SUBSCRIPTION_STATUS.ACTIVE;\n case APPLE_SUBSCRIPTION_STATUS_CODE.GRACE_PERIOD:\n return SUBSCRIPTION_STATUS.GRACE_PERIOD;\n case APPLE_SUBSCRIPTION_STATUS_CODE.BILLING_RETRY:\n return SUBSCRIPTION_STATUS.ON_HOLD;\n case APPLE_SUBSCRIPTION_STATUS_CODE.REVOKED:\n return SUBSCRIPTION_STATUS.CANCELED;\n case APPLE_SUBSCRIPTION_STATUS_CODE.EXPIRED:\n default:\n return SUBSCRIPTION_STATUS.EXPIRED;\n }\n}\n\n/**\n * Shape of the GET /inApps/v1/subscriptions/{originalTransactionId} response.\n * Only the fields we read.\n */\ninterface AppleSubscriptionStatusResponse {\n data?: Array<{\n subscriptionGroupIdentifier?: string;\n lastTransactions?: Array<{\n originalTransactionId?: string;\n status?: number;\n signedTransactionInfo?: string;\n signedRenewalInfo?: string;\n }>;\n }>;\n bundleId?: string;\n environment?: 'Production' | 'Sandbox';\n}\n\n/**\n * Fetch the current state of a subscription directly from Apple's App Store\n * Server API — the canonical source of truth when webhooks have been missed,\n * delivered out of order, or the local store has no record at all.\n *\n * GET /inApps/v1/subscriptions/{originalTransactionId}\n * https://developer.apple.com/documentation/appstoreserverapi/get_all_subscription_statuses\n *\n * Returns null on:\n * - Missing API credentials (issuerId/keyId/privateKey)\n * - Network or auth failure\n * - Empty response (transaction not found)\n *\n * Hosts can call this directly (e.g. from a reconciliation cron) or it runs\n * automatically as the unknown-transaction fallback inside the Apple webhook.\n */\nexport async function fetchAppleSubscriptionStatus(\n originalTransactionId: string,\n config: AppleConfig,\n options?: { sandbox?: boolean },\n): Promise<SubscriptionInfo | null> {\n if (config.mockMode) return null;\n\n let jwt: string;\n try {\n jwt = await makeAppleApiJwt(config);\n } catch (err) {\n log.warn('[onesub/apple] Cannot fetch subscription status — JWT mint failed:', err);\n return null;\n }\n\n const host = options?.sandbox\n ? 'api.storekit-sandbox.itunes.apple.com'\n : 'api.storekit.itunes.apple.com';\n const url = `https://${host}/inApps/v1/subscriptions/${encodeURIComponent(originalTransactionId)}`;\n\n let body: AppleSubscriptionStatusResponse;\n try {\n const resp = await fetchWithTimeout(url, {\n headers: { Authorization: `Bearer ${jwt}` },\n });\n if (!resp.ok) {\n const text = await resp.text();\n log.warn(`[onesub/apple] Status API error ${resp.status}: ${text}`);\n return null;\n }\n body = (await resp.json()) as AppleSubscriptionStatusResponse;\n } catch (err) {\n log.warn('[onesub/apple] Status API network error:', err);\n return null;\n }\n\n // Find the lastTransactions entry matching the requested originalTransactionId.\n // Apple groups by subscriptionGroupIdentifier; one originalTxId is in exactly one group.\n const entry = body.data\n ?.flatMap((g) => g.lastTransactions ?? [])\n .find((t) => t.originalTransactionId === originalTransactionId);\n\n if (!entry || entry.status == null || !entry.signedTransactionInfo) {\n log.warn('[onesub/apple] Status API returned no matching transaction for', originalTransactionId);\n return null;\n }\n\n let tx: AppleTransactionPayload;\n try {\n tx = await decodeJws<AppleTransactionPayload>(entry.signedTransactionInfo, config.skipJwsVerification);\n } catch (err) {\n log.warn('[onesub/apple] Failed to decode signedTransactionInfo from Status API:', err);\n return null;\n }\n\n let renewal: AppleRenewalPayload | null = null;\n if (entry.signedRenewalInfo) {\n try {\n renewal = await decodeJws<AppleRenewalPayload>(entry.signedRenewalInfo, config.skipJwsVerification);\n } catch {\n // renewal info is optional\n }\n }\n\n if (!tx.productId || !tx.expiresDate) {\n log.warn('[onesub/apple] Status API transaction missing productId or expiresDate');\n return null;\n }\n\n const status = mapAppleStatusCode(entry.status);\n const purchasedAt = tx.originalPurchaseDate ?? tx.purchaseDate ?? Date.now();\n\n return {\n userId: '', // caller fills this in\n productId: tx.productId,\n platform: 'apple',\n status,\n expiresAt: new Date(tx.expiresDate).toISOString(),\n originalTransactionId,\n purchasedAt: new Date(purchasedAt).toISOString(),\n willRenew: renewal?.autoRenewStatus === 1,\n };\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// App Store Server API — Transaction History\n// ───────────────────────────────────────────────────────────────────────────\n\ninterface AppleTransactionHistoryResponse {\n signedTransactions?: string[];\n revision?: string;\n hasMore?: boolean;\n bundleId?: string;\n appAppleId?: number;\n environment?: 'Production' | 'Sandbox';\n}\n\n/**\n * Decoded lightweight transaction record from transaction history.\n */\nexport interface AppleTransactionRecord {\n originalTransactionId: string;\n transactionId: string;\n productId: string;\n type: string;\n purchasedAt: string;\n expiresAt: string | null;\n appAccountToken: string | null;\n inAppOwnershipType: string | null;\n revocationDate: string | null;\n}\n\n/**\n * Pagination safety cap for the Transaction History API. Apple returns ~20\n * transactions per page, so 50 pages ≈ 1000 transactions — far beyond any\n * realistic single-subscription history. Guards against a misbehaving\n * endpoint keeping the loop alive forever.\n */\nconst MAX_HISTORY_PAGES = 50;\n\n/**\n * Fetch all transactions for an originalTransactionId from the App Store\n * Transaction History API (GET /inApps/v2/history/{originalTransactionId}).\n *\n * Paginates automatically until `hasMore` is false. Returns all decoded\n * transaction records, oldest-first (Apple's default sort).\n *\n * Returns null when API credentials are missing or the call fails.\n */\nexport async function fetchAppleTransactionHistory(\n originalTransactionId: string,\n config: AppleConfig,\n options?: { sandbox?: boolean },\n): Promise<AppleTransactionRecord[] | null> {\n if (config.mockMode) return null;\n\n let jwt: string;\n try {\n jwt = await makeAppleApiJwt(config);\n } catch (err) {\n log.warn('[onesub/apple] Cannot fetch transaction history — JWT mint failed:', err);\n return null;\n }\n\n const host = options?.sandbox\n ? 'api.storekit-sandbox.itunes.apple.com'\n : 'api.storekit.itunes.apple.com';\n\n const results: AppleTransactionRecord[] = [];\n let revision: string | undefined;\n let pageCount = 0;\n\n for (;;) {\n const url = new URL(`https://${host}/inApps/v2/history/${encodeURIComponent(originalTransactionId)}`);\n if (revision) url.searchParams.set('revision', revision);\n\n let page: AppleTransactionHistoryResponse;\n try {\n const resp = await fetchWithTimeout(url.toString(), {\n headers: { Authorization: `Bearer ${jwt}` },\n });\n if (!resp.ok) {\n const text = await resp.text();\n log.warn(`[onesub/apple] Transaction History API error ${resp.status}: ${text}`);\n return null;\n }\n page = (await resp.json()) as AppleTransactionHistoryResponse;\n } catch (err) {\n log.warn('[onesub/apple] Transaction History API network error:', err);\n return null;\n }\n\n for (const signed of page.signedTransactions ?? []) {\n try {\n const tx = await decodeJws<AppleTransactionPayload>(signed, config.skipJwsVerification);\n if (!tx.originalTransactionId || !tx.transactionId || !tx.productId) continue;\n results.push({\n originalTransactionId: tx.originalTransactionId,\n transactionId: tx.transactionId,\n productId: tx.productId,\n type: tx.type ?? 'Unknown',\n purchasedAt: tx.purchaseDate ? new Date(tx.purchaseDate).toISOString() : new Date().toISOString(),\n expiresAt: tx.expiresDate ? new Date(tx.expiresDate).toISOString() : null,\n appAccountToken: tx.appAccountToken ?? null,\n inAppOwnershipType: tx.inAppOwnershipType ?? null,\n revocationDate: tx.revocationDate ? new Date(tx.revocationDate).toISOString() : null,\n });\n } catch {\n // skip malformed JWS entries\n }\n }\n\n pageCount++;\n if (!page.hasMore) break;\n // Infinite-loop guards: `hasMore: true` with a missing or unchanged\n // revision cursor would refetch the same page forever.\n if (!page.revision || page.revision === revision) {\n log.warn(\n '[onesub/apple] Transaction History API returned hasMore without a new revision cursor — stopping pagination (partial history returned)',\n );\n break;\n }\n if (pageCount >= MAX_HISTORY_PAGES) {\n log.warn(\n `[onesub/apple] Transaction History pagination hit the ${MAX_HISTORY_PAGES}-page cap — stopping (partial history returned)`,\n );\n break;\n }\n revision = page.revision;\n }\n\n return results;\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// Apple Promotional Offer — server-side signature\n// ───────────────────────────────────────────────────────────────────────────\n\n/**\n * Input for signing an Apple Promotional Offer payload.\n * https://developer.apple.com/documentation/storekit/generating-a-signature-for-promotional-offers\n */\nexport interface AppleOfferSignatureInput {\n /** App Bundle ID (e.g. com.example.app) */\n bundleId: string;\n /** The product ID of the subscription being offered */\n productId: string;\n /** The promotional offer ID defined in App Store Connect */\n offerId: string;\n /** A unique UUID generated per request (the nonce) */\n applicationUsername: string;\n}\n\n/**\n * Result returned to the client so StoreKit can verify the offer.\n */\nexport interface AppleOfferSignatureResult {\n keyId: string;\n nonce: string;\n timestamp: number;\n signature: string;\n}\n\n/**\n * Sign an Apple Promotional Offer payload with ES256 (ECDSA P-256).\n *\n * The message to sign is:\n * appBundleId + '⁣' + keyIdentifier + '⁣' + productIdentifier + '⁣'\n * + offerIdentifier + '⁣' + applicationUsername + '⁣'\n * + nonce (lowercase) + '⁣' + timestamp (ms, string)\n *\n * Requires `config.offerKeyId` and `config.offerPrivateKey` (PEM ES256 key from\n * App Store Connect → Subscriptions → Promotional Offers → Keys).\n *\n * Throws if the required keys are not configured.\n */\nexport async function signApplePromotionalOffer(\n input: AppleOfferSignatureInput,\n config: AppleConfig,\n): Promise<AppleOfferSignatureResult> {\n const { offerKeyId, offerPrivateKey } = config as AppleConfig & { offerKeyId?: string; offerPrivateKey?: string };\n if (!offerKeyId || !offerPrivateKey) {\n throw new Error('[onesub/apple] Promotional Offer signing requires config.apple.offerKeyId and config.apple.offerPrivateKey');\n }\n\n const nonce = randomUUID().toLowerCase();\n const timestamp = Date.now();\n const separator = '⁣';\n\n const message = [\n input.bundleId,\n offerKeyId,\n input.productId,\n input.offerId,\n input.applicationUsername,\n nonce,\n String(timestamp),\n ].join(separator);\n\n if (!offerPrivateKey.includes('BEGIN') || !offerPrivateKey.includes('END')) {\n throw new Error('[onesub/apple] offerPrivateKey does not appear to be PEM-encoded');\n }\n\n let signatureBuffer: Buffer;\n try {\n const sign = createSign('SHA256');\n sign.update(message);\n sign.end();\n signatureBuffer = sign.sign(offerPrivateKey);\n } catch (err) {\n throw new Error(`[onesub/apple] Failed to sign promotional offer — check that offerPrivateKey is a valid ES256 PEM key: ${(err as Error).message}`);\n }\n const signature = signatureBuffer.toString('base64');\n\n return { keyId: offerKeyId, nonce, timestamp, signature };\n}\n","import type { SubscriptionInfo, GoogleNotificationPayload, OneSubServerConfig } from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS } from '@onesub/shared';\nimport { log } from '../logger.js';\nimport { fetchWithTimeout } from '../http.js';\nimport { getDefaultCache } from '../cache.js';\nimport {\n mockValidateGoogleSubscription,\n mockValidateGoogleProduct,\n} from './mock.js';\n\ntype GoogleConfig = NonNullable<OneSubServerConfig['google']>;\n\n/**\n * Google Play Developer API v3 — SubscriptionPurchaseV2 resource (partial).\n * https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.subscriptionsv2/get\n *\n * V2 differences from the deprecated V1 (purchases.subscriptions):\n * - URL doesn't take productId — one token can map to multiple lineItems\n * - subscriptionState is a string enum (explicit grace/hold/paused) instead\n * of being inferred from expiryTime\n * - lineItems[] carries per-product productId + expiryTime + autoRenewingPlan\n * - linkedPurchaseToken first-class (upgrade/downgrade chain tracking)\n * - latestOrderId replaces orderId as the canonical transaction id\n */\ninterface GoogleSubscriptionPurchaseV2 {\n kind?: string; // 'androidpublisher#subscriptionPurchaseV2'\n startTime?: string; // RFC3339 (ISO 8601)\n regionCode?: string;\n lineItems?: Array<{\n productId?: string;\n expiryTime?: string; // RFC3339\n autoRenewingPlan?: {\n autoRenewEnabled?: boolean;\n priceChangeDetails?: unknown;\n };\n prepaidPlan?: {\n allowExtendAfterTime?: string;\n };\n offerDetails?: unknown;\n }>;\n subscriptionState?:\n | 'SUBSCRIPTION_STATE_UNSPECIFIED'\n | 'SUBSCRIPTION_STATE_PENDING'\n | 'SUBSCRIPTION_STATE_ACTIVE'\n | 'SUBSCRIPTION_STATE_PAUSED'\n | 'SUBSCRIPTION_STATE_IN_GRACE_PERIOD'\n | 'SUBSCRIPTION_STATE_ON_HOLD'\n | 'SUBSCRIPTION_STATE_CANCELED'\n | 'SUBSCRIPTION_STATE_EXPIRED';\n latestOrderId?: string;\n linkedPurchaseToken?: string;\n externalAccountIdentifiers?: {\n externalAccountId?: string;\n obfuscatedExternalAccountId?: string;\n obfuscatedExternalProfileId?: string;\n };\n acknowledgementState?:\n | 'ACKNOWLEDGEMENT_STATE_UNSPECIFIED'\n | 'ACKNOWLEDGEMENT_STATE_PENDING'\n | 'ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED';\n pausedStateContext?: { autoResumeTime?: string };\n canceledStateContext?: unknown;\n testPurchase?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Google RTDN notification types.\n * https://developer.android.com/google/play/billing/rtdn-reference\n */\nconst GOOGLE_NOTIFICATION_TYPE = {\n SUBSCRIPTION_RECOVERED: 1,\n SUBSCRIPTION_RENEWED: 2,\n SUBSCRIPTION_CANCELED: 3,\n SUBSCRIPTION_PURCHASED: 4,\n SUBSCRIPTION_ON_HOLD: 5,\n SUBSCRIPTION_IN_GRACE_PERIOD: 6,\n SUBSCRIPTION_RESTARTED: 7,\n SUBSCRIPTION_PRICE_CHANGE_CONFIRMED: 8,\n SUBSCRIPTION_DEFERRED: 9,\n SUBSCRIPTION_PAUSED: 10,\n SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED: 11,\n SUBSCRIPTION_REVOKED: 12,\n SUBSCRIPTION_EXPIRED: 13,\n} as const;\n\ntype GoogleNotificationType = (typeof GOOGLE_NOTIFICATION_TYPE)[keyof typeof GOOGLE_NOTIFICATION_TYPE];\n\n/**\n * Parsed Google RTDN inner message.\n */\ninterface GoogleDeveloperNotification {\n version?: string;\n packageName: string;\n eventTimeMillis: string;\n subscriptionNotification?: {\n version: string;\n notificationType: GoogleNotificationType;\n purchaseToken: string;\n subscriptionId: string;\n };\n voidedPurchaseNotification?: {\n purchaseToken: string;\n orderId: string;\n /** 1 = Subscription, 2 = One-time product */\n productType: 1 | 2;\n /** 1 = Full refund, 2 = Quantity-based partial refund (consumables) */\n refundType: 1 | 2;\n };\n oneTimeProductNotification?: {\n version: string;\n /** 1 = PURCHASED, 2 = CANCELED */\n notificationType: 1 | 2;\n purchaseToken: string;\n /** Product SKU (productId) */\n sku: string;\n };\n testNotification?: {\n version: string;\n };\n}\n\n/**\n * Decoded Google RTDN oneTimeProductNotification — sent when a consumable or\n * non-consumable product is purchased or canceled before acknowledgment.\n *\n * https://developer.android.com/google/play/billing/rtdn-reference#one-time\n */\nexport interface GoogleOneTimeProductNotification {\n /** 1 = PURCHASED, 2 = CANCELED (user canceled before purchase completed) */\n notificationType: 1 | 2;\n purchaseToken: string;\n /** Product SKU / productId */\n sku: string;\n packageName: string;\n}\n\n/**\n * Google Play Developer API v3 — ProductPurchase resource (consumable / non-consumable).\n * https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.products\n */\ninterface GoogleProductPurchase {\n purchaseTimeMillis?: string;\n purchaseState?: number; // 0 = Purchased, 1 = Canceled, 2 = Pending\n consumptionState?: number; // 0 = Not consumed, 1 = Consumed\n orderId?: string;\n /** Account identity set by the client at purchase time (obfuscatedAccountIdAndroid). */\n obfuscatedExternalAccountId?: string;\n [key: string]: unknown;\n}\n\n\n/**\n * Per-key in-flight token mint promises. Used to deduplicate concurrent\n * refreshes inside a single process — the cluster-wide cache layer sits in\n * `getDefaultCache()` underneath. Without this map every concurrent caller in\n * the same process would fire a separate network refresh while waiting for\n * the new value to land in Redis / memory.\n */\nconst refreshPromises = new Map<string, Promise<string>>();\n\n/**\n * Build the cache key for a given service account.\n *\n * Hashing the key (instead of using it raw) keeps the key length bounded for\n * cache backends that have key-size limits, and avoids leaking partial JSON\n * into log lines that print cache keys.\n */\nfunction googleTokenCacheKey(serviceAccountKey: string): string {\n let hash = 0;\n for (let i = 0; i < serviceAccountKey.length; i++) {\n hash = (hash * 31 + serviceAccountKey.charCodeAt(i)) | 0;\n }\n return `google:oauth:${hash}`;\n}\n\n/**\n * Obtain a Google OAuth2 access token, returning a cached token if it has more\n * than 60 seconds of remaining validity. Google tokens are valid for 3600 seconds,\n * so this avoids a network round-trip on every API call.\n *\n * Cache layer: `getDefaultCache()` (in-memory by default, swappable with\n * Redis / Memcached via `setDefaultCache()`). When the cache is shared across\n * cluster nodes only one node refreshes per TTL window.\n *\n * Promise deduplication prevents a thundering herd inside a single process:\n * concurrent callers that arrive while the token is being refreshed all await\n * the same in-flight request instead of each issuing their own.\n */\nasync function getCachedAccessToken(serviceAccountKey: string): Promise<string> {\n const cache = getDefaultCache();\n const cacheKey = googleTokenCacheKey(serviceAccountKey);\n\n const cached = await cache.get<{ token: string; expiresAt: number }>(cacheKey);\n if (cached && cached.expiresAt - Date.now() > 60_000) {\n return cached.token;\n }\n\n const inflight = refreshPromises.get(cacheKey);\n if (inflight) return inflight;\n\n const promise = getAccessToken(serviceAccountKey)\n .then(async (token) => {\n const expiresAt = Date.now() + 3_600_000;\n // TTL slightly under the 1h Google validity — cache eviction matches token expiry.\n await cache.set(cacheKey, { token, expiresAt }, 3_540);\n return token;\n })\n .finally(() => {\n refreshPromises.delete(cacheKey);\n });\n refreshPromises.set(cacheKey, promise);\n return promise;\n}\n\n/**\n * Obtain a Google OAuth2 access token using a service account key (JWT assertion flow).\n * The serviceAccountKey is expected to be a JSON string of the service account credentials.\n */\nasync function getAccessToken(serviceAccountKey: string): Promise<string> {\n let key: {\n client_email: string;\n private_key: string;\n token_uri?: string;\n };\n\n try {\n key = JSON.parse(serviceAccountKey) as typeof key;\n } catch {\n throw new Error('[onesub/google] Invalid serviceAccountKey JSON');\n }\n\n const tokenUri = key.token_uri ?? 'https://oauth2.googleapis.com/token';\n const scope = 'https://www.googleapis.com/auth/androidpublisher';\n const now = Math.floor(Date.now() / 1000);\n\n // Build a JWT assertion\n const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');\n const payload = Buffer.from(\n JSON.stringify({\n iss: key.client_email,\n scope,\n aud: tokenUri,\n iat: now,\n exp: now + 3600,\n })\n ).toString('base64url');\n\n const signingInput = `${header}.${payload}`;\n\n // Sign with the private key using the native crypto module\n const { createSign } = await import('crypto');\n const sign = createSign('RSA-SHA256');\n sign.update(signingInput);\n const signature = sign.sign(key.private_key, 'base64url');\n\n const assertion = `${signingInput}.${signature}`;\n\n const resp = await fetchWithTimeout(tokenUri, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n assertion,\n }),\n });\n\n if (!resp.ok) {\n throw new Error(`[onesub/google] Token request failed: ${resp.status}`);\n }\n\n const data = (await resp.json()) as { access_token?: string };\n if (!data.access_token) throw new Error('[onesub/google] No access_token in response');\n return data.access_token;\n}\n\n/**\n * Fetch a subscription purchase from the Google Play Developer API\n * (purchases.subscriptionsv2.get). The v2 endpoint takes only the purchaseToken\n * — the productId comes back in lineItems, which lets one token represent a\n * multi-product subscription.\n */\nasync function fetchSubscriptionPurchaseV2(\n packageName: string,\n purchaseToken: string,\n accessToken: string,\n): Promise<GoogleSubscriptionPurchaseV2> {\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(packageName)}/purchases/subscriptionsv2/tokens/` +\n `${encodeURIComponent(purchaseToken)}`;\n\n const resp = await fetchWithTimeout(url, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n throw new Error(`[onesub/google] Play API v2 error ${resp.status}: ${body}`);\n }\n\n return resp.json() as Promise<GoogleSubscriptionPurchaseV2>;\n}\n\n/**\n * Fetch a one-time product purchase from the Google Play Developer API.\n * Uses purchases.products instead of purchases.subscriptions.\n */\nasync function fetchProductPurchase(\n packageName: string,\n productId: string,\n purchaseToken: string,\n accessToken: string,\n): Promise<GoogleProductPurchase> {\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(packageName)}/purchases/products/` +\n `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}`;\n\n const resp = await fetchWithTimeout(url, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n throw new Error(`[onesub/google] Play Products API error ${resp.status}: ${body}`);\n }\n\n return resp.json() as Promise<GoogleProductPurchase>;\n}\n\n/**\n * Acknowledge a Google Play subscription purchase.\n * Google auto-refunds purchases that are not acknowledged within 3 days.\n *\n * Idempotent on Google's side: a no-op when the purchase has already been\n * acknowledged. Fire-and-forget — entitlement was already granted, so failures\n * are logged but not surfaced to the caller (operations should monitor logs).\n */\nexport async function acknowledgeGoogleSubscription(\n purchaseToken: string,\n productId: string,\n config: GoogleConfig,\n): Promise<void> {\n if (config.mockMode) return;\n if (!config.serviceAccountKey) return;\n if (!config.packageName) return;\n\n let accessToken: string;\n try {\n accessToken = await getCachedAccessToken(config.serviceAccountKey);\n } catch (err) {\n log.warn('[onesub/google] Could not get access token for subscription ack:', err);\n return;\n }\n\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(config.packageName)}/purchases/subscriptions/` +\n `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}:acknowledge`;\n\n try {\n const resp = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n body: '{}',\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n log.warn(`[onesub/google] Subscription acknowledge API error ${resp.status}: ${body} — auto-refund risk`);\n }\n } catch (err) {\n log.warn('[onesub/google] Subscription acknowledge network error — auto-refund risk:', err);\n }\n}\n\n/**\n * Acknowledge a Google Play one-time product purchase (non-consumable).\n * Consumables do not need this — :consume implicitly acknowledges.\n *\n * Same fire-and-forget semantics as acknowledgeGoogleSubscription.\n */\nexport async function acknowledgeGoogleProduct(\n purchaseToken: string,\n productId: string,\n config: GoogleConfig,\n): Promise<void> {\n if (config.mockMode) return;\n if (!config.serviceAccountKey) return;\n if (!config.packageName) return;\n\n let accessToken: string;\n try {\n accessToken = await getCachedAccessToken(config.serviceAccountKey);\n } catch (err) {\n log.warn('[onesub/google] Could not get access token for product ack:', err);\n return;\n }\n\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(config.packageName)}/purchases/products/` +\n `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}:acknowledge`;\n\n try {\n const resp = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n body: '{}',\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n log.warn(`[onesub/google] Product acknowledge API error ${resp.status}: ${body} — auto-refund risk`);\n }\n } catch (err) {\n log.warn('[onesub/google] Product acknowledge network error — auto-refund risk:', err);\n }\n}\n\n/**\n * Consume a one-time product purchase via the Google Play Developer API.\n * Must be called for consumables after the entitlement is granted to the user.\n * Google auto-refunds unconsumed purchases after 3 days.\n *\n * This function does not throw — callers should log failures and monitor,\n * as the entitlement has already been granted.\n */\nexport async function consumeGoogleProductReceipt(\n purchaseToken: string,\n productId: string,\n config: GoogleConfig,\n): Promise<void> {\n if (!config.serviceAccountKey) return;\n if (!config.packageName) return;\n\n let accessToken: string;\n try {\n accessToken = await getCachedAccessToken(config.serviceAccountKey);\n } catch (err) {\n log.warn('[onesub/google] Could not get access token for consume:', err);\n return;\n }\n\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(config.packageName)}/purchases/products/` +\n `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}:consume`;\n\n try {\n const resp = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n log.warn(`[onesub/google] Consume API error ${resp.status}: ${body} — auto-refund risk`);\n }\n } catch (err) {\n log.warn('[onesub/google] Consume network error — auto-refund risk:', err);\n }\n}\n\n/**\n * Map a v2 subscriptionState string to onesub SubscriptionStatus.\n *\n * Returns null when the state is unrecognised or PENDING (initial purchase\n * not yet settled — entitlement should not be granted yet).\n */\nfunction deriveStatusV2(\n state: GoogleSubscriptionPurchaseV2['subscriptionState'],\n): SubscriptionInfo['status'] | null {\n switch (state) {\n case 'SUBSCRIPTION_STATE_ACTIVE':\n return SUBSCRIPTION_STATUS.ACTIVE;\n case 'SUBSCRIPTION_STATE_IN_GRACE_PERIOD':\n return SUBSCRIPTION_STATUS.GRACE_PERIOD;\n case 'SUBSCRIPTION_STATE_ON_HOLD':\n return SUBSCRIPTION_STATUS.ON_HOLD;\n case 'SUBSCRIPTION_STATE_PAUSED':\n return SUBSCRIPTION_STATUS.PAUSED;\n case 'SUBSCRIPTION_STATE_CANCELED':\n return SUBSCRIPTION_STATUS.CANCELED;\n case 'SUBSCRIPTION_STATE_EXPIRED':\n return SUBSCRIPTION_STATUS.EXPIRED;\n case 'SUBSCRIPTION_STATE_PENDING':\n case 'SUBSCRIPTION_STATE_UNSPECIFIED':\n default:\n return null;\n }\n}\n\n/**\n * Validate a Google Play purchase token via purchases.subscriptionsv2.get.\n *\n * The productId argument is used to pick the matching `lineItems` entry. If\n * the response has no lineItem with that productId, validation fails — the\n * token does not entitle the caller to that product. (For multi-product\n * subscriptions, callers can read other lineItems out of the API directly.)\n *\n * @param receipt The purchaseToken from the client (Google Play Billing)\n * @param productId Expected subscription productId — must match a lineItem\n * @param config Google config with packageName + optional serviceAccountKey\n */\nexport async function validateGoogleReceipt(\n receipt: string,\n productId: string,\n config: GoogleConfig\n): Promise<SubscriptionInfo | null> {\n if (config.mockMode) return mockValidateGoogleSubscription(receipt, productId);\n if (!config.serviceAccountKey) {\n log.warn('[onesub/google] No serviceAccountKey provided — cannot call Play API');\n return null;\n }\n if (!config.packageName) {\n log.warn('[onesub/google] No packageName provided — cannot call Play API');\n return null;\n }\n\n let purchase: GoogleSubscriptionPurchaseV2;\n try {\n const token = await getCachedAccessToken(config.serviceAccountKey);\n purchase = await fetchSubscriptionPurchaseV2(config.packageName, receipt, token);\n } catch (err) {\n log.error('[onesub/google] Receipt validation failed:', err);\n return null;\n }\n\n const status = deriveStatusV2(purchase.subscriptionState);\n if (!status) {\n log.warn(\n '[onesub/google] Unrecognised or pending subscriptionState — rejecting:',\n purchase.subscriptionState,\n );\n return null;\n }\n\n // Pick the lineItem matching the requested productId. v2 supports multi-product\n // subscriptions so the same token can carry several lineItems; we only entitle\n // the caller for the productId they explicitly asked about.\n const lineItem = purchase.lineItems?.find((item) => item.productId === productId);\n if (!lineItem) {\n log.warn(\n '[onesub/google] productId not found in subscription lineItems:',\n productId,\n 'available:',\n purchase.lineItems?.map((i) => i.productId).join(', ') ?? '(none)',\n );\n return null;\n }\n\n const expiresAt = lineItem.expiryTime ?? new Date().toISOString();\n const purchasedAt = purchase.startTime ?? new Date().toISOString();\n const willRenew = lineItem.autoRenewingPlan?.autoRenewEnabled ?? false;\n\n // Surface autoResumeTime only when the subscription is actually paused —\n // pausedStateContext is undefined for any other state, but be defensive in\n // case Google starts including it on adjacent states (e.g. recently-resumed).\n const autoResumeTime =\n status === SUBSCRIPTION_STATUS.PAUSED\n ? purchase.pausedStateContext?.autoResumeTime\n : undefined;\n\n const boundAccountId = purchase.externalAccountIdentifiers?.obfuscatedExternalAccountId;\n\n return {\n userId: '', // caller fills this in\n productId,\n platform: 'google',\n status,\n expiresAt,\n // Keyed by the purchaseToken, NOT latestOrderId: RTDN webhooks and\n // linkedPurchaseToken chains only carry the token, so records keyed by\n // order id are invisible to the entire webhook lifecycle (refund/cancel/\n // renewal would never find them).\n originalTransactionId: receipt,\n purchasedAt,\n willRenew,\n // Surface the linked token so host apps (and our webhook userId-continuity\n // logic) can follow upgrade/downgrade chains.\n linkedPurchaseToken: purchase.linkedPurchaseToken,\n autoResumeTime,\n ...(boundAccountId ? { boundAccountId } : {}),\n };\n}\n\n/**\n * Result of validating a Google Play product (consumable / non-consumable) receipt.\n */\nexport interface GoogleProductResult {\n /** orderId — unique per Google Play transaction, safe deduplication key */\n transactionId: string;\n purchasedAt: string;\n /**\n * Account identity the client baked in at purchase time\n * (`obfuscatedAccountIdAndroid` → Play `obfuscatedExternalAccountId`). When\n * present the validate route rejects attributing/reassigning the purchase to\n * a non-matching userId. Absent for purchases made before the client set it\n * (backward-compatible).\n */\n obfuscatedExternalAccountId?: string;\n}\n\n/**\n * Validate a Google Play purchase token for a one-time product (consumable or\n * non-consumable). Uses purchases.products, not purchases.subscriptions.\n *\n * Security checks applied beyond basic API verification:\n * - purchaseState must be 0 (completed)\n * - consumptionState checked for consumables: already-consumed tokens indicate replay\n * - Receipt age limited to 72 hours\n * - orderId used as transactionId (per-purchase unique, unlike purchaseToken)\n */\nexport async function validateGoogleProductReceipt(\n purchaseToken: string,\n productId: string,\n config: GoogleConfig,\n type: 'consumable' | 'non_consumable' = 'non_consumable',\n): Promise<GoogleProductResult | null> {\n if (config.mockMode) return mockValidateGoogleProduct(purchaseToken, productId);\n if (!config.serviceAccountKey) {\n log.warn('[onesub/google] No serviceAccountKey — cannot validate product receipt');\n return null;\n }\n if (!config.packageName) {\n log.warn('[onesub/google] No packageName — cannot validate product receipt');\n return null;\n }\n\n let purchase: GoogleProductPurchase;\n try {\n const token = await getCachedAccessToken(config.serviceAccountKey);\n purchase = await fetchProductPurchase(config.packageName, productId, purchaseToken, token);\n } catch (err) {\n log.error('[onesub/google] Product receipt validation failed:', err);\n return null;\n }\n\n // purchaseState 0 = completed (1 = canceled, 2 = pending)\n if (purchase.purchaseState !== 0) {\n log.warn('[onesub/google] Purchase not completed, state:', purchase.purchaseState);\n return null;\n }\n\n // For consumables: consumptionState 1 means already consumed by a previous request.\n // This is the primary replay-attack signal for consumables on Android.\n if (type === 'consumable' && purchase.consumptionState === 1) {\n log.warn('[onesub/google] Consumable already consumed — possible replay attack');\n return null;\n }\n\n // Reject receipts older than the configured window (see\n // productReceiptMaxAgeHours — raise it for migrations / e2e).\n if (purchase.purchaseTimeMillis) {\n const purchaseTime = parseInt(purchase.purchaseTimeMillis, 10);\n const maxAgeMs = (config.productReceiptMaxAgeHours ?? 72) * 60 * 60 * 1000;\n if (Date.now() - purchaseTime > maxAgeMs) {\n log.warn(`[onesub/google] Product receipt too old (>${config.productReceiptMaxAgeHours ?? 72}h)`);\n return null;\n }\n }\n\n if (!purchase.orderId) {\n log.warn('[onesub/google] No orderId in product purchase');\n return null;\n }\n\n return {\n transactionId: purchase.orderId,\n purchasedAt: purchase.purchaseTimeMillis\n ? new Date(parseInt(purchase.purchaseTimeMillis, 10)).toISOString()\n : new Date().toISOString(),\n obfuscatedExternalAccountId: purchase.obfuscatedExternalAccountId,\n };\n}\n\n/**\n * Decode a Google RTDN Pub/Sub notification.\n * Returns the extracted subscription notification data, or null for test pings\n * and notification kinds handled by other decoders (e.g. voided purchases).\n */\nexport function decodeGoogleNotification(payload: GoogleNotificationPayload): {\n notificationType: GoogleNotificationType;\n purchaseToken: string;\n subscriptionId: string;\n packageName: string;\n} | null {\n let notification: GoogleDeveloperNotification;\n\n try {\n const json = Buffer.from(payload.message.data, 'base64').toString('utf-8');\n notification = JSON.parse(json) as GoogleDeveloperNotification;\n } catch {\n return null;\n }\n\n if (!notification.subscriptionNotification) {\n // test notification, voided purchase, or unsupported type\n return null;\n }\n\n const { notificationType, purchaseToken, subscriptionId } = notification.subscriptionNotification;\n\n return {\n notificationType,\n purchaseToken,\n subscriptionId,\n packageName: notification.packageName,\n };\n}\n\n/**\n * Decoded Google RTDN voidedPurchaseNotification — sent when a purchase\n * (subscription or one-time product) is refunded, charged back, or revoked.\n *\n * https://developer.android.com/google/play/billing/rtdn-reference#voided-purchase\n */\nexport interface GoogleVoidedNotification {\n /** Subscription purchaseToken or one-time product purchaseToken */\n purchaseToken: string;\n /** GPA.* — matches the orderId stored as transactionId for one-time products */\n orderId: string;\n /** 1 = Subscription, 2 = One-time product */\n productType: 1 | 2;\n /** 1 = Full refund, 2 = Quantity-based partial refund (consumables) */\n refundType: 1 | 2;\n packageName: string;\n}\n\n/**\n * Decode a Google RTDN voidedPurchaseNotification, if the payload is one.\n * Returns null when the payload is a different notification kind.\n */\nexport function decodeGoogleVoidedNotification(\n payload: GoogleNotificationPayload,\n): GoogleVoidedNotification | null {\n let notification: GoogleDeveloperNotification;\n\n try {\n const json = Buffer.from(payload.message.data, 'base64').toString('utf-8');\n notification = JSON.parse(json) as GoogleDeveloperNotification;\n } catch {\n return null;\n }\n\n if (!notification.voidedPurchaseNotification) return null;\n\n const { purchaseToken, orderId, productType, refundType } = notification.voidedPurchaseNotification;\n\n return {\n purchaseToken,\n orderId,\n productType,\n refundType,\n packageName: notification.packageName,\n };\n}\n\n/**\n * Decode a Google RTDN oneTimeProductNotification, if the payload is one.\n * Returns null when the payload is a different notification kind.\n *\n * Note: the notification does NOT carry userId context. For PURCHASED, the\n * receipt must be acknowledged via acknowledgeGoogleProduct to prevent the\n * 3-day auto-refund window. The userId is only known after the client calls\n * POST /onesub/purchase/validate, which is the authoritative record-creation\n * path. The webhook handler therefore acknowledges without creating a record.\n */\nexport function decodeGoogleOneTimeProductNotification(\n payload: GoogleNotificationPayload,\n): GoogleOneTimeProductNotification | null {\n let notification: GoogleDeveloperNotification;\n try {\n const json = Buffer.from(payload.message.data, 'base64').toString('utf-8');\n notification = JSON.parse(json) as GoogleDeveloperNotification;\n } catch {\n return null;\n }\n if (!notification.oneTimeProductNotification) return null;\n const { notificationType, purchaseToken, sku } = notification.oneTimeProductNotification;\n if (notificationType !== 1 && notificationType !== 2) {\n log.warn('[onesub/google] Unknown oneTimeProductNotification type:', notificationType);\n return null;\n }\n return { notificationType, purchaseToken, sku, packageName: notification.packageName };\n}\n\n/**\n * Determine if a Google RTDN notification type represents an active subscription\n * inside the paid window (excludes grace period — that's a separate state now).\n */\nexport function isGoogleActiveNotification(notificationType: GoogleNotificationType): boolean {\n return (\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_PURCHASED ||\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_RENEWED ||\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_RECOVERED ||\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_RESTARTED\n );\n}\n\nexport function isGoogleCanceledNotification(notificationType: GoogleNotificationType): boolean {\n return (\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_CANCELED ||\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_REVOKED\n );\n}\n\nexport function isGoogleExpiredNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_EXPIRED;\n}\n\n/**\n * Subscription is in the store-granted grace period — payment failed but the\n * user retains access while Google retries. Maps to SUBSCRIPTION_STATUS.GRACE_PERIOD.\n */\nexport function isGoogleGracePeriodNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_IN_GRACE_PERIOD;\n}\n\n/**\n * Subscription is on hold — grace period ended, retry continuing, entitlement\n * REVOKED. Maps to SUBSCRIPTION_STATUS.ON_HOLD.\n */\nexport function isGoogleOnHoldNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_ON_HOLD;\n}\n\n/**\n * User-voluntary pause — entitlement REVOKED until autoResumeTime or manual\n * resume. Maps to SUBSCRIPTION_STATUS.PAUSED.\n */\nexport function isGooglePausedNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_PAUSED;\n}\n\n/**\n * User agreed to a developer-initiated price change. The new price applies\n * on the next renewal. Subscription remains active in the meantime.\n */\nexport function isGooglePriceChangeConfirmedNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_PRICE_CHANGE_CONFIRMED;\n}\n","import type { Response } from 'express';\nimport { z } from 'zod';\nimport type { OneSubErrorCode } from '@onesub/shared';\nimport { ONESUB_ERROR_CODE } from '@onesub/shared';\n\n/**\n * Send a structured error response. Every onesub HTTP endpoint uses this\n * so consumers can rely on `errorCode` being present on every 4xx/5xx body.\n *\n * `extra` lets callers inject route-specific defaults (e.g. `purchase: null`\n * for ValidatePurchaseResponse shape compatibility).\n */\nexport function sendError(\n res: Response,\n status: number,\n code: OneSubErrorCode,\n error: string,\n extra: Record<string, unknown> = {},\n): void {\n res.status(status).json({ ...extra, error, errorCode: code });\n}\n\n/**\n * 400 response for a failed zod parse. The ZodError's per-issue messages\n * are joined for the human-readable `error` field.\n */\nexport function sendZodError(\n res: Response,\n err: z.ZodError,\n extra: Record<string, unknown> = {},\n): void {\n sendError(\n res,\n 400,\n ONESUB_ERROR_CODE.INVALID_INPUT,\n err.issues.map((e: { message: string }) => e.message).join(', '),\n extra,\n );\n}\n","import type { OneSubAppConfig, OneSubServerConfig } from '@onesub/shared';\nimport { log } from './logger.js';\n\n/**\n * Resolves which app an incoming receipt belongs to, so one onesub instance can\n * serve N bundles.\n *\n * A single-app deployment configures `apple`/`google` at the top level and never\n * touches `apps` — that config stays the default and nothing here changes its\n * behaviour.\n */\nexport interface AppRegistry {\n readonly apps: OneSubAppConfig[];\n readonly defaultApp: OneSubAppConfig | undefined;\n /**\n * Returns the server config with `apple`/`google` swapped to the matched app's\n * credentials, so downstream providers keep reading `config.apple` /\n * `config.google` without knowing about multi-app.\n */\n configFor(hint: AppHint): OneSubServerConfig;\n}\n\nexport interface AppHint {\n /** `appId` from the request body, when the client sends one. */\n appId?: string | null | undefined;\n /** Bundle id read out of an Apple receipt — the receipt names its own app. */\n bundleId?: string | null | undefined;\n}\n\nfunction appName(app: OneSubAppConfig): string {\n return app.id || app.apple?.bundleId || app.google?.packageName || 'default';\n}\n\nconst registryCache = new WeakMap<OneSubServerConfig, AppRegistry>();\n\n/**\n * Registry for this config, built once. Routes and webhook handlers all resolve\n * through the same instance so the multi-app summary is logged a single time.\n */\nexport function getAppRegistry(config: OneSubServerConfig): AppRegistry {\n const cached = registryCache.get(config);\n if (cached) return cached;\n const registry = buildAppRegistry(config);\n registryCache.set(config, registry);\n return registry;\n}\n\nexport function buildAppRegistry(config: OneSubServerConfig): AppRegistry {\n const apps: OneSubAppConfig[] = [...(config.apps ?? [])];\n\n // The top-level apple/google config is an app in its own right. Keeping it in\n // the list means a single-app deployment resolves through the same path.\n if (config.apple || config.google) {\n const id = config.defaultAppId || config.apple?.bundleId || config.google?.packageName || 'default';\n if (!apps.some((a) => a.id === id)) {\n apps.unshift({ id, apple: config.apple, google: config.google });\n }\n }\n\n const defaultApp =\n apps.find((a) => a.id === config.defaultAppId) ??\n // No explicit default: fall back to the top-level config, which unshifted to\n // the front above. Never silently pick an arbitrary app when several exist —\n // that would validate one app's receipt against another's credentials.\n (config.apple || config.google || apps.length === 1 ? apps[0] : undefined);\n\n if (apps.length > 1) {\n log.info('[onesub] Multi-app mode:', apps.map(appName).join(', '), '| default:', defaultApp ? appName(defaultApp) : '(none)');\n }\n\n function match(hint: AppHint): OneSubAppConfig | undefined {\n if (hint.appId) {\n const byId = apps.find(\n (a) =>\n a.id === hint.appId ||\n a.apple?.bundleId === hint.appId ||\n a.google?.packageName === hint.appId,\n );\n if (byId) return byId;\n // An appId we don't serve must not silently fall through to the default —\n // that would validate it against some other app's credentials.\n log.warn('[onesub] Unknown appId:', hint.appId);\n return undefined;\n }\n\n // Apple receipts carry their own bundleId, so an Apple client needs no appId.\n if (hint.bundleId) {\n const byBundle = apps.find((a) => a.apple?.bundleId === hint.bundleId);\n if (byBundle) return byBundle;\n log.warn('[onesub] No app configured for bundleId:', hint.bundleId);\n return undefined;\n }\n\n return defaultApp;\n }\n\n return {\n apps,\n defaultApp,\n configFor(hint: AppHint): OneSubServerConfig {\n const app = match(hint);\n if (!app) {\n // Hand back a config with no providers: the route then reports the usual\n // \"config missing\" error rather than validating against the wrong app.\n return { ...config, apple: undefined, google: undefined };\n }\n return { ...config, apple: app.apple, google: app.google };\n },\n };\n}\n\n/**\n * Reads the bundleId out of an Apple JWS **without verifying its signature**,\n * only to decide which app's credentials to validate it with.\n *\n * Trusting this to pick the app is safe: the chosen app's validator then verifies\n * the signature against Apple's roots and re-checks the bundleId, so a forged\n * payload claiming another app's bundle fails there.\n */\nexport function peekAppleBundleId(jws: string): string | undefined {\n try {\n const payload = jws.split('.')[1];\n if (!payload) return undefined;\n const json = Buffer.from(payload, 'base64url').toString('utf-8');\n const claims = JSON.parse(json) as { bundleId?: string };\n return typeof claims.bundleId === 'string' ? claims.bundleId : undefined;\n } catch {\n return undefined;\n }\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type { ValidateReceiptResponse, OneSubServerConfig } from '@onesub/shared';\nimport { ROUTES, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { SubscriptionStore } from '../store.js';\nimport { validateAppleReceipt } from '../providers/apple.js';\nimport { validateGoogleReceipt, acknowledgeGoogleSubscription } from '../providers/google.js';\nimport { log } from '../logger.js';\nimport { sendError, sendZodError } from '../errors.js';\nimport { getAppRegistry, peekAppleBundleId } from '../apps.js';\n\nconst NO_SUB = { valid: false, subscription: null } as const;\n\nconst validateSchema = z.object({\n platform: z.enum(['apple', 'google']),\n receipt: z.string().min(1).max(10000),\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256),\n /** Which app this receipt belongs to. Optional — see OneSubServerConfig.apps. */\n appId: z.string().min(1).max(256).optional(),\n});\n\nexport function createValidateRouter(\n config: OneSubServerConfig,\n store: SubscriptionStore\n): Router {\n const router = Router();\n const registry = getAppRegistry(config);\n\n router.post(ROUTES.VALIDATE, async (req: Request, res: Response) => {\n let platform: string;\n let receipt: string;\n let userId: string;\n let productId: string;\n let appId: string | undefined;\n\n try {\n ({ platform, receipt, userId, productId, appId } = validateSchema.parse(req.body));\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err, NO_SUB);\n return;\n }\n throw err;\n }\n\n try {\n let sub = null;\n\n // An Apple receipt names its own app, so an Apple client needs no appId.\n // A Google purchase token does not, so it relies on appId (or the default).\n const appConfig = registry.configFor({\n appId,\n bundleId: platform === 'apple' ? peekAppleBundleId(receipt) : undefined,\n });\n\n if (platform === 'apple') {\n if (!appConfig.apple) {\n sendError(res, 500, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, 'Apple configuration not provided', NO_SUB);\n return;\n }\n sub = await validateAppleReceipt(receipt, appConfig.apple);\n } else {\n if (!appConfig.google) {\n sendError(res, 500, ONESUB_ERROR_CODE.GOOGLE_CONFIG_MISSING, 'Google configuration not provided', NO_SUB);\n return;\n }\n sub = await validateGoogleReceipt(receipt, productId, appConfig.google);\n }\n\n if (!sub) {\n sendError(res, 422, ONESUB_ERROR_CODE.RECEIPT_VALIDATION_FAILED, 'Receipt validation failed', NO_SUB);\n return;\n }\n\n // Account-binding guard (payment-bypass defense), mirroring the one-time\n // purchase route: when the receipt carries an account identity baked in at\n // purchase time (Apple appAccountToken / Google obfuscatedExternalAccountId),\n // the subscription may only be bound to a matching userId. Without this, a\n // leaked/shared receipt can be re-bound to any attacker-chosen userId.\n // Apple compares case-insensitively (appAccountToken is normalized to a\n // lowercase UUID); Google ids are verbatim — a case-insensitive compare\n // would let a case-flipped userId through on hosts with case-sensitive ids.\n const boundAccountId = sub.boundAccountId;\n delete sub.boundAccountId;\n const bindingMismatch = platform === 'apple'\n ? boundAccountId && boundAccountId.toLowerCase() !== userId.toLowerCase()\n : boundAccountId && boundAccountId !== userId;\n if (bindingMismatch) {\n log.warn(\n `[onesub/validate] account binding mismatch for ${sub.originalTransactionId}: receipt token does not match userId ${userId}`,\n );\n sendError(\n res,\n 409,\n ONESUB_ERROR_CODE.TRANSACTION_BELONGS_TO_OTHER_USER,\n 'TRANSACTION_BELONGS_TO_OTHER_USER',\n NO_SUB,\n );\n return;\n }\n\n sub.userId = userId;\n await store.save(sub);\n\n // Google requires acknowledgement within 3 days of purchase or the\n // transaction is auto-refunded. Fire-and-forget — entitlement is already\n // saved, ack is idempotent on the Play side.\n if (platform === 'google' && appConfig.google) {\n void acknowledgeGoogleSubscription(receipt, productId, appConfig.google);\n }\n\n const response: ValidateReceiptResponse = { valid: true, subscription: sub };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/validate] Unexpected error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, 'Internal server error during receipt validation', NO_SUB);\n }\n });\n\n return router;\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport type { StatusResponse } from '@onesub/shared';\nimport { ROUTES, SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { SubscriptionStore } from '../store.js';\nimport { log } from '../logger.js';\nimport { sendError } from '../errors.js';\n\nconst NO_SUB = { active: false, subscription: null } as const;\n\nexport function createStatusRouter(store: SubscriptionStore): Router {\n const router = Router();\n\n /**\n * GET /onesub/status?userId=xxx\n *\n * Returns whether the user has an active subscription and the full\n * SubscriptionInfo if one exists.\n */\n router.get(ROUTES.STATUS, async (req: Request, res: Response) => {\n const userId = req.query['userId'];\n\n if (!userId || typeof userId !== 'string') {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'Missing required query param: userId', NO_SUB);\n return;\n }\n\n if (userId.length > 256) {\n sendError(res, 400, ONESUB_ERROR_CODE.USER_ID_TOO_LONG, 'userId must not exceed 256 characters', NO_SUB);\n return;\n }\n\n try {\n const sub = await store.getByUserId(userId);\n\n if (!sub) {\n const response: StatusResponse = { active: false, subscription: null };\n res.status(200).json(response);\n return;\n }\n\n // Entitlement is valid during the store-granted grace period (Apple\n // GRACE_PERIOD subtype, Google IN_GRACE_PERIOD). on_hold is excluded —\n // the user must fix payment before they regain access.\n //\n // The expiresAt check guards two cases:\n // 1. refundPolicy='until_expiry' lets a refunded record keep\n // status='active' until the original expiry passes — without this\n // check, the user would keep entitlement forever if Apple/Google\n // never sent the matching EXPIRED notification.\n // 2. Stale records where the EXPIRED webhook was missed entirely.\n const statusAllows =\n sub.status === SUBSCRIPTION_STATUS.ACTIVE ||\n sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD;\n const notYetExpired = new Date(sub.expiresAt).getTime() > Date.now();\n const active = statusAllows && notYetExpired;\n const response: StatusResponse = { active, subscription: sub };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/status] Store error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error', NO_SUB);\n }\n });\n\n return router;\n}\n","import type { CacheAdapter } from './cache.js';\n\n/**\n * Pluggable webhook-event idempotency store.\n *\n * Apple sends `notificationUUID` and Google Pub/Sub sends `messageId` — both\n * are guaranteed unique per notification. Apple/Google will retry on any\n * non-2xx response (Apple: ~3 days, Google: configurable Pub/Sub policy), so\n * the same notification can hit our handler multiple times if the previous\n * response timed out, the DB was briefly down, etc. We must not double-apply\n * state changes (extra subscription days, refunded purchase being deleted\n * twice, consumption response being PUT twice).\n *\n * `markIfNew` returns `true` when the caller should process the event,\n * `false` when it has been seen before. Implementations decide retention —\n * once-seen IDs are kept long enough that retries from the source can't beat\n * the TTL (we recommend ≥ 7 days).\n */\nexport interface WebhookEventStore {\n /**\n * Atomically register the given event id. Returns `true` if it's new (first\n * time we've seen it), `false` if it was already seen.\n *\n * `provider` lets us key by source so Apple's notificationUUID and Google's\n * messageId never collide.\n */\n markIfNew(provider: 'apple' | 'google', eventId: string): Promise<boolean>;\n\n /**\n * Forget a previously marked event id. Called by the webhook routes when\n * processing fails AFTER `markIfNew` succeeded (e.g. the store was briefly\n * down) — without it, the source's retry would be deduped and the lifecycle\n * event permanently dropped. Optional for backward compatibility with\n * custom implementations.\n */\n unmark?(provider: 'apple' | 'google', eventId: string): Promise<void>;\n}\n\n/**\n * Best-effort unmark used by the webhook routes when INLINE processing fails\n * after `markIfNew` succeeded — without it the source's retry (Apple/Google\n * both retry on 5xx) would be deduped and the lifecycle event dropped forever.\n *\n * No-ops when the store is absent, the store doesn't implement `unmark`, or\n * no event id was marked. Swallows its own errors: a failing unmark must not\n * mask the original processing error that triggered it.\n *\n * NOTE — only the inline path (and a failed `enqueue()`) calls this. When a\n * webhook queue is configured and the job was durably enqueued, handler\n * failures are retried by the QUEUE, not the source; unmarking there would\n * let a source retry race the queue's retries and double-apply the event.\n */\nexport async function unmarkWebhookEvent(\n store: WebhookEventStore | undefined,\n provider: 'apple' | 'google',\n eventId: string | undefined,\n): Promise<void> {\n if (!store?.unmark || typeof eventId !== 'string') return;\n try {\n await store.unmark(provider, eventId);\n } catch {\n /* best-effort */\n }\n}\n\n/** Default 7-day retention — covers Apple's 3-day retry window plus headroom. */\nconst DEFAULT_TTL_SECONDS = 7 * 24 * 60 * 60;\n\n/**\n * In-memory implementation. Suitable for single-instance dev/test; for\n * production multi-instance use the Redis or Postgres variants so retries to\n * other nodes are still deduped.\n */\nexport class InMemoryWebhookEventStore implements WebhookEventStore {\n private readonly seen = new Map<string, number>();\n\n constructor(private readonly ttlSeconds = DEFAULT_TTL_SECONDS) {}\n\n async markIfNew(provider: 'apple' | 'google', eventId: string): Promise<boolean> {\n this.evictExpired();\n const key = `${provider}:${eventId}`;\n if (this.seen.has(key)) return false;\n this.seen.set(key, Date.now() + this.ttlSeconds * 1000);\n return true;\n }\n\n async unmark(provider: 'apple' | 'google', eventId: string): Promise<void> {\n this.seen.delete(`${provider}:${eventId}`);\n }\n\n private evictExpired(): void {\n const now = Date.now();\n for (const [key, expiresAt] of this.seen) {\n if (expiresAt < now) this.seen.delete(key);\n }\n }\n}\n\n/**\n * Cache-backed implementation — works with any `CacheAdapter`.\n *\n * Uses a `get` → `set` sequence, which is **not** atomic under concurrent\n * retries: two simultaneous calls for the same id can both read null and\n * both return `true`. The downstream store PKs / BullMQ jobId dedup catch\n * this worst case.\n *\n * For Redis deployments prefer `RedisWebhookEventStore` (from\n * `@onesub/server`) which uses a single `SET NX` command and is fully\n * atomic.\n */\nexport class CacheWebhookEventStore implements WebhookEventStore {\n constructor(\n private readonly cache: CacheAdapter,\n private readonly ttlSeconds = DEFAULT_TTL_SECONDS,\n ) {}\n\n async markIfNew(provider: 'apple' | 'google', eventId: string): Promise<boolean> {\n const key = `webhook:event:${provider}:${eventId}`;\n const existing = await this.cache.get<string>(key);\n if (existing) return false;\n await this.cache.set(key, '1', this.ttlSeconds);\n return true;\n }\n\n async unmark(provider: 'apple' | 'google', eventId: string): Promise<void> {\n await this.cache.del(`webhook:event:${provider}:${eventId}`);\n }\n}\n","import type { Request, Response } from 'express';\nimport { randomUUID } from 'node:crypto';\nimport type {\n AppleNotificationPayload,\n OneSubServerConfig,\n SubscriptionInfo,\n} from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { SubscriptionStore, PurchaseStore } from '../store.js';\nimport {\n decodeAppleNotification,\n decodeJws,\n sendAppleConsumptionResponse,\n fetchAppleSubscriptionStatus,\n} from '../providers/apple.js';\nimport { log } from '../logger.js';\nimport { getAppRegistry } from '../apps.js';\nimport { sendError } from '../errors.js';\nimport type { WebhookEventStore } from '../webhook-events.js';\nimport { unmarkWebhookEvent } from '../webhook-events.js';\nimport type { WebhookQueue } from '../webhook-queue.js';\n\nconst APPLE_ACTIVE_TYPES = new Set(['SUBSCRIBED', 'DID_RENEW', 'DID_RECOVER', 'OFFER_REDEEMED']);\n// Granted refunds/revocations only. CONSUMPTION_REQUEST is deliberately NOT\n// here: it is Apple *asking for consumption info* while reviewing a refund\n// request — the refund may be declined, so revoking entitlement (or deleting\n// a purchase row) on it is premature. The actual REFUND/REVOKE notification\n// follows if Apple grants it.\nconst APPLE_CANCELED_TYPES = new Set(['REVOKE', 'REFUND']);\nconst APPLE_EXPIRED_TYPES = new Set(['EXPIRED']);\n\nfunction mapAppleNotificationStatus(\n notificationType: string,\n subtype: string | undefined,\n): SubscriptionInfo['status'] | null {\n if (notificationType === 'DID_FAIL_TO_RENEW') {\n return subtype === 'GRACE_PERIOD'\n ? SUBSCRIPTION_STATUS.GRACE_PERIOD\n : SUBSCRIPTION_STATUS.ON_HOLD;\n }\n if (notificationType === 'GRACE_PERIOD_EXPIRED') return SUBSCRIPTION_STATUS.ON_HOLD;\n if (APPLE_CANCELED_TYPES.has(notificationType)) return SUBSCRIPTION_STATUS.CANCELED;\n if (APPLE_EXPIRED_TYPES.has(notificationType)) return SUBSCRIPTION_STATUS.EXPIRED;\n if (APPLE_ACTIVE_TYPES.has(notificationType)) return SUBSCRIPTION_STATUS.ACTIVE;\n return null;\n}\n\n/** Fully decoded + verified Apple notification transaction data. */\ntype DecodedAppleNotification = NonNullable<Awaited<ReturnType<typeof decodeAppleNotification>>>;\n\n/**\n * The unit of work handed to the state-mutating processor. Deliberately\n * JSON-serializable (plain strings / booleans / nulls) so it survives a\n * round-trip through a Redis-backed queue (BullMQ) unchanged.\n */\nexport interface AppleWebhookWork {\n decoded: DecodedAppleNotification;\n notificationType: string;\n subtype?: string;\n}\n\n/**\n * State-mutating half of the Apple webhook: everything AFTER the gate\n * (payload validation, JWS verification, idempotency markIfNew, bundleId\n * check) has passed. Called from two places:\n * - inline by `handleAppleWebhook` when no queue is configured\n * - by the webhook queue handler (registered in createWebhookRouter)\n * Throws on store failure — the caller decides the retry semantics (source\n * retry via 5xx inline, queue retry + dead-letter in queue mode).\n */\nexport async function processAppleNotification(\n work: AppleWebhookWork,\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Promise<void> {\n const { notificationType, subtype } = work;\n const {\n originalTransactionId,\n transactionId,\n type,\n productId,\n bundleId,\n environment,\n status,\n willRenew,\n expiresAt,\n appAccountToken,\n inAppOwnershipType,\n } = work.decoded;\n\n // The notification names its own app; use that app's Apple credentials rather\n // than whichever app happens to be the default.\n const appleConfigForApp = getAppRegistry(config).configFor({ bundleId }).apple;\n\n const mapped = mapAppleNotificationStatus(notificationType, subtype);\n const finalStatus: SubscriptionInfo['status'] = mapped ?? status;\n\n if (\n notificationType === 'CONSUMPTION_REQUEST' &&\n appleConfigForApp?.consumptionInfoProvider &&\n transactionId &&\n productId\n ) {\n const provider = appleConfigForApp.consumptionInfoProvider;\n const appleConfig = appleConfigForApp;\n void (async () => {\n try {\n const info = await provider({\n transactionId,\n originalTransactionId,\n productId,\n bundleId: bundleId ?? appleConfig.bundleId,\n environment,\n });\n if (info) {\n await sendAppleConsumptionResponse(transactionId, info, appleConfig, {\n sandbox: environment === 'Sandbox',\n });\n }\n } catch (err) {\n log.warn('[onesub/webhook/apple] consumptionInfoProvider failed:', err);\n }\n })();\n }\n\n const isOneTimePurchase = type === 'Consumable' || type === 'Non-Consumable';\n const isRefundOrRevoke = APPLE_CANCELED_TYPES.has(notificationType);\n\n // CONSUMPTION_REQUEST on a one-time purchase is informational — the\n // consumptionInfoProvider hook above is the only work to do. Falling through\n // would run the subscription lookup and a doomed Status API recovery call.\n if (isOneTimePurchase && notificationType === 'CONSUMPTION_REQUEST') {\n return;\n }\n\n if (isOneTimePurchase && isRefundOrRevoke) {\n const lookupId = transactionId ?? originalTransactionId;\n const removed = await purchaseStore.deletePurchaseByTransactionId(lookupId);\n if (!removed) {\n log.warn('[onesub/webhook/apple] IAP refund for unknown transaction:', lookupId);\n }\n return;\n }\n\n const existing = await store.getByTransactionId(originalTransactionId);\n if (existing) {\n const isSubscriptionRefund = isRefundOrRevoke && !isOneTimePurchase;\n const keepEntitlement = isSubscriptionRefund && config.refundPolicy === 'until_expiry';\n\n // If the stored userId was a fallback (originalTransactionId) and we now\n // have a real appAccountToken, correct it. Never overwrite a userId that\n // the client set explicitly (which would differ from originalTransactionId).\n const correctedUserId =\n appAccountToken && existing.userId === originalTransactionId\n ? appAccountToken\n : existing.userId;\n if (correctedUserId !== existing.userId) {\n log.info('[onesub/webhook/apple] correcting userId from originalTransactionId to appAccountToken:', correctedUserId);\n }\n\n const updated: SubscriptionInfo = keepEntitlement\n ? { ...existing, userId: correctedUserId, willRenew: false }\n : {\n ...existing,\n userId: correctedUserId,\n status: finalStatus,\n willRenew,\n expiresAt: expiresAt ?? existing.expiresAt,\n };\n await store.save(updated);\n } else if (appleConfigForApp?.issuerId && appleConfigForApp?.keyId && appleConfigForApp?.privateKey) {\n const fresh = await fetchAppleSubscriptionStatus(originalTransactionId, appleConfigForApp, {\n sandbox: environment === 'Sandbox',\n });\n if (fresh) {\n fresh.userId = appAccountToken ?? originalTransactionId;\n if (inAppOwnershipType === 'FAMILY_SHARED') {\n const source = appAccountToken ? 'appAccountToken' : 'originalTransactionId (fallback)';\n log.info(`[onesub/webhook/apple] FAMILY_SHARED — userId: ${fresh.userId} (source: ${source})`);\n }\n await store.save(fresh);\n } else {\n log.warn(\n '[onesub/webhook/apple] Unknown transaction and Status API returned no record:',\n originalTransactionId,\n );\n }\n } else {\n log.warn(\n '[onesub/webhook/apple] Received notification for unknown transaction (no API creds to recover):',\n originalTransactionId,\n );\n }\n}\n\nexport async function handleAppleWebhook(\n req: Request,\n res: Response,\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n webhookEventStore?: WebhookEventStore,\n webhookQueue?: WebhookQueue,\n): Promise<void> {\n const body = req.body as { signedPayload?: string };\n\n if (!body.signedPayload) {\n sendError(res, 400, ONESUB_ERROR_CODE.MISSING_SIGNED_PAYLOAD, 'Missing signedPayload');\n return;\n }\n\n let payload: AppleNotificationPayload;\n try {\n payload = await decodeJws<AppleNotificationPayload>(\n body.signedPayload,\n config.apple?.skipJwsVerification,\n );\n } catch (err) {\n log.error('[onesub/webhook/apple] Failed to decode signedPayload:', err);\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_SIGNED_PAYLOAD, 'Invalid signedPayload');\n return;\n }\n\n // Idempotency gate. Runs BEFORE any enqueue, so in queue mode duplicate\n // deliveries never produce a second job (complemented by the queue-level\n // jobId dedup below). Capture the marked id once here — it is the only id\n // `unmarkWebhookEvent` may ever release.\n let markedEventId: string | undefined;\n if (webhookEventStore && typeof payload.notificationUUID === 'string') {\n const fresh = await webhookEventStore.markIfNew('apple', payload.notificationUUID);\n if (!fresh) {\n log.info('[onesub/webhook/apple] dedupe: already processed', payload.notificationUUID);\n res.status(200).json({ received: true, deduped: true });\n return;\n }\n markedEventId = payload.notificationUUID;\n }\n\n const decoded = await decodeAppleNotification(payload, config.apple?.skipJwsVerification);\n if (!decoded) {\n res.status(200).json({ received: true });\n return;\n }\n\n // Signature verification proves the notification came from Apple — not that it\n // is for an app we serve. Resolve the notification's bundleId against the app\n // registry, so a multi-app instance accepts every app it is configured for and\n // still rejects everything else.\n const notifiedApp = getAppRegistry(config).configFor({ bundleId: decoded.bundleId });\n if (decoded.bundleId && !notifiedApp.apple) {\n log.warn('[onesub/webhook/apple] No app configured for bundleId:', decoded.bundleId);\n sendError(res, 400, ONESUB_ERROR_CODE.BUNDLE_ID_MISMATCH, 'Bundle ID mismatch');\n return;\n }\n\n const work: AppleWebhookWork = {\n decoded,\n notificationType: payload.notificationType,\n subtype: payload.subtype,\n };\n\n if (webhookQueue) {\n // Queue mode: the cheap gating above ran inline; the state-mutating\n // processing runs in the queue handler. Ack as soon as the job is\n // durably accepted.\n //\n // Idempotency tradeoff: once enqueued, transient handler failures are the\n // QUEUE's retries (backoff + dead-letter), not the source's — so the\n // event stays marked even if the job ultimately fails. After max attempts\n // the job lands in the dead-letter list; admin replay\n // (POST /onesub/admin/webhook-replay/:id) is the recovery path. Unmarking\n // here would let a source retry race the queue's retries and double-apply.\n try {\n await webhookQueue.enqueue<AppleWebhookWork>({\n provider: 'apple',\n // Stable job id feeds queue-level dedup (BullMQ jobId). Random\n // fallback when Apple omits the UUID — no dedup possible anyway.\n eventId: typeof payload.notificationUUID === 'string' ? payload.notificationUUID : randomUUID(),\n payload: work,\n });\n res.status(200).json({ received: true, queued: true });\n } catch (err) {\n // Enqueue itself failed (e.g. Redis down) — the job was never durably\n // accepted, so the queue can't retry it. Fall back to source-retry\n // semantics: unmark + 5xx so Apple redelivers.\n log.error('[onesub/webhook/apple] Failed to enqueue webhook job:', err);\n await unmarkWebhookEvent(webhookEventStore, 'apple', markedEventId);\n sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, 'Failed to update subscription');\n }\n return;\n }\n\n // Inline mode (no queue configured): process synchronously inside the\n // request. On failure, un-mark the idempotency key so Apple's retry is\n // processed instead of being deduped — otherwise a transient store failure\n // drops the event forever.\n try {\n await processAppleNotification(work, config, store, purchaseStore);\n res.status(200).json({ received: true });\n } catch (err) {\n log.error('[onesub/webhook/apple] Store update error:', err);\n await unmarkWebhookEvent(webhookEventStore, 'apple', markedEventId);\n sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, 'Failed to update subscription');\n }\n}\n","import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { randomUUID } from 'node:crypto';\nimport type { Request, Response } from 'express';\nimport type { GoogleNotificationPayload, OneSubServerConfig, SubscriptionInfo } from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { SubscriptionStore, PurchaseStore } from '../store.js';\nimport {\n decodeGoogleNotification,\n decodeGoogleVoidedNotification,\n decodeGoogleOneTimeProductNotification,\n validateGoogleReceipt,\n acknowledgeGoogleProduct,\n isGoogleActiveNotification,\n isGoogleCanceledNotification,\n isGoogleExpiredNotification,\n isGoogleGracePeriodNotification,\n isGoogleOnHoldNotification,\n isGooglePausedNotification,\n isGooglePriceChangeConfirmedNotification,\n} from '../providers/google.js';\nimport type {\n GoogleVoidedNotification,\n GoogleOneTimeProductNotification,\n} from '../providers/google.js';\nimport { log } from '../logger.js';\nimport { getAppRegistry } from '../apps.js';\nimport { sendError } from '../errors.js';\nimport type { WebhookEventStore } from '../webhook-events.js';\nimport { unmarkWebhookEvent } from '../webhook-events.js';\nimport type { WebhookQueue } from '../webhook-queue.js';\n\nconst GOOGLE_JWKS_URL = 'https://www.googleapis.com/oauth2/v3/certs';\n\nlet googleJwks: ReturnType<typeof createRemoteJWKSet> | null = null;\n\nfunction getGoogleJwks(): ReturnType<typeof createRemoteJWKSet> {\n if (!googleJwks) googleJwks = createRemoteJWKSet(new URL(GOOGLE_JWKS_URL));\n return googleJwks;\n}\n\nexport async function verifyGooglePushToken(\n req: Request,\n expectedAudience: string,\n expectedServiceAccountEmail?: string,\n): Promise<boolean> {\n const authHeader = req.headers['authorization'];\n if (typeof authHeader !== 'string' || !authHeader.startsWith('Bearer ')) return false;\n try {\n const { payload } = await jwtVerify(authHeader.slice('Bearer '.length).trim(), getGoogleJwks(), {\n audience: expectedAudience,\n // Any Google Cloud principal can mint an OIDC token with an arbitrary\n // audience — issuer (and, when configured, the push service-account\n // email) narrows acceptance to real Pub/Sub push auth tokens. Google\n // documents both issuer forms, so accept either.\n issuer: ['https://accounts.google.com', 'accounts.google.com'],\n });\n if (expectedServiceAccountEmail) {\n if (payload['email'] !== expectedServiceAccountEmail || payload['email_verified'] !== true) {\n return false;\n }\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** Decoded subscription RTDN — inline shape returned by decodeGoogleNotification. */\ntype DecodedGoogleSubscriptionNotification = NonNullable<ReturnType<typeof decodeGoogleNotification>>;\n\n/**\n * The unit of work handed to the state-mutating processor. Deliberately\n * JSON-serializable (a discriminated union of plain decoded notification\n * objects) so it survives a round-trip through a Redis-backed queue (BullMQ)\n * unchanged.\n */\nexport type GoogleWebhookWork =\n | { kind: 'voided'; voided: GoogleVoidedNotification }\n | { kind: 'oneTimeProduct'; oneTimeProduct: GoogleOneTimeProductNotification }\n | { kind: 'subscription'; notification: DecodedGoogleSubscriptionNotification };\n\n/** Per-kind log prefixes / error messages, kept identical to the pre-refactor inline handlers. */\nconst GOOGLE_FAILURE_MESSAGES: Record<GoogleWebhookWork['kind'], { logPrefix: string; message: string }> = {\n voided: {\n logPrefix: '[onesub/webhook/google] voided notification error:',\n message: 'Failed to process voided notification',\n },\n oneTimeProduct: {\n logPrefix: '[onesub/webhook/google] oneTimeProduct error:',\n message: 'Failed to process oneTimeProduct notification',\n },\n subscription: {\n logPrefix: '[onesub/webhook/google] Error handling notification:',\n message: 'Failed to process notification',\n },\n};\n\n/**\n * Resolves an RTDN's package to an app.\n *\n * When no configured app declares a packageName the instance is in legacy \"open\n * mode\" — it accepts notifications for any package and uses the default\n * credentials. Once any app names a package, only known packages are served.\n */\nfunction googleResolver(config: OneSubServerConfig) {\n const registry = getAppRegistry(config);\n const restricted = registry.apps.some((app) => !!app.google?.packageName);\n return {\n registry,\n serves: (packageName: string): boolean =>\n !restricted || !!registry.configFor({ appId: packageName }).google,\n googleFor: (packageName: string) =>\n restricted\n ? registry.configFor({ appId: packageName }).google\n : (registry.defaultApp?.google ?? config.google),\n };\n}\n\n/**\n * State-mutating half of the Google webhook: everything AFTER the gate\n * (push-token auth, body validation, idempotency markIfNew, packageName\n * check, RTDN decode) has passed. Called from two places:\n * - inline by `handleGoogleWebhook` when no queue is configured\n * - by the webhook queue handler (registered in createWebhookRouter)\n * Throws on store failure — the caller decides the retry semantics (source\n * retry via 5xx inline, queue retry + dead-letter in queue mode).\n */\nexport async function processGoogleNotification(\n work: GoogleWebhookWork,\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Promise<void> {\n // Each notification names its own package; use that app's Google credentials\n // rather than whichever app happens to be the default.\n const { googleFor } = googleResolver(config);\n\n if (work.kind === 'voided') {\n const { voided } = work;\n if (voided.productType === 1) {\n const existing = await store.getByTransactionId(voided.purchaseToken);\n if (existing) {\n const updated = config.refundPolicy === 'until_expiry'\n ? { ...existing, willRenew: false }\n : { ...existing, status: SUBSCRIPTION_STATUS.CANCELED };\n await store.save(updated);\n } else {\n log.warn('[onesub/webhook/google] voided subscription for unknown purchaseToken:', voided.purchaseToken);\n }\n } else {\n const removed = await purchaseStore.deletePurchaseByTransactionId(voided.orderId);\n if (!removed) {\n log.warn('[onesub/webhook/google] voided IAP for unknown orderId:', voided.orderId);\n }\n }\n return;\n }\n\n // oneTimeProductNotification — consumable / non-consumable purchase signal.\n // The notification carries no userId, so we cannot create a purchase record\n // here (the client's POST /onesub/purchase/validate is the authoritative path).\n // For PURCHASED we acknowledge to prevent the 3-day auto-refund window.\n if (work.kind === 'oneTimeProduct') {\n const { notificationType, purchaseToken, sku } = work.oneTimeProduct;\n if (notificationType === 1 /* PURCHASED */) {\n log.info('[onesub/webhook/google] oneTimeProduct PURCHASED:', sku);\n const googleCfg = googleFor(work.oneTimeProduct.packageName);\n if (googleCfg?.serviceAccountKey && googleCfg.packageName) {\n void acknowledgeGoogleProduct(purchaseToken, sku, googleCfg).catch(\n (err) => log.warn(`[onesub/webhook/google] oneTimeProduct ack failed for SKU ${sku} — 3-day auto-refund risk:`, err),\n );\n }\n } else {\n // notificationType === 2: CANCELED (user aborted before purchase completed)\n log.info('[onesub/webhook/google] oneTimeProduct CANCELED (pre-completion):', sku);\n }\n return;\n }\n\n const { notificationType, purchaseToken, subscriptionId, packageName } = work.notification;\n\n const existing = await store.getByTransactionId(purchaseToken);\n\n let finalStatus: SubscriptionInfo['status'];\n if (isGoogleActiveNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.ACTIVE;\n } else if (isGoogleGracePeriodNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.GRACE_PERIOD;\n } else if (isGoogleOnHoldNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.ON_HOLD;\n } else if (isGooglePausedNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.PAUSED;\n } else if (isGooglePriceChangeConfirmedNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.ACTIVE;\n } else if (isGoogleCanceledNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.CANCELED;\n } else if (isGoogleExpiredNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.EXPIRED;\n } else {\n // Unknown/benign notification types (e.g. PAUSE_SCHEDULE_CHANGED,\n // DEFERRED) must not resurrect a canceled/expired record — preserve the\n // stored status; the re-fetch below corrects it when credentials exist.\n finalStatus = existing?.status ?? SUBSCRIPTION_STATUS.ACTIVE;\n }\n\n const subGoogleCfg = googleFor(packageName);\n\n if (isGooglePriceChangeConfirmedNotification(notificationType) && subGoogleCfg?.onPriceChangeConfirmed) {\n const hook = subGoogleCfg.onPriceChangeConfirmed;\n void Promise.resolve()\n .then(() => hook({ purchaseToken, subscriptionId, packageName }))\n .catch((err) => log.warn('[onesub/webhook/google] onPriceChangeConfirmed hook failed:', err));\n }\n\n if (existing) {\n let updated: SubscriptionInfo = { ...existing, status: finalStatus };\n\n if (subGoogleCfg?.serviceAccountKey) {\n const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId, subGoogleCfg);\n if (fresh) {\n // Preserve grace/on-hold only when the NOTIFICATION said so — a\n // finalStatus inherited from the stored record (unknown types above)\n // must not block the re-fetched status, or a lost recovery RTDN\n // leaves the record stuck on_hold while Google reports active.\n const preserveNotificationStatus =\n isGoogleGracePeriodNotification(notificationType) ||\n isGoogleOnHoldNotification(notificationType);\n updated = {\n ...existing,\n status: preserveNotificationStatus ? finalStatus : fresh.status,\n expiresAt: fresh.expiresAt,\n willRenew: fresh.willRenew,\n autoResumeTime: fresh.autoResumeTime,\n linkedPurchaseToken: fresh.linkedPurchaseToken ?? existing.linkedPurchaseToken,\n };\n }\n }\n\n await store.save(updated);\n } else {\n if (subGoogleCfg?.serviceAccountKey) {\n const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId, subGoogleCfg);\n if (fresh) {\n // Consume the account identity out of the record: it seeds the\n // placeholder userId, but must never be persisted (validate route\n // strips it the same way; stores/status would leak it otherwise).\n const boundAccountId = fresh.boundAccountId;\n delete fresh.boundAccountId;\n if (fresh.linkedPurchaseToken) {\n const previous = await store.getByTransactionId(fresh.linkedPurchaseToken);\n fresh.userId = previous ? previous.userId : boundAccountId ?? purchaseToken;\n if (previous) {\n log.info(`[onesub/webhook/google] inherited userId ${previous.userId} from linkedPurchaseToken ${fresh.linkedPurchaseToken} → new token ${purchaseToken}`);\n }\n } else {\n fresh.userId = boundAccountId ?? purchaseToken;\n }\n await store.save(fresh);\n }\n } else {\n log.warn('[onesub/webhook/google] Unknown purchase token and no serviceAccountKey to re-fetch:', purchaseToken);\n }\n }\n}\n\nexport async function handleGoogleWebhook(\n req: Request,\n res: Response,\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n webhookEventStore?: WebhookEventStore,\n webhookQueue?: WebhookQueue,\n): Promise<void> {\n // Push auth runs before the payload is decoded, so the app is not known yet.\n // Each app pushes from its own GCP project (its own service account), so accept\n // the token when it verifies against any configured app's push identity.\n const pushIdentities = getAppRegistry(config)\n .apps.map((app) => app.google)\n .filter((g): g is NonNullable<typeof g> => !!g?.pushAudience);\n\n if (pushIdentities.length > 0) {\n let authenticated = false;\n for (const google of pushIdentities) {\n if (await verifyGooglePushToken(req, google.pushAudience!, google.pushServiceAccountEmail)) {\n authenticated = true;\n break;\n }\n }\n if (!authenticated) {\n sendError(res, 401, ONESUB_ERROR_CODE.UNAUTHORIZED, 'Unauthorized');\n return;\n }\n }\n\n const body = req.body as Partial<GoogleNotificationPayload>;\n\n if (!body.message?.data) {\n sendError(res, 400, ONESUB_ERROR_CODE.MISSING_MESSAGE_DATA, 'Missing message.data');\n return;\n }\n\n // Idempotency gate. Runs BEFORE any enqueue, so in queue mode duplicate\n // Pub/Sub deliveries never produce a second job (complemented by the\n // queue-level jobId dedup below). Capture the marked id once here — it is\n // the only id `unmarkWebhookEvent` may ever release.\n let markedEventId: string | undefined;\n if (webhookEventStore && typeof body.message.messageId === 'string') {\n const fresh = await webhookEventStore.markIfNew('google', body.message.messageId);\n if (!fresh) {\n log.info('[onesub/webhook/google] dedupe: already processed', body.message.messageId);\n res.status(200).json({ received: true, deduped: true });\n return;\n }\n markedEventId = body.message.messageId;\n }\n\n // Decode + packageName gating stays inline (cheap, and 4xx responses must\n // come from the request cycle — a queued job can't tell the source \"don't\n // retry this payload\").\n let work: GoogleWebhookWork;\n\n // The RTDN names its own package. Accept it when a configured app serves that\n // package, so a multi-app instance takes notifications for every app it knows\n // and still rejects the rest.\n const { serves: servesPackage } = googleResolver(config);\n\n const voided = decodeGoogleVoidedNotification(body as GoogleNotificationPayload);\n if (voided) {\n if (!servesPackage(voided.packageName)) {\n log.warn('[onesub/webhook/google] voided package name not served:', voided.packageName);\n sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, 'Package name mismatch');\n return;\n }\n work = { kind: 'voided', voided };\n } else {\n const oneTimeProduct = decodeGoogleOneTimeProductNotification(body as GoogleNotificationPayload);\n if (oneTimeProduct) {\n if (!servesPackage(oneTimeProduct.packageName)) {\n log.warn('[onesub/webhook/google] oneTimeProduct package name not served:', oneTimeProduct.packageName);\n sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, 'Package name mismatch');\n return;\n }\n work = { kind: 'oneTimeProduct', oneTimeProduct };\n } else {\n const notification = decodeGoogleNotification(body as GoogleNotificationPayload);\n if (!notification) {\n res.status(200).json({ received: true });\n return;\n }\n if (!servesPackage(notification.packageName)) {\n log.warn('[onesub/webhook/google] Package name not served:', notification.packageName);\n sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, 'Package name mismatch');\n return;\n }\n work = { kind: 'subscription', notification };\n }\n }\n\n if (webhookQueue) {\n // Queue mode: the cheap gating above ran inline; the state-mutating\n // processing runs in the queue handler. Ack as soon as the job is\n // durably accepted.\n //\n // Idempotency tradeoff: once enqueued, transient handler failures are the\n // QUEUE's retries (backoff + dead-letter), not Pub/Sub's — so the event\n // stays marked even if the job ultimately fails. After max attempts the\n // job lands in the dead-letter list; admin replay\n // (POST /onesub/admin/webhook-replay/:id) is the recovery path. Unmarking\n // here would let a Pub/Sub retry race the queue's retries and double-apply.\n try {\n await webhookQueue.enqueue<GoogleWebhookWork>({\n provider: 'google',\n // Stable job id feeds queue-level dedup (BullMQ jobId). Random\n // fallback when the push lacks a messageId — no dedup possible anyway.\n eventId:\n typeof body.message.messageId === 'string' ? body.message.messageId : randomUUID(),\n payload: work,\n });\n res.status(200).json({ received: true, queued: true });\n } catch (err) {\n // Enqueue itself failed (e.g. Redis down) — the job was never durably\n // accepted, so the queue can't retry it. Fall back to source-retry\n // semantics: unmark + 5xx so Pub/Sub redelivers.\n log.error('[onesub/webhook/google] Failed to enqueue webhook job:', err);\n await unmarkWebhookEvent(webhookEventStore, 'google', markedEventId);\n sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, GOOGLE_FAILURE_MESSAGES[work.kind].message);\n }\n return;\n }\n\n // Inline mode (no queue configured): process synchronously inside the\n // request. On failure, un-mark the idempotency key so the Pub/Sub retry is\n // processed instead of being deduped — otherwise a transient store failure\n // drops the event forever.\n try {\n await processGoogleNotification(work, config, store, purchaseStore);\n res.status(200).json({ received: true });\n } catch (err) {\n const { logPrefix, message } = GOOGLE_FAILURE_MESSAGES[work.kind];\n log.error(logPrefix, err);\n await unmarkWebhookEvent(webhookEventStore, 'google', markedEventId);\n sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, message);\n }\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport type { OneSubServerConfig } from '@onesub/shared';\nimport { ROUTES } from '@onesub/shared';\nimport type { SubscriptionStore, PurchaseStore } from '../store.js';\nimport type { WebhookEventStore } from '../webhook-events.js';\nimport type { WebhookQueue } from '../webhook-queue.js';\nimport { handleAppleWebhook, processAppleNotification } from './webhook-apple.js';\nimport type { AppleWebhookWork } from './webhook-apple.js';\nimport { handleGoogleWebhook, processGoogleNotification } from './webhook-google.js';\nimport type { GoogleWebhookWork } from './webhook-google.js';\n\n/**\n * Retry / durability semantics.\n *\n * Inline mode (no `webhookQueue`): both Apple and Google retry on any\n * non-2xx response.\n * 4xx — payload is unusable; do NOT retry.\n * 5xx — transient failure; source will retry.\n * 2xx — processed or intentionally ignored.\n *\n * Queue mode (`webhookQueue` configured): the route still owns every 4xx\n * (validation, auth, bundleId/packageName, dedup) inline, but 200s as soon as\n * the decoded work is enqueued. Processing failures are then the QUEUE's\n * retries; exhausted jobs land in the dead-letter list for admin replay.\n */\nexport function createWebhookRouter(\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n webhookEventStore?: WebhookEventStore,\n webhookQueue?: WebhookQueue,\n): Router {\n // Register the worker handler exactly once, at router creation — it closes\n // over config/stores so job payloads stay JSON-serializable (BullMQ ships\n // them through Redis; live objects would not survive the round-trip).\n if (webhookQueue) {\n webhookQueue.setHandler(async (job) => {\n if (job.provider === 'apple') {\n await processAppleNotification(job.payload as AppleWebhookWork, config, store, purchaseStore);\n } else {\n await processGoogleNotification(job.payload as GoogleWebhookWork, config, store, purchaseStore);\n }\n });\n }\n\n const router = Router();\n\n router.post(ROUTES.WEBHOOK_APPLE, (req: Request, res: Response) =>\n handleAppleWebhook(req, res, config, store, purchaseStore, webhookEventStore, webhookQueue),\n );\n\n router.post(ROUTES.WEBHOOK_GOOGLE, (req: Request, res: Response) =>\n handleGoogleWebhook(req, res, config, store, purchaseStore, webhookEventStore, webhookQueue),\n );\n\n return router;\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type {\n ValidatePurchaseResponse,\n PurchaseStatusResponse,\n PurchaseInfo,\n OneSubServerConfig,\n} from '@onesub/shared';\nimport { ROUTES, PURCHASE_TYPE, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { PurchaseStore } from '../store.js';\nimport { getAppRegistry, peekAppleBundleId } from '../apps.js';\nimport { validateAppleConsumableReceipt } from '../providers/apple.js';\nimport {\n validateGoogleProductReceipt,\n consumeGoogleProductReceipt,\n acknowledgeGoogleProduct,\n} from '../providers/google.js';\nimport { log } from '../logger.js';\nimport { sendError, sendZodError } from '../errors.js';\n\nconst NO_PURCHASE = { valid: false, purchase: null } as const;\n\nconst validatePurchaseSchema = z.object({\n platform: z.enum(['apple', 'google']),\n receipt: z.string().min(1).max(10000),\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256),\n type: z.enum([PURCHASE_TYPE.CONSUMABLE, PURCHASE_TYPE.NON_CONSUMABLE]),\n /** Which app this receipt belongs to. Optional — see OneSubServerConfig.apps. */\n appId: z.string().min(1).max(256).optional(),\n});\n\nconst purchaseStatusQuerySchema = z.object({\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256).optional(),\n});\n\nexport function createPurchaseRouter(\n config: OneSubServerConfig,\n purchaseStore: PurchaseStore\n): Router {\n const router = Router();\n const registry = getAppRegistry(config);\n\n /**\n * POST /onesub/purchase/validate\n *\n * Validate a receipt for a consumable or non-consumable purchase.\n * - Non-consumables: rejected if the user already owns the product.\n * - Consumables: always recorded (multiple purchases allowed).\n *\n * Uses platform-specific product validators (purchases.products API) instead\n * of the subscription validator. Key differences:\n * - Apple: validates tx.type === 'Consumable'|'Non-Consumable', uses transactionId\n * (not originalTransactionId) as the dedup key, enforces 72h receipt age\n * - Google: checks consumptionState for consumables (replay prevention),\n * enforces 72h receipt age, uses orderId as the dedup key\n */\n router.post(ROUTES.VALIDATE_PURCHASE, async (req: Request, res: Response) => {\n let body: z.infer<typeof validatePurchaseSchema>;\n\n try {\n body = validatePurchaseSchema.parse(req.body);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err, NO_PURCHASE);\n return;\n }\n throw err;\n }\n\n const { platform, receipt, userId, productId, type, appId } = body;\n\n // An Apple receipt names its own app; a Google purchase token does not, so it\n // relies on appId (or the default app).\n const appConfig = registry.configFor({\n appId,\n bundleId: platform === 'apple' ? peekAppleBundleId(receipt) : undefined,\n });\n\n try {\n // Non-consumable idempotent restore. If the user already owns this\n // product, return the *recorded* purchase — with its real transactionId —\n // as a `restored` success instead of a 409 error.\n //\n // Owning a non-consumable IS success. The old behaviour (409 +\n // `purchase: null`) forced the SDK to synthesize a restored result with\n // no transactionId, so downstream consumers that re-entitle off\n // `result.transactionId` (e.g. a host app registering its own pass)\n // received `undefined` and silently failed — the user was charged but\n // never granted. Returning the stored record keeps the receipt chain\n // intact and is safe: ownership was already proven by the prior validated\n // purchase recorded under this userId (same idempotent-restore semantics\n // as the transactionId-match path below).\n if (type === PURCHASE_TYPE.NON_CONSUMABLE) {\n const owned = (await purchaseStore.getPurchasesByUserId(userId)).find(\n (p) => p.productId === productId,\n );\n if (owned) {\n const response: ValidatePurchaseResponse = {\n valid: true,\n purchase: { ...owned, userId },\n action: 'restored',\n };\n res.status(200).json(response);\n return;\n }\n }\n\n // Validate receipt via the appropriate platform-specific product validator.\n // Note: these are separate from the subscription validators — they call\n // purchases.products (not purchases.subscriptions) and apply product-specific\n // security checks (type validation, consumptionState, receipt age).\n let transactionId: string | null = null;\n let purchasedAt: string = new Date().toISOString();\n // Account binding token baked into the receipt at purchase time (Apple\n // appAccountToken / Google obfuscatedExternalAccountId). Null when the\n // client didn't set one (legacy purchases / apps not yet on the bound SDK).\n let boundAccountId: string | null = null;\n\n if (platform === 'apple') {\n if (!appConfig.apple) {\n sendError(res, 500, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, 'Apple configuration not provided', NO_PURCHASE);\n return;\n }\n const result = await validateAppleConsumableReceipt(receipt, appConfig.apple, productId);\n if (result) {\n transactionId = result.transactionId;\n purchasedAt = result.purchasedAt;\n boundAccountId = result.appAccountToken ?? null;\n }\n } else {\n if (!appConfig.google) {\n sendError(res, 500, ONESUB_ERROR_CODE.GOOGLE_CONFIG_MISSING, 'Google configuration not provided', NO_PURCHASE);\n return;\n }\n const result = await validateGoogleProductReceipt(\n receipt,\n productId,\n appConfig.google,\n type === PURCHASE_TYPE.CONSUMABLE ? 'consumable' : 'non_consumable',\n );\n if (result) {\n transactionId = result.transactionId;\n purchasedAt = result.purchasedAt;\n boundAccountId = result.obfuscatedExternalAccountId ?? null;\n }\n }\n\n if (!transactionId) {\n sendError(res, 422, ONESUB_ERROR_CODE.RECEIPT_VALIDATION_FAILED, 'Receipt validation failed', NO_PURCHASE);\n return;\n }\n\n // Account-binding guard (payment-bypass defense). If the receipt carries an\n // appAccountToken (a stable account id the client baked in at purchase time),\n // the purchase may ONLY be attributed to a matching userId. This blocks the\n // core exploit: a leaked/valid JWS being SAVED or REASSIGNED to an\n // attacker-chosen userId (both the fresh-save and reassign paths below trust\n // the request-body userId otherwise). Backward-compatible: purchases made\n // before the client set appAccountToken have no token → guard is skipped and\n // current behaviour (incl. reinstall reassignment) is unchanged.\n // Apple compares case-insensitively (appAccountToken is normalized to a\n // lowercase UUID in the signed transaction, so a host passing the same\n // UUID uppercased must not be rejected). Google ids are verbatim — a\n // case-insensitive compare would let a case-flipped userId through on\n // hosts with case-sensitive ids (e.g. Firebase UIDs).\n const bindingMismatch = platform === 'apple'\n ? boundAccountId && boundAccountId.toLowerCase() !== userId.toLowerCase()\n : boundAccountId && boundAccountId !== userId;\n if (bindingMismatch) {\n log.warn(\n `[onesub/purchase] account binding mismatch for transaction ${transactionId}: token does not match userId ${userId}`,\n );\n sendError(\n res,\n 409,\n ONESUB_ERROR_CODE.TRANSACTION_BELONGS_TO_OTHER_USER,\n 'TRANSACTION_BELONGS_TO_OTHER_USER',\n NO_PURCHASE,\n );\n return;\n }\n\n // Check if this transactionId has been seen before.\n // - Same userId → idempotent restore (e.g. network retry)\n // - Different userId + non_consumable → reassign (device reinstall)\n // - Different userId + consumable → reject (receipts can't be reused)\n // - No existing row → fresh purchase, INSERT below\n const existing = await purchaseStore.getPurchaseByTransactionId(transactionId);\n let action: 'new' | 'restored' = 'new';\n\n if (existing) {\n action = 'restored';\n if (existing.userId !== userId) {\n if (type === PURCHASE_TYPE.NON_CONSUMABLE) {\n await purchaseStore.reassignPurchase(transactionId, userId);\n log.info(\n `[onesub/purchase] reassigned transaction ${transactionId} from ${existing.userId} to ${userId}`,\n );\n } else {\n sendError(\n res,\n 409,\n ONESUB_ERROR_CODE.TRANSACTION_BELONGS_TO_OTHER_USER,\n 'TRANSACTION_BELONGS_TO_OTHER_USER',\n NO_PURCHASE,\n );\n return;\n }\n }\n const response: ValidatePurchaseResponse = {\n valid: true,\n purchase: { ...existing, userId },\n action,\n };\n res.status(200).json(response);\n return;\n }\n\n const purchase: PurchaseInfo = {\n userId,\n productId,\n platform,\n type,\n transactionId,\n purchasedAt,\n quantity: 1,\n };\n\n await purchaseStore.savePurchase(purchase);\n\n // Google requires acknowledgement within 3 days of purchase or the\n // transaction is auto-refunded.\n // - Consumables: :consume implicitly acknowledges, so consume is enough.\n // - Non-consumables: must call :acknowledge explicitly.\n // Both must run after savePurchase — if called before, a DB failure would\n // leave the receipt acknowledged/consumed but the entitlement ungranted\n // with no retry path.\n if (platform === 'google' && appConfig.google) {\n if (type === PURCHASE_TYPE.CONSUMABLE) {\n void consumeGoogleProductReceipt(receipt, productId, appConfig.google);\n } else {\n void acknowledgeGoogleProduct(receipt, productId, appConfig.google);\n }\n }\n\n const response: ValidatePurchaseResponse = {\n valid: true,\n purchase,\n action,\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/purchase/validate] Unexpected error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, 'Internal server error during purchase validation', NO_PURCHASE);\n }\n });\n\n /**\n * GET /onesub/purchase/status?userId=xxx[&productId=yyy]\n *\n * Returns all purchases for a user, optionally filtered by productId.\n */\n router.get(ROUTES.PURCHASE_STATUS, async (req: Request, res: Response) => {\n let query: z.infer<typeof purchaseStatusQuerySchema>;\n\n try {\n query = purchaseStatusQuerySchema.parse(req.query);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err, { purchases: [] });\n return;\n }\n throw err;\n }\n\n const { userId, productId } = query;\n\n try {\n let purchases = await purchaseStore.getPurchasesByUserId(userId);\n\n if (productId !== undefined) {\n purchases = purchases.filter((p) => p.productId === productId);\n }\n\n const response: PurchaseStatusResponse = { purchases };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/purchase/status] Store error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error', { purchases: [] });\n }\n });\n\n return router;\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type {\n Entitlement,\n EntitlementResponse,\n EntitlementStatus,\n EntitlementsConfig,\n EntitlementsResponse,\n OneSubServerConfig,\n PurchaseInfo,\n SubscriptionInfo,\n} from '@onesub/shared';\nimport { ROUTES, SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE, PURCHASE_TYPE } from '@onesub/shared';\nimport type { PurchaseStore, SubscriptionStore } from '../store.js';\nimport { log } from '../logger.js';\nimport { sendError } from '../errors.js';\n\n/**\n * Evaluate a single entitlement for a user.\n *\n * A user is entitled when EITHER condition holds for any productId in\n * `entitlement.productIds`:\n * 1. an active subscription (status === active|grace_period AND\n * expiresAt > now) for that productId exists\n * 2. a non-consumable purchase for that productId exists\n *\n * Consumables are intentionally excluded — they grant a one-time resource,\n * not an ongoing right.\n *\n * Subscription is preferred over purchase when both match (subs carry an\n * expiry; non-consumables are forever) so the source field skews toward the\n * more \"interesting\" signal for ops/analytics.\n */\nexport async function evaluateEntitlement(\n userId: string,\n entitlement: Entitlement,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Promise<EntitlementStatus> {\n const productIdSet = new Set(entitlement.productIds);\n const now = Date.now();\n\n // 1. Check subscriptions first (richer signal — has expiry).\n const subs = await store.getAllByUserId(userId);\n for (const sub of subs) {\n if (!productIdSet.has(sub.productId)) continue;\n const statusAllows =\n sub.status === SUBSCRIPTION_STATUS.ACTIVE ||\n sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD;\n if (!statusAllows) continue;\n if (new Date(sub.expiresAt).getTime() <= now) continue;\n return {\n active: true,\n source: 'subscription',\n productId: sub.productId,\n expiresAt: sub.expiresAt,\n };\n }\n\n // 2. Check non-consumable purchases.\n const purchases = await purchaseStore.getPurchasesByUserId(userId);\n for (const p of purchases) {\n if (p.type !== PURCHASE_TYPE.NON_CONSUMABLE) continue;\n if (!productIdSet.has(p.productId)) continue;\n return {\n active: true,\n source: 'purchase',\n productId: p.productId,\n };\n }\n\n return { active: false, source: null };\n}\n\nconst userIdSchema = z.string().min(1).max(256);\nconst entitlementIdSchema = z.string().min(1).max(128);\n\nexport function createEntitlementRouter(\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Router | null {\n // Only mount when entitlements are configured. Without this guard, the\n // routes would always 503 — better to make the absence loud (404 from\n // express) so misconfiguration is obvious during dev.\n if (!config.entitlements || Object.keys(config.entitlements).length === 0) {\n return null;\n }\n const entitlements: EntitlementsConfig = config.entitlements;\n\n const router = Router();\n\n /**\n * GET /onesub/entitlement?userId=&id=premium\n * Single entitlement check.\n */\n router.get(ROUTES.ENTITLEMENT, async (req: Request, res: Response) => {\n let userId: string;\n let id: string;\n try {\n userId = userIdSchema.parse(req.query['userId']);\n id = entitlementIdSchema.parse(req.query['id']);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId and id are required');\n return;\n }\n\n const entitlement = entitlements[id];\n if (!entitlement) {\n sendError(res, 404, ONESUB_ERROR_CODE.ENTITLEMENT_NOT_FOUND, `Unknown entitlement: ${id}`);\n return;\n }\n\n try {\n const status = await evaluateEntitlement(userId, entitlement, store, purchaseStore);\n const response: EntitlementResponse = { id, ...status };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/entitlement] evaluation error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n /**\n * GET /onesub/entitlements?userId=\n * Evaluate every configured entitlement in one round-trip — useful on\n * app launch / login when the host wants the full entitlement map.\n */\n router.get(ROUTES.ENTITLEMENTS, async (req: Request, res: Response) => {\n let userId: string;\n try {\n userId = userIdSchema.parse(req.query['userId']);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId is required');\n return;\n }\n\n try {\n const entries = await Promise.all(\n Object.entries(entitlements).map(async ([id, entitlement]) => {\n const status = await evaluateEntitlement(userId, entitlement, store, purchaseStore);\n return [id, status] as const;\n }),\n );\n const response: EntitlementsResponse = {\n entitlements: Object.fromEntries(entries),\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/entitlements] evaluation error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error', { entitlements: {} });\n }\n });\n\n return router;\n}\n\n// Keep the unused-type imports honest — these are exported as documentation\n// for callers writing their own evaluators.\nexport type { Entitlement, EntitlementStatus, PurchaseInfo, SubscriptionInfo };\n","import { timingSafeEqual } from 'node:crypto';\n\n/**\n * Constant-time comparison for shared secrets (admin secret, offer secret).\n *\n * A plain `!==` short-circuits on the first differing byte, letting an\n * attacker recover the secret byte-by-byte from response timing.\n * `crypto.timingSafeEqual` requires equal-length buffers, so a length\n * mismatch returns `false` immediately — this leaks only the secret's\n * LENGTH (not its content), which is an accepted trade-off.\n */\nexport function secretsEqual(provided: string, expected: string): boolean {\n const providedBuf = Buffer.from(provided, 'utf8');\n const expectedBuf = Buffer.from(expected, 'utf8');\n if (providedBuf.length !== expectedBuf.length) return false;\n return timingSafeEqual(providedBuf, expectedBuf);\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type {\n CustomerProfileResponse,\n EntitlementStatus,\n OneSubServerConfig,\n PurchaseInfo,\n ListSubscriptionsResponse,\n Platform,\n SubscriptionStatus,\n} from '@onesub/shared';\nimport { PURCHASE_TYPE, ONESUB_ERROR_CODE, ROUTES, SUBSCRIPTION_STATUS } from '@onesub/shared';\nimport type { PurchaseStore, SubscriptionStore } from '../store.js';\nimport { evaluateEntitlement } from './entitlements.js';\nimport { sendError, sendZodError } from '../errors.js';\nimport type { WebhookQueue } from '../webhook-queue.js';\nimport { fetchAppleSubscriptionStatus } from '../providers/apple.js';\nimport { secretsEqual } from './secret-compare.js';\n\nconst ADMIN_SECRET_HEADER = 'x-admin-secret';\n\n/**\n * Admin routes for testing / operational tasks. Require `X-Admin-Secret`\n * header matching config.adminSecret. If config.adminSecret is not set, the\n * entire admin router is not mounted (returns 404 from the parent router).\n *\n * Endpoints:\n * DELETE /onesub/purchase/admin/:userId/:productId\n * → reset a non-consumable so the user can test the purchase flow again\n *\n * POST /onesub/purchase/admin/grant\n * → manually insert a purchase record (skips store verification)\n * body: { userId, productId, platform, type?, transactionId? }\n *\n * POST /onesub/purchase/admin/transfer\n * → reassign a transactionId to a new userId (device migration)\n *\n * GET /onesub/admin/subscriptions?userId=&status=&productId=&platform=&limit=&offset=\n * → filtered/paginated subscription list (used by dashboard + scripts)\n *\n * GET /onesub/admin/subscriptions/:transactionId\n * → single subscription record by originalTransactionId (dashboard detail page)\n *\n * GET /onesub/admin/customers/:userId\n * → bundle of every record onesub knows for one user (subs + purchases +\n * entitlements when configured). Backs the dashboard customer detail page.\n */\nexport function createAdminRouter(\n config: OneSubServerConfig,\n purchaseStore: PurchaseStore,\n store: SubscriptionStore,\n webhookQueue?: WebhookQueue,\n): Router | null {\n if (!config.adminSecret) return null;\n\n const router = Router();\n const adminSecret = config.adminSecret;\n\n // Auth middleware — applied to both admin scopes. Without this guard, a\n // misconfigured mount on the parent root would expose admin endpoints\n // alongside host-app routes (e.g. /health).\n const adminAuth = (req: Request, res: Response, next: () => void) => {\n const provided = req.headers[ADMIN_SECRET_HEADER];\n if (typeof provided !== 'string' || !secretsEqual(provided, adminSecret)) {\n sendError(res, 401, ONESUB_ERROR_CODE.INVALID_ADMIN_SECRET, 'INVALID_ADMIN_SECRET');\n return;\n }\n next();\n };\n router.use('/onesub/purchase/admin', adminAuth);\n router.use('/onesub/admin', adminAuth);\n\n // DELETE /onesub/purchase/admin/:userId/:productId\n // Express 5 types route params as `string | string[]` — narrow via zod.\n const resetParamsSchema = z.object({\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256),\n });\n router.delete('/onesub/purchase/admin/:userId/:productId', async (req: Request, res: Response) => {\n let params;\n try {\n params = resetParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId and productId required');\n return;\n }\n const deleted = await purchaseStore.deletePurchases(params.userId, params.productId);\n res.json({ ok: true, deleted });\n });\n\n // POST /onesub/purchase/admin/transfer — reassign transactionId to a new userId\n // (legitimate device/account migration)\n const transferSchema = z.object({\n transactionId: z.string().min(1).max(256),\n newUserId: z.string().min(1).max(256),\n });\n router.post('/onesub/purchase/admin/transfer', async (req: Request, res: Response) => {\n let body;\n try {\n body = transferSchema.parse(req.body);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err);\n return;\n }\n throw err;\n }\n const existing = await purchaseStore.getPurchaseByTransactionId(body.transactionId);\n if (!existing) {\n sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'TRANSACTION_NOT_FOUND');\n return;\n }\n // Move only this transaction. deletePurchases(userId, productId) would\n // also destroy sibling consumable rows for the same product.\n const moved = await purchaseStore.reassignPurchase(body.transactionId, body.newUserId);\n if (!moved) {\n // Row vanished between the lookup above and the reassign (e.g. a\n // concurrent refund webhook deleted it) — don't report a false success.\n sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'TRANSACTION_NOT_FOUND');\n return;\n }\n const migrated: PurchaseInfo = { ...existing, userId: body.newUserId };\n res.json({ ok: true, purchase: migrated });\n });\n\n // POST /onesub/purchase/admin/grant\n const grantSchema = z.object({\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256),\n platform: z.enum(['apple', 'google']),\n type: z.enum([PURCHASE_TYPE.CONSUMABLE, PURCHASE_TYPE.NON_CONSUMABLE]).default(PURCHASE_TYPE.NON_CONSUMABLE),\n transactionId: z.string().min(1).max(256).optional(),\n });\n\n router.post('/onesub/purchase/admin/grant', async (req: Request, res: Response) => {\n let body;\n try {\n body = grantSchema.parse(req.body);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err);\n return;\n }\n throw err;\n }\n\n const transactionId = body.transactionId ?? `admin_grant_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;\n const purchase: PurchaseInfo = {\n transactionId,\n userId: body.userId,\n productId: body.productId,\n platform: body.platform,\n type: body.type,\n quantity: 1,\n purchasedAt: new Date().toISOString(),\n };\n await purchaseStore.savePurchase(purchase);\n res.json({ ok: true, purchase });\n });\n\n // GET /onesub/admin/subscriptions?userId=&status=&productId=&platform=&limit=&offset=\n // Filtered + paginated subscription list. Backs the dashboard's\n // subscriptions page and ad-hoc operational scripts.\n const listQuerySchema = z.object({\n userId: z.string().min(1).max(256).optional(),\n status: z.enum([\n SUBSCRIPTION_STATUS.ACTIVE,\n SUBSCRIPTION_STATUS.GRACE_PERIOD,\n SUBSCRIPTION_STATUS.ON_HOLD,\n SUBSCRIPTION_STATUS.PAUSED,\n SUBSCRIPTION_STATUS.EXPIRED,\n SUBSCRIPTION_STATUS.CANCELED,\n SUBSCRIPTION_STATUS.NONE,\n ] as [SubscriptionStatus, ...SubscriptionStatus[]]).optional(),\n productId: z.string().min(1).max(256).optional(),\n platform: z.enum(['apple', 'google'] as [Platform, ...Platform[]]).optional(),\n // Cap at 200 server-side so a runaway dashboard query can't tip the DB.\n limit: z.coerce.number().int().positive().max(200).optional(),\n offset: z.coerce.number().int().nonnegative().optional(),\n });\n\n router.get(ROUTES.ADMIN_SUBSCRIPTIONS, async (req: Request, res: Response) => {\n let query: z.infer<typeof listQuerySchema>;\n try {\n query = listQuerySchema.parse(req.query);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err);\n return;\n }\n throw err;\n }\n\n try {\n const result = await store.listFiltered(query);\n const response: ListSubscriptionsResponse = {\n items: result.items,\n total: result.total,\n limit: result.limit,\n offset: result.offset,\n };\n res.status(200).json(response);\n } catch (err) {\n // Bubble the message up but not the stack — admin clients log status code\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'list error');\n }\n });\n\n // GET /onesub/admin/subscriptions/:transactionId — single record by\n // originalTransactionId. Backs the dashboard's subscription detail page.\n const detailParamsSchema = z.object({\n transactionId: z.string().min(1).max(256),\n });\n router.get('/onesub/admin/subscriptions/:transactionId', async (req: Request, res: Response) => {\n let params;\n try {\n params = detailParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'transactionId required');\n return;\n }\n try {\n const sub = await store.getByTransactionId(params.transactionId);\n if (!sub) {\n sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'TRANSACTION_NOT_FOUND');\n return;\n }\n res.status(200).json(sub);\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'detail error');\n }\n });\n\n // GET /onesub/admin/customers/:userId — full per-user profile. Combines the\n // store calls a CS dashboard makes for \"show me everything about this user\"\n // into a single round-trip.\n //\n // Always returns 200 (even for unknown userIds) — empty arrays + no\n // entitlements means \"we have no record of this user\", which is a valid\n // signal for the dashboard to render rather than an error condition.\n const customerParamsSchema = z.object({\n userId: z.string().min(1).max(256),\n });\n router.get('/onesub/admin/customers/:userId', async (req: Request, res: Response) => {\n let params;\n try {\n params = customerParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId required');\n return;\n }\n try {\n const [subscriptions, purchases] = await Promise.all([\n store.getAllByUserId(params.userId),\n purchaseStore.getPurchasesByUserId(params.userId),\n ]);\n\n // Evaluate entitlements only when configured. Hosts that don't use the\n // entitlement abstraction get the field omitted (dashboard hides panel).\n let entitlements: Record<string, EntitlementStatus> | undefined;\n if (config.entitlements && Object.keys(config.entitlements).length > 0) {\n entitlements = {};\n for (const [id, def] of Object.entries(config.entitlements)) {\n entitlements[id] = await evaluateEntitlement(params.userId, def, store, purchaseStore);\n }\n }\n\n const response: CustomerProfileResponse = {\n userId: params.userId,\n subscriptions,\n purchases,\n ...(entitlements ? { entitlements } : {}),\n };\n res.status(200).json(response);\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'customer error');\n }\n });\n\n // POST /onesub/admin/sync-apple/:originalTransactionId\n // Fetch the current subscription state directly from Apple's Status API and\n // upsert into the local store. Useful for reconciliation / missed-webhook recovery.\n const syncAppleParamsSchema = z.object({\n originalTransactionId: z.string().min(1).max(256),\n });\n router.post(ROUTES.ADMIN_SYNC_APPLE, async (req: Request, res: Response) => {\n let params;\n try {\n params = syncAppleParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'originalTransactionId required');\n return;\n }\n\n if (!config.apple?.issuerId || !config.apple?.keyId || !config.apple?.privateKey) {\n sendError(res, 400, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, 'Apple API credentials not configured');\n return;\n }\n\n try {\n const { originalTransactionId } = params;\n // Allow callers to force sandbox mode; otherwise infer from the stored record.\n const existingForEnv = await store.getByTransactionId(originalTransactionId);\n const isSandbox =\n req.query['sandbox'] === 'true' ||\n (existingForEnv as (typeof existingForEnv & { environment?: string }) | null)?.environment === 'Sandbox';\n const fresh = await fetchAppleSubscriptionStatus(originalTransactionId, config.apple, { sandbox: isSandbox });\n if (!fresh) {\n sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'Apple Status API returned no record');\n return;\n }\n\n const existing = existingForEnv;\n fresh.userId = existing?.userId ?? originalTransactionId;\n await store.save(fresh);\n res.status(200).json({ ok: true, subscription: fresh });\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, (err as Error).message ?? 'sync error');\n }\n });\n\n // GET /onesub/admin/webhook-deadletters\n // POST /onesub/admin/webhook-replay/:id\n // Only mounted when the configured queue supports dead-letter inspection\n // (the default in-process queue does not — Apple/Google retries are the\n // durability layer for that mode).\n if (webhookQueue?.listDeadLetters) {\n router.get('/onesub/admin/webhook-deadletters', async (_req: Request, res: Response) => {\n try {\n const items = await webhookQueue.listDeadLetters!();\n res.status(200).json({ items });\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'list error');\n }\n });\n }\n\n if (webhookQueue?.replayDeadLetter) {\n const replayParamsSchema = z.object({ id: z.string().min(1).max(256) });\n router.post('/onesub/admin/webhook-replay/:id', async (req: Request, res: Response) => {\n let params;\n try {\n params = replayParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'id required');\n return;\n }\n try {\n await webhookQueue.replayDeadLetter!(params.id);\n res.status(200).json({ ok: true });\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'replay error');\n }\n });\n }\n\n return router;\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type {\n MetricsActiveResponse,\n MetricsBucket,\n MetricsCountResponse,\n OneSubServerConfig,\n PurchaseInfo,\n SubscriptionInfo,\n} from '@onesub/shared';\nimport {\n ROUTES,\n SUBSCRIPTION_STATUS,\n PURCHASE_TYPE,\n ONESUB_ERROR_CODE,\n} from '@onesub/shared';\nimport type { PurchaseStore, SubscriptionStore } from '../store.js';\nimport { log } from '../logger.js';\nimport { sendError } from '../errors.js';\nimport { secretsEqual } from './secret-compare.js';\n\nconst ADMIN_SECRET_HEADER = 'x-admin-secret';\n\n/**\n * Read-only aggregate metrics endpoints — gated behind `config.adminSecret`.\n * Returns count-based metrics only (active count, started count, expired count).\n *\n * Revenue metrics (MRR, ARR, LTV) require per-product price configuration the\n * server doesn't currently track; deferred to a follow-up release that adds\n * `config.products: { 'pro_monthly': { price: 9.99, currency: 'USD' } }`.\n *\n * Aggregation strategy: pulls every record via `store.listAll()` and reduces\n * in memory. Fine up to ~100k records; large deployments should optimise the\n * Postgres path with SQL aggregates (separate PR).\n */\nexport function createMetricsRouter(\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Router | null {\n if (!config.adminSecret) return null;\n\n const router = Router();\n const adminSecret = config.adminSecret;\n\n // Auth middleware — only protects /onesub/metrics/* (siblings unaffected\n // even when this router is mounted on the parent root).\n router.use('/onesub/metrics', (req, res, next) => {\n const provided = req.headers[ADMIN_SECRET_HEADER];\n if (typeof provided !== 'string' || !secretsEqual(provided, adminSecret)) {\n sendError(res, 401, ONESUB_ERROR_CODE.INVALID_ADMIN_SECRET, 'INVALID_ADMIN_SECRET');\n return;\n }\n next();\n });\n\n // ── helpers ──────────────────────────────────────────────────────────────\n\n const isActiveSub = (sub: SubscriptionInfo, now: number): boolean => {\n const statusAllows =\n sub.status === SUBSCRIPTION_STATUS.ACTIVE ||\n sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD;\n return statusAllows && new Date(sub.expiresAt).getTime() > now;\n };\n\n const bump = (map: Record<string, number>, key: string) => {\n map[key] = (map[key] ?? 0) + 1;\n };\n\n // ── GET /onesub/metrics/active ───────────────────────────────────────────\n\n router.get(ROUTES.METRICS_ACTIVE, async (_req: Request, res: Response) => {\n try {\n const [subs, purchases] = await Promise.all([\n store.listAll(),\n purchaseStore.listAll(),\n ]);\n const now = Date.now();\n\n const byProduct: Record<string, number> = {};\n const byProductPurchases: Record<string, number> = {};\n const byPlatform: Record<string, number> = {};\n let activeSubscriptions = 0;\n let gracePeriodSubscriptions = 0;\n let nonConsumablePurchases = 0;\n\n for (const sub of subs) {\n if (!isActiveSub(sub, now)) continue;\n activeSubscriptions++;\n if (sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD) {\n gracePeriodSubscriptions++;\n }\n bump(byProduct, sub.productId);\n bump(byPlatform, sub.platform);\n }\n\n for (const p of purchases) {\n if (p.type !== PURCHASE_TYPE.NON_CONSUMABLE) continue;\n nonConsumablePurchases++;\n // byProductPurchases is purchase-only; byProduct (subs-only) stays clean\n // so dashboards can render distinct \"subscription mix\" vs \"lifetime mix\"\n // panels without untangling them client-side.\n bump(byProductPurchases, p.productId);\n bump(byPlatform, p.platform);\n }\n\n const response: MetricsActiveResponse = {\n total: activeSubscriptions + nonConsumablePurchases,\n activeSubscriptions,\n gracePeriodSubscriptions,\n nonConsumablePurchases,\n byProduct,\n byProductPurchases,\n byPlatform,\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/metrics/active] error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n // ── GET /onesub/metrics/started?from=&to=&groupBy= ───────────────────────\n\n const rangeSchema = z.object({\n from: z.string().min(1),\n to: z.string().min(1),\n groupBy: z.enum(['none', 'day']).optional(),\n });\n\n type Range = { fromMs: number; toMs: number; groupBy: 'none' | 'day' };\n\n // groupBy=day zero-fills one bucket object per calendar day, so an\n // unbounded range (e.g. from=0001-01-01) would allocate millions of\n // buckets from a single request. Cap the span at a year of daily buckets;\n // ranges without groupBy stay uncapped (they only produce totals).\n const MAX_DAY_BUCKET_SPAN_DAYS = 366;\n const MAX_DAY_BUCKET_SPAN_MS = MAX_DAY_BUCKET_SPAN_DAYS * 86_400_000;\n\n function parseRange(req: Request): Range | { error: string } {\n const parsed = rangeSchema.safeParse(req.query);\n if (!parsed.success) return { error: 'from and to are required (ISO 8601)' };\n const fromMs = new Date(parsed.data.from).getTime();\n const toMs = new Date(parsed.data.to).getTime();\n if (Number.isNaN(fromMs) || Number.isNaN(toMs)) {\n return { error: 'from / to must be ISO 8601 timestamps' };\n }\n if (fromMs > toMs) return { error: 'from must be ≤ to' };\n const groupBy = parsed.data.groupBy ?? 'none';\n if (groupBy === 'day' && toMs - fromMs > MAX_DAY_BUCKET_SPAN_MS) {\n return {\n error: `groupBy=day supports a range of at most ${MAX_DAY_BUCKET_SPAN_DAYS} days — narrow the from/to window or omit groupBy`,\n };\n }\n return { fromMs, toMs, groupBy };\n }\n\n // UTC `YYYY-MM-DD` for a given epoch-ms. Used to assign each record to a\n // calendar-day bucket; UTC keeps the result deterministic regardless of\n // server timezone (Postgres `updated_at` is UTC; SubscriptionInfo dates are\n // ISO with explicit zone offsets).\n function utcDateKey(ms: number): string {\n const d = new Date(ms);\n const yyyy = d.getUTCFullYear();\n const mm = String(d.getUTCMonth() + 1).padStart(2, '0');\n const dd = String(d.getUTCDate()).padStart(2, '0');\n return `${yyyy}-${mm}-${dd}`;\n }\n\n // Zero-fill a daily series across [fromMs, toMs] (inclusive of both\n // boundary days). The route handler then increments the counts.\n function emptyDailyBuckets(fromMs: number, toMs: number): MetricsBucket[] {\n const out: MetricsBucket[] = [];\n // Snap `from` to UTC midnight so iteration steps land on calendar boundaries.\n const start = new Date(fromMs);\n start.setUTCHours(0, 0, 0, 0);\n let cur = start.getTime();\n while (cur <= toMs) {\n out.push({ date: utcDateKey(cur), count: 0 });\n cur += 86_400_000;\n }\n return out;\n }\n\n router.get(ROUTES.METRICS_STARTED, async (req: Request, res: Response) => {\n const range = parseRange(req);\n if ('error' in range) {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, range.error);\n return;\n }\n try {\n const subs = await store.listAll();\n const byProduct: Record<string, number> = {};\n const byPlatform: Record<string, number> = {};\n let total = 0;\n\n // Build a date→index map only when bucketing is requested — keeps the\n // non-bucketed path identical to the previous implementation.\n const buckets =\n range.groupBy === 'day' ? emptyDailyBuckets(range.fromMs, range.toMs) : null;\n const bucketIndex =\n buckets ? new Map(buckets.map((b, i) => [b.date, i])) : null;\n\n for (const sub of subs) {\n const purchasedMs = new Date(sub.purchasedAt).getTime();\n if (purchasedMs < range.fromMs || purchasedMs > range.toMs) continue;\n total++;\n bump(byProduct, sub.productId);\n bump(byPlatform, sub.platform);\n if (buckets && bucketIndex) {\n const idx = bucketIndex.get(utcDateKey(purchasedMs));\n if (idx !== undefined) buckets[idx]!.count++;\n }\n }\n\n const response: MetricsCountResponse = {\n from: req.query['from'] as string,\n to: req.query['to'] as string,\n total,\n byProduct,\n byPlatform,\n ...(buckets ? { buckets } : {}),\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/metrics/started] error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n // ── GET /onesub/metrics/expired?from=&to= ────────────────────────────────\n\n router.get(ROUTES.METRICS_EXPIRED, async (req: Request, res: Response) => {\n const range = parseRange(req);\n if ('error' in range) {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, range.error);\n return;\n }\n try {\n const subs = await store.listAll();\n const byProduct: Record<string, number> = {};\n const byPlatform: Record<string, number> = {};\n let total = 0;\n\n const buckets =\n range.groupBy === 'day' ? emptyDailyBuckets(range.fromMs, range.toMs) : null;\n const bucketIndex =\n buckets ? new Map(buckets.map((b, i) => [b.date, i])) : null;\n\n for (const sub of subs) {\n // Counted only if currently expired or canceled — a record that's\n // still active doesn't qualify even if its expiresAt happened to fall\n // inside the window (e.g. mid-period billing renewal).\n if (\n sub.status !== SUBSCRIPTION_STATUS.EXPIRED &&\n sub.status !== SUBSCRIPTION_STATUS.CANCELED\n ) continue;\n const expiredMs = new Date(sub.expiresAt).getTime();\n if (expiredMs < range.fromMs || expiredMs > range.toMs) continue;\n total++;\n bump(byProduct, sub.productId);\n bump(byPlatform, sub.platform);\n if (buckets && bucketIndex) {\n const idx = bucketIndex.get(utcDateKey(expiredMs));\n if (idx !== undefined) buckets[idx]!.count++;\n }\n }\n\n const response: MetricsCountResponse = {\n from: req.query['from'] as string,\n to: req.query['to'] as string,\n total,\n byProduct,\n byPlatform,\n ...(buckets ? { buckets } : {}),\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/metrics/expired] error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n // ── GET /onesub/metrics/purchases/started?from=&to=&groupBy= ─────────────\n // Counts non-consumable purchases (lifetime products) by purchasedAt within\n // the window. Backs the dashboard's Purchases timeseries chart so hosts that\n // sell only lifetime products (e.g. coffee's Welcome Pass) get a meaningful\n // growth signal even when they have no subscription data.\n //\n // Consumables are excluded — they grant a one-time resource (coins, lives),\n // not an ongoing entitlement, and would dominate the count noisily.\n router.get(ROUTES.METRICS_PURCHASES_STARTED, async (req: Request, res: Response) => {\n const range = parseRange(req);\n if ('error' in range) {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, range.error);\n return;\n }\n try {\n const purchases = await purchaseStore.listAll();\n const byProduct: Record<string, number> = {};\n const byPlatform: Record<string, number> = {};\n let total = 0;\n\n const buckets =\n range.groupBy === 'day' ? emptyDailyBuckets(range.fromMs, range.toMs) : null;\n const bucketIndex =\n buckets ? new Map(buckets.map((b, i) => [b.date, i])) : null;\n\n for (const p of purchases) {\n if (p.type !== PURCHASE_TYPE.NON_CONSUMABLE) continue;\n const purchasedMs = new Date(p.purchasedAt).getTime();\n if (purchasedMs < range.fromMs || purchasedMs > range.toMs) continue;\n total++;\n bump(byProduct, p.productId);\n bump(byPlatform, p.platform);\n if (buckets && bucketIndex) {\n const idx = bucketIndex.get(utcDateKey(purchasedMs));\n if (idx !== undefined) buckets[idx]!.count++;\n }\n }\n\n const response: MetricsCountResponse = {\n from: req.query['from'] as string,\n to: req.query['to'] as string,\n total,\n byProduct,\n byPlatform,\n ...(buckets ? { buckets } : {}),\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/metrics/purchases/started] error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n return router;\n}\n\nexport type { PurchaseInfo, SubscriptionInfo };\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type { OneSubServerConfig } from '@onesub/shared';\nimport { ONESUB_ERROR_CODE } from '@onesub/shared';\nimport { signApplePromotionalOffer } from '../providers/apple.js';\nimport { sendError, sendZodError } from '../errors.js';\nimport { secretsEqual } from './secret-compare.js';\n\nconst OFFER_SECRET_HEADER = 'x-onesub-offer-secret';\n\nconst offerBodySchema = z.object({\n productId: z.string().min(1).max(256),\n offerId: z.string().min(1).max(256),\n applicationUsername: z.string().min(1).max(256),\n});\n\n/**\n * POST /onesub/apple/offer-signature\n *\n * Sign an Apple Promotional Offer payload server-side.\n * Requires config.apple.offerKeyId and config.apple.offerPrivateKey.\n *\n * Authentication: if config.adminSecret is set, the request must carry\n * `X-Onesub-Offer-Secret: <adminSecret>` (same value). Hosts that mount\n * onesub without adminSecret are responsible for securing this endpoint\n * themselves (e.g. behind their own auth middleware).\n *\n * Body: { productId, offerId, applicationUsername }\n * Response: { keyId, nonce, timestamp, signature }\n *\n * The client passes these four values to StoreKit's\n * Product.SubscriptionOffer.Signature to redeem the offer.\n */\nexport function createAppleOfferRouter(config: OneSubServerConfig): Router | null {\n const apple = config.apple as (OneSubServerConfig['apple'] & { offerKeyId?: string; offerPrivateKey?: string }) | undefined;\n if (!apple?.offerKeyId || !apple?.offerPrivateKey) return null;\n\n const router = Router();\n\n router.post('/onesub/apple/offer-signature', async (req: Request, res: Response) => {\n if (config.adminSecret) {\n const provided = req.headers[OFFER_SECRET_HEADER];\n if (typeof provided !== 'string' || !secretsEqual(provided, config.adminSecret)) {\n sendError(res, 401, ONESUB_ERROR_CODE.UNAUTHORIZED, 'Unauthorized');\n return;\n }\n }\n let body: z.infer<typeof offerBodySchema>;\n try {\n body = offerBodySchema.parse(req.body);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err);\n return;\n }\n throw err;\n }\n\n if (!apple.bundleId) {\n sendError(res, 400, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, 'config.apple.bundleId is required for offer signing');\n return;\n }\n\n try {\n const result = await signApplePromotionalOffer(\n {\n bundleId: apple.bundleId,\n productId: body.productId,\n offerId: body.offerId,\n applicationUsername: body.applicationUsername,\n },\n apple,\n );\n res.status(200).json(result);\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, (err as Error).message ?? 'Offer signing failed');\n }\n });\n\n return router;\n}\n","/**\n * Embedded Postgres DDL mirrored from `packages/server/sql/schema.sql`.\n *\n * The `.sql` file is the single human-facing source of truth (mounted into\n * docker-entrypoint-initdb.d, runnable via `psql -f`, readable by DBAs).\n * These constants exist so that `initSchema()` can run without a filesystem\n * lookup after the package is bundled/installed.\n *\n * A test in `__tests__/schema.test.ts` asserts that the two stay in sync —\n * if you edit one, update the other (or the test will fail).\n */\n\nexport const SUBSCRIPTIONS_SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS onesub_subscriptions (\n original_transaction_id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL,\n product_id TEXT NOT NULL,\n platform TEXT NOT NULL,\n status TEXT NOT NULL,\n expires_at TIMESTAMPTZ NOT NULL,\n purchased_at TIMESTAMPTZ NOT NULL,\n will_renew BOOLEAN NOT NULL,\n linked_purchase_token TEXT,\n auto_resume_time TIMESTAMPTZ,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_user_id\n ON onesub_subscriptions (user_id, updated_at DESC);\n\n-- Filter-helper indexes for /onesub/admin/subscriptions. Each filter column\n-- is paired with updated_at DESC so the planner can serve \"latest matching\"\n-- without sorting the full table once row count grows.\nCREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_status_updated\n ON onesub_subscriptions (status, updated_at DESC);\nCREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_platform_updated\n ON onesub_subscriptions (platform, updated_at DESC);\nCREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_product\n ON onesub_subscriptions (product_id, updated_at DESC);\n\n-- Backfill columns for installs that already created the table from an older\n-- schema. Safe to re-run.\nALTER TABLE onesub_subscriptions ADD COLUMN IF NOT EXISTS linked_purchase_token TEXT;\nALTER TABLE onesub_subscriptions ADD COLUMN IF NOT EXISTS auto_resume_time TIMESTAMPTZ;\n`.trim();\n\nexport const PURCHASES_SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS onesub_purchases (\n transaction_id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL,\n product_id TEXT NOT NULL,\n platform TEXT NOT NULL,\n type TEXT NOT NULL,\n quantity INTEGER NOT NULL DEFAULT 1,\n purchased_at TIMESTAMPTZ NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE INDEX IF NOT EXISTS idx_onesub_purchases_user_id\n ON onesub_purchases (user_id, purchased_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_onesub_purchases_user_product\n ON onesub_purchases (user_id, product_id);\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_onesub_purchases_non_consumable\n ON onesub_purchases (user_id, product_id)\n WHERE type = 'non_consumable';\n`.trim();\n","import type { SubscriptionInfo, PurchaseInfo } from '@onesub/shared';\nimport type {\n SubscriptionStore,\n PurchaseStore,\n ListFilteredOptions,\n ListFilteredResult,\n} from '../store.js';\nimport { SUBSCRIPTIONS_SCHEMA_SQL, PURCHASES_SCHEMA_SQL } from './schema.js';\nimport { log } from '../logger.js';\n\n/**\n * Shared pool construction for both Postgres stores.\n *\n * `label` names the owning store in the missing-dependency error and in the\n * pool 'error' log so operators can tell which store hit the problem.\n */\nasync function createPgPool(connectionString: string, label: string): Promise<import('pg').Pool> {\n // Dynamic import so that `pg` is an optional peer dependency —\n // consumers who only use InMemorySubscriptionStore pay no cost.\n const pg = await import('pg').catch(() => {\n throw new Error(`[onesub] ${label} requires the \\`pg\\` package. Run: npm install pg`);\n });\n const Pool = pg.default?.Pool ?? (pg as unknown as { Pool: typeof import('pg').Pool }).Pool;\n const pool = new Pool({ connectionString, max: 10 });\n // node-postgres emits 'error' on the Pool when an idle client's backend\n // connection dies (e.g. DB failover / restart). Without a listener that's\n // an unhandled EventEmitter 'error' — it crashes the process. Log it and\n // let the pool replace the client.\n pool.on('error', (err) => {\n log.error(`[onesub] ${label} pool error (idle client):`, err);\n });\n return pool;\n}\n\n/**\n * PostgreSQL-backed subscription store.\n *\n * Uses raw `pg` (node-postgres) to avoid Prisma / ORM dependencies.\n * The `pg` package must be installed separately:\n *\n * npm install pg\n * npm install -D @types/pg\n *\n * Usage:\n * const store = new PostgresSubscriptionStore(process.env.DATABASE_URL!);\n * await store.initSchema();\n * app.use(createOneSubMiddleware({ ...config, store }));\n */\nexport class PostgresSubscriptionStore implements SubscriptionStore {\n // Lazy-loaded to avoid requiring `pg` unless this class is actually instantiated.\n private poolPromise: Promise<import('pg').Pool> | null = null;\n\n constructor(private readonly connectionString: string) {}\n\n private getPool(): Promise<import('pg').Pool> {\n if (!this.poolPromise) {\n this.poolPromise = createPgPool(this.connectionString, 'PostgresSubscriptionStore');\n }\n return this.poolPromise;\n }\n\n /**\n * Creates the `onesub_subscriptions` table and indexes if they do not\n * already exist. Call this once during application startup.\n */\n async initSchema(): Promise<void> {\n const pool = await this.getPool();\n // DDL mirrored in packages/server/sql/schema.sql — kept in sync by\n // schema parity test. Edit both (or only the .sql file and regenerate).\n await pool.query(SUBSCRIPTIONS_SCHEMA_SQL);\n }\n\n /**\n * Upserts the given subscription.\n * If a row with the same `original_transaction_id` already exists it is\n * updated in place; otherwise a new row is inserted.\n */\n async save(sub: SubscriptionInfo): Promise<void> {\n const pool = await this.getPool();\n await pool.query(\n `INSERT INTO onesub_subscriptions\n (original_transaction_id, user_id, product_id, platform, status,\n expires_at, purchased_at, will_renew, linked_purchase_token,\n auto_resume_time, updated_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW())\n ON CONFLICT (original_transaction_id) DO UPDATE SET\n user_id = EXCLUDED.user_id,\n product_id = EXCLUDED.product_id,\n platform = EXCLUDED.platform,\n status = EXCLUDED.status,\n expires_at = EXCLUDED.expires_at,\n purchased_at = EXCLUDED.purchased_at,\n will_renew = EXCLUDED.will_renew,\n linked_purchase_token = EXCLUDED.linked_purchase_token,\n auto_resume_time = EXCLUDED.auto_resume_time,\n updated_at = NOW()`,\n [\n sub.originalTransactionId,\n sub.userId,\n sub.productId,\n sub.platform,\n sub.status,\n sub.expiresAt,\n sub.purchasedAt,\n sub.willRenew,\n sub.linkedPurchaseToken ?? null,\n sub.autoResumeTime ?? null,\n ]\n );\n }\n\n /**\n * Returns the most recently updated subscription for the given user, or\n * `null` if no record exists.\n *\n * \"Most recent\" is determined by `updated_at DESC` so that the latest\n * status change is always returned when a user has multiple subscriptions.\n */\n async getByUserId(userId: string): Promise<SubscriptionInfo | null> {\n const pool = await this.getPool();\n const result = await pool.query<DbRow>(\n `SELECT *\n FROM onesub_subscriptions\n WHERE user_id = $1\n ORDER BY updated_at DESC\n LIMIT 1`,\n [userId]\n );\n return result.rows.length > 0 ? rowToSubscriptionInfo(result.rows[0]) : null;\n }\n\n /**\n * Returns every subscription record for the user (across all productIds),\n * ordered most-recent-first. Used by entitlement evaluation, which needs to\n * see all of a user's active subscriptions to decide whether any of them\n * grants the requested entitlement.\n */\n async getAllByUserId(userId: string): Promise<SubscriptionInfo[]> {\n const pool = await this.getPool();\n const result = await pool.query<DbRow>(\n `SELECT *\n FROM onesub_subscriptions\n WHERE user_id = $1\n ORDER BY updated_at DESC`,\n [userId]\n );\n return result.rows.map(rowToSubscriptionInfo);\n }\n\n /**\n * Returns the subscription identified by `originalTransactionId` (the\n * table primary key), or `null` if it does not exist.\n */\n async getByTransactionId(txId: string): Promise<SubscriptionInfo | null> {\n const pool = await this.getPool();\n const result = await pool.query<DbRow>(\n `SELECT *\n FROM onesub_subscriptions\n WHERE original_transaction_id = $1`,\n [txId]\n );\n return result.rows.length > 0 ? rowToSubscriptionInfo(result.rows[0]) : null;\n }\n\n async listAll(): Promise<SubscriptionInfo[]> {\n const pool = await this.getPool();\n const result = await pool.query<DbRow>(`SELECT * FROM onesub_subscriptions`);\n return result.rows.map(rowToSubscriptionInfo);\n }\n\n async listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult> {\n const pool = await this.getPool();\n const limit = opts.limit ?? 50;\n const offset = opts.offset ?? 0;\n\n // Build the WHERE clause incrementally. Each non-undefined filter adds an\n // AND condition with a parameterised value (no SQL injection surface).\n const conditions: string[] = [];\n const params: unknown[] = [];\n if (opts.userId) {\n params.push(opts.userId);\n conditions.push(`user_id = $${params.length}`);\n }\n if (opts.status) {\n params.push(opts.status);\n conditions.push(`status = $${params.length}`);\n }\n if (opts.productId) {\n params.push(opts.productId);\n conditions.push(`product_id = $${params.length}`);\n }\n if (opts.platform) {\n params.push(opts.platform);\n conditions.push(`platform = $${params.length}`);\n }\n const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';\n\n // Run the count + page in parallel — both queries hit the same WHERE clause\n // so they share the index plan, and we save a round-trip.\n const [countResult, pageResult] = await Promise.all([\n pool.query<{ total: string }>(\n `SELECT COUNT(*)::text AS total FROM onesub_subscriptions ${where}`,\n params,\n ),\n pool.query<DbRow>(\n `SELECT * FROM onesub_subscriptions ${where}\n ORDER BY updated_at DESC\n LIMIT $${params.length + 1} OFFSET $${params.length + 2}`,\n [...params, limit, offset],\n ),\n ]);\n\n return {\n items: pageResult.rows.map(rowToSubscriptionInfo),\n total: parseInt(countResult.rows[0]?.total ?? '0', 10),\n limit,\n offset,\n };\n }\n\n /** Gracefully close the underlying connection pool. */\n async close(): Promise<void> {\n if (this.poolPromise) {\n const pool = await this.poolPromise;\n await pool.end();\n this.poolPromise = null;\n }\n }\n}\n\n/**\n * PostgreSQL-backed purchase store for consumables and non-consumables.\n *\n * Uses raw `pg` (node-postgres). The `pg` package must be installed separately:\n *\n * npm install pg\n * npm install -D @types/pg\n *\n * Usage:\n * const store = new PostgresPurchaseStore(process.env.DATABASE_URL!);\n * await store.initSchema();\n * app.use(createOneSubMiddleware({ ...config, purchaseStore: store }));\n */\nexport class PostgresPurchaseStore implements PurchaseStore {\n private poolPromise: Promise<import('pg').Pool> | null = null;\n\n constructor(private readonly connectionString: string) {}\n\n private getPool(): Promise<import('pg').Pool> {\n if (!this.poolPromise) {\n this.poolPromise = createPgPool(this.connectionString, 'PostgresPurchaseStore');\n }\n return this.poolPromise;\n }\n\n /**\n * Creates the `onesub_purchases` table and indexes if they do not already exist.\n * Call this once during application startup.\n */\n async initSchema(): Promise<void> {\n const pool = await this.getPool();\n // DDL mirrored in packages/server/sql/schema.sql — kept in sync by\n // schema parity test.\n await pool.query(PURCHASES_SCHEMA_SQL);\n }\n\n async savePurchase(purchase: PurchaseInfo): Promise<void> {\n const pool = await this.getPool();\n // transactionId is PRIMARY KEY. If the same transactionId already belongs\n // to a different userId we refuse the save — the second call is either a\n // fraudulent receipt-reuse attempt or a legitimate device migration that\n // should go through an explicit admin transfer instead. Returning silently\n // for a different owner (a blanket ON CONFLICT DO NOTHING) would leave the\n // server reporting valid:true while the DB has no row for the current\n // user, which lets the caller keep re-\"buying\" forever.\n //\n // INSERT ... ON CONFLICT (transaction_id) DO NOTHING claims the row\n // atomically: under two concurrent saves of the same transactionId only\n // one INSERT wins, and the loser gets zero rows back instead of a raw\n // PK-violation error (which used to surface as a 500).\n const inserted = await pool.query(\n `INSERT INTO onesub_purchases\n (transaction_id, user_id, product_id, platform, type, quantity, purchased_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (transaction_id) DO NOTHING\n RETURNING transaction_id`,\n [\n purchase.transactionId,\n purchase.userId,\n purchase.productId,\n purchase.platform,\n purchase.type,\n purchase.quantity,\n purchase.purchasedAt,\n ],\n );\n if (inserted.rows.length > 0) return; // fresh row inserted — we own it\n\n // Row already existed — enforce ownership.\n const existing = await pool.query<{ user_id: string }>(\n `SELECT user_id FROM onesub_purchases WHERE transaction_id = $1`,\n [purchase.transactionId],\n );\n const owner = existing.rows[0]?.user_id;\n if (owner !== undefined && owner !== purchase.userId) {\n const err = new Error('TRANSACTION_BELONGS_TO_OTHER_USER') as Error & { code?: string };\n err.code = 'TRANSACTION_BELONGS_TO_OTHER_USER';\n throw err;\n }\n // same user — idempotent no-op (owner === undefined means the row was\n // deleted between the INSERT and the SELECT; treat as no-op as well)\n }\n\n async getPurchasesByUserId(userId: string): Promise<PurchaseInfo[]> {\n const pool = await this.getPool();\n const result = await pool.query<PurchaseDbRow>(\n `SELECT *\n FROM onesub_purchases\n WHERE user_id = $1\n ORDER BY purchased_at DESC`,\n [userId]\n );\n return result.rows.map(rowToPurchaseInfo);\n }\n\n async getPurchaseByTransactionId(txId: string): Promise<PurchaseInfo | null> {\n const pool = await this.getPool();\n const result = await pool.query<PurchaseDbRow>(\n `SELECT *\n FROM onesub_purchases\n WHERE transaction_id = $1`,\n [txId]\n );\n return result.rows.length > 0 ? rowToPurchaseInfo(result.rows[0]) : null;\n }\n\n async hasPurchased(userId: string, productId: string): Promise<boolean> {\n const pool = await this.getPool();\n const result = await pool.query<{ exists: boolean }>(\n `SELECT EXISTS(\n SELECT 1 FROM onesub_purchases\n WHERE user_id = $1 AND product_id = $2\n ) AS exists`,\n [userId, productId]\n );\n return result.rows[0]?.exists === true;\n }\n\n async reassignPurchase(transactionId: string, newUserId: string): Promise<boolean> {\n const pool = await this.getPool();\n const result = await pool.query(\n `UPDATE onesub_purchases SET user_id = $2 WHERE transaction_id = $1`,\n [transactionId, newUserId],\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n async deletePurchases(userId: string, productId: string): Promise<number> {\n const pool = await this.getPool();\n const result = await pool.query(\n `DELETE FROM onesub_purchases WHERE user_id = $1 AND product_id = $2`,\n [userId, productId]\n );\n return result.rowCount ?? 0;\n }\n\n async deletePurchaseByTransactionId(transactionId: string): Promise<boolean> {\n const pool = await this.getPool();\n const result = await pool.query(\n `DELETE FROM onesub_purchases WHERE transaction_id = $1`,\n [transactionId]\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n async listAll(): Promise<PurchaseInfo[]> {\n const pool = await this.getPool();\n const result = await pool.query<PurchaseDbRow>(`SELECT * FROM onesub_purchases`);\n return result.rows.map(rowToPurchaseInfo);\n }\n\n /** Gracefully close the underlying connection pool. */\n async close(): Promise<void> {\n if (this.poolPromise) {\n const pool = await this.poolPromise;\n await pool.end();\n this.poolPromise = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface DbRow {\n original_transaction_id: string;\n user_id: string;\n product_id: string;\n platform: string;\n status: string;\n expires_at: Date;\n purchased_at: Date;\n will_renew: boolean;\n linked_purchase_token: string | null;\n auto_resume_time: Date | null;\n}\n\nfunction rowToSubscriptionInfo(row: DbRow): SubscriptionInfo {\n return {\n originalTransactionId: row.original_transaction_id,\n userId: row.user_id,\n productId: row.product_id,\n platform: row.platform as SubscriptionInfo['platform'],\n status: row.status as SubscriptionInfo['status'],\n expiresAt: row.expires_at.toISOString(),\n purchasedAt: row.purchased_at.toISOString(),\n willRenew: row.will_renew,\n linkedPurchaseToken: row.linked_purchase_token ?? undefined,\n autoResumeTime: row.auto_resume_time?.toISOString(),\n };\n}\n\ninterface PurchaseDbRow {\n transaction_id: string;\n user_id: string;\n product_id: string;\n platform: string;\n type: string;\n quantity: number;\n purchased_at: Date;\n}\n\nfunction rowToPurchaseInfo(row: PurchaseDbRow): PurchaseInfo {\n return {\n transactionId: row.transaction_id,\n userId: row.user_id,\n productId: row.product_id,\n platform: row.platform as PurchaseInfo['platform'],\n type: row.type as PurchaseInfo['type'],\n quantity: row.quantity,\n purchasedAt: row.purchased_at.toISOString(),\n };\n}\n","import type { SubscriptionInfo, PurchaseInfo } from '@onesub/shared';\nimport type {\n SubscriptionStore,\n PurchaseStore,\n ListFilteredOptions,\n ListFilteredResult,\n} from '../store.js';\nimport type { CacheAdapter } from '../cache.js';\nimport type { WebhookEventStore } from '../webhook-events.js';\n\ntype IORedis = import('ioredis').Redis;\n\n/**\n * Redis-backed subscription / purchase / cache stores.\n *\n * Uses the `ioredis` package — kept as an optional peer dependency so callers\n * who only need InMemory or Postgres pay no install cost.\n *\n * npm install ioredis\n *\n * Usage:\n * import Redis from 'ioredis';\n * import {\n * RedisSubscriptionStore,\n * RedisPurchaseStore,\n * RedisCacheAdapter,\n * } from '@onesub/server';\n *\n * const redis = new Redis(process.env.REDIS_URL!);\n * const store = new RedisSubscriptionStore(redis);\n * const purchaseStore = new RedisPurchaseStore(redis);\n * const cache = new RedisCacheAdapter(redis);\n *\n * app.use(createOneSubMiddleware({ ...config, store, purchaseStore, cache }));\n *\n * Key layout:\n * onesub:sub:tx:<originalTransactionId> → JSON SubscriptionInfo\n * onesub:sub:owner:<originalTransactionId> → userId (cheap prev-owner lookup in save(); written in the same MULTI as the record)\n * onesub:sub:user:<userId> → SortedSet of originalTransactionIds, scored by updatedAt (ms)\n * onesub:sub:all:sorted → SortedSet of originalTransactionIds, scored by save time (for listAll/listFiltered)\n * onesub:purchase:tx:<transactionId> → JSON PurchaseInfo\n * onesub:purchase:user:<userId> → SortedSet of transactionIds, scored by purchasedAt (ms)\n * onesub:purchase:user_product:<u>:<p> → Set of transactionIds (for non-consumable hasPurchased)\n * onesub:purchase:all → Set of transactionIds\n * onesub:cache:<key> → string with TTL (RedisCacheAdapter)\n * onesub:webhook:event:<provider>:<id> → \"1\" with TTL (RedisWebhookEventStore)\n */\n\nconst SUB_TX_PREFIX = 'onesub:sub:tx:';\nconst SUB_OWNER_PREFIX = 'onesub:sub:owner:';\nconst SUB_USER_PREFIX = 'onesub:sub:user:';\n// Global sorted set (score = save timestamp ms) — enables ordered listAll and\n// O(log n + limit) fast-path pagination when no secondary filters are applied.\nconst SUB_ALL_SORTED = 'onesub:sub:all:sorted';\n\nconst PUR_TX_PREFIX = 'onesub:purchase:tx:';\nconst PUR_USER_PREFIX = 'onesub:purchase:user:';\nconst PUR_USER_PRODUCT_PREFIX = 'onesub:purchase:user_product:';\nconst PUR_ALL = 'onesub:purchase:all';\n\nexport class RedisSubscriptionStore implements SubscriptionStore {\n constructor(private readonly redis: IORedis) {}\n\n async save(sub: SubscriptionInfo): Promise<void> {\n const score = Date.now();\n const txKey = SUB_TX_PREFIX + sub.originalTransactionId;\n const ownerKey = SUB_OWNER_PREFIX + sub.originalTransactionId;\n const userKey = SUB_USER_PREFIX + sub.userId;\n\n // If this transaction was previously bound to a different userId (the\n // validate route rebinds ownership on re-validation), drop it from the\n // old user's index — otherwise both userIds would resolve the same live\n // subscription forever. The owner side-key keeps that check to a small\n // string GET on the hot path instead of a GET + JSON.parse of the full\n // record; records written before the side-key existed fall back to the\n // full record once and are backfilled by the MULTI below.\n let prevUserId: string | null = await this.redis.get(ownerKey);\n if (prevUserId === null) {\n const prevRaw = await this.redis.get(txKey);\n prevUserId = prevRaw ? (JSON.parse(prevRaw) as SubscriptionInfo).userId : null;\n }\n\n const pipeline = this.redis.multi();\n pipeline.set(txKey, JSON.stringify(sub));\n pipeline.set(ownerKey, sub.userId);\n if (prevUserId !== null && prevUserId !== sub.userId) {\n pipeline.zrem(SUB_USER_PREFIX + prevUserId, sub.originalTransactionId);\n }\n pipeline.zadd(userKey, score, sub.originalTransactionId);\n pipeline.zadd(SUB_ALL_SORTED, score, sub.originalTransactionId);\n await pipeline.exec();\n }\n\n async getByUserId(userId: string): Promise<SubscriptionInfo | null> {\n const userKey = SUB_USER_PREFIX + userId;\n // ZREVRANGE 0 0 = most-recent (highest score)\n const ids = await this.redis.zrevrange(userKey, 0, 0);\n if (ids.length === 0) return null;\n const raw = await this.redis.get(SUB_TX_PREFIX + ids[0]);\n return raw ? (JSON.parse(raw) as SubscriptionInfo) : null;\n }\n\n async getAllByUserId(userId: string): Promise<SubscriptionInfo[]> {\n const userKey = SUB_USER_PREFIX + userId;\n const ids = await this.redis.zrevrange(userKey, 0, -1);\n if (ids.length === 0) return [];\n const raws = await this.redis.mget(...ids.map((id) => SUB_TX_PREFIX + id));\n return raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as SubscriptionInfo);\n }\n\n async getByTransactionId(txId: string): Promise<SubscriptionInfo | null> {\n const raw = await this.redis.get(SUB_TX_PREFIX + txId);\n return raw ? (JSON.parse(raw) as SubscriptionInfo) : null;\n }\n\n async listAll(): Promise<SubscriptionInfo[]> {\n // ZREVRANGE on the sorted set gives newest-first order (vs random SMEMBERS).\n const ids = await this.redis.zrevrange(SUB_ALL_SORTED, 0, -1);\n if (ids.length === 0) return [];\n const raws = await this.redis.mget(...ids.map((id) => SUB_TX_PREFIX + id));\n return raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as SubscriptionInfo);\n }\n\n async listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult> {\n const limit = opts.limit ?? 50;\n const offset = opts.offset ?? 0;\n const hasSecondaryFilters = !!(opts.status || opts.productId || opts.platform);\n\n if (opts.userId) {\n // Per-user path — already O(log n) via per-user sorted set.\n const candidates = await this.getAllByUserId(opts.userId);\n const filtered = candidates.filter((s) => {\n if (opts.status && s.status !== opts.status) return false;\n if (opts.productId && s.productId !== opts.productId) return false;\n if (opts.platform && s.platform !== opts.platform) return false;\n return true;\n });\n return { items: filtered.slice(offset, offset + limit), total: filtered.length, limit, offset };\n }\n\n if (!hasSecondaryFilters) {\n // Fast path: pure pagination with no filter — O(log n + limit) via sorted set.\n const [ids, total] = await Promise.all([\n this.redis.zrevrange(SUB_ALL_SORTED, offset, offset + limit - 1),\n this.redis.zcard(SUB_ALL_SORTED),\n ]);\n if (ids.length === 0) return { items: [], total, limit, offset };\n const raws = await this.redis.mget(...ids.map((id) => SUB_TX_PREFIX + id));\n const items = raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as SubscriptionInfo);\n return { items, total, limit, offset };\n }\n\n // Slow path: secondary filters require full scan. Results come in\n // newest-first order. For >100k rows pair with Postgres or Redis Stack.\n const allIds = await this.redis.zrevrange(SUB_ALL_SORTED, 0, -1);\n if (allIds.length === 0) return { items: [], total: 0, limit, offset };\n const raws = await this.redis.mget(...allIds.map((id) => SUB_TX_PREFIX + id));\n const all = raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as SubscriptionInfo);\n const filtered = all.filter((s) => {\n if (opts.status && s.status !== opts.status) return false;\n if (opts.productId && s.productId !== opts.productId) return false;\n if (opts.platform && s.platform !== opts.platform) return false;\n return true;\n });\n return { items: filtered.slice(offset, offset + limit), total: filtered.length, limit, offset };\n }\n}\n\nexport class RedisPurchaseStore implements PurchaseStore {\n constructor(private readonly redis: IORedis) {}\n\n async savePurchase(purchase: PurchaseInfo): Promise<void> {\n const txKey = PUR_TX_PREFIX + purchase.transactionId;\n\n // Atomically claim the transaction key with SET NX — only the first\n // writer wins, which closes the GET-then-write race where two concurrent\n // saves with different userIds could both pass a read-side owner check\n // and both index the transaction (account-binding bypass).\n const claimed = await this.redis.set(txKey, JSON.stringify(purchase), 'NX');\n\n if (claimed !== 'OK') {\n // Key already exists — same TRANSACTION_BELONGS_TO_OTHER_USER semantics\n // as Postgres / InMemory implementations. Without this guard a stolen\n // receipt could be re-bound to a different account.\n const existing = await this.redis.get(txKey);\n const owner = existing ? (JSON.parse(existing) as PurchaseInfo).userId : null;\n if (owner !== null && owner !== purchase.userId) {\n const err = new Error('TRANSACTION_BELONGS_TO_OTHER_USER') as Error & { code?: string };\n err.code = 'TRANSACTION_BELONGS_TO_OTHER_USER';\n throw err;\n }\n // Same user: fall through to the index writes below instead of returning.\n // They are idempotent (zadd/sadd), and skipping them would make a crash\n // between the SET NX and the pipeline permanent — the tx key would exist\n // with no indexes and no retry could ever backfill them.\n }\n\n const score = Date.parse(purchase.purchasedAt) || Date.now();\n const pipeline = this.redis.multi();\n pipeline.zadd(PUR_USER_PREFIX + purchase.userId, score, purchase.transactionId);\n pipeline.sadd(PUR_USER_PRODUCT_PREFIX + purchase.userId + ':' + purchase.productId, purchase.transactionId);\n pipeline.sadd(PUR_ALL, purchase.transactionId);\n await pipeline.exec();\n }\n\n async getPurchasesByUserId(userId: string): Promise<PurchaseInfo[]> {\n const ids = await this.redis.zrevrange(PUR_USER_PREFIX + userId, 0, -1);\n if (ids.length === 0) return [];\n const raws = await this.redis.mget(...ids.map((id) => PUR_TX_PREFIX + id));\n return raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as PurchaseInfo);\n }\n\n async getPurchaseByTransactionId(txId: string): Promise<PurchaseInfo | null> {\n const raw = await this.redis.get(PUR_TX_PREFIX + txId);\n return raw ? (JSON.parse(raw) as PurchaseInfo) : null;\n }\n\n async hasPurchased(userId: string, productId: string): Promise<boolean> {\n const count = await this.redis.scard(PUR_USER_PRODUCT_PREFIX + userId + ':' + productId);\n return count > 0;\n }\n\n async reassignPurchase(transactionId: string, newUserId: string): Promise<boolean> {\n const txKey = PUR_TX_PREFIX + transactionId;\n const raw = await this.redis.get(txKey);\n if (!raw) return false;\n const existing = JSON.parse(raw) as PurchaseInfo;\n if (existing.userId === newUserId) return true;\n\n const updated: PurchaseInfo = { ...existing, userId: newUserId };\n const score = Date.parse(updated.purchasedAt) || Date.now();\n const oldUserProductKey = PUR_USER_PRODUCT_PREFIX + existing.userId + ':' + existing.productId;\n const newUserProductKey = PUR_USER_PRODUCT_PREFIX + newUserId + ':' + existing.productId;\n\n const pipeline = this.redis.multi();\n pipeline.set(txKey, JSON.stringify(updated));\n pipeline.zrem(PUR_USER_PREFIX + existing.userId, transactionId);\n pipeline.zadd(PUR_USER_PREFIX + newUserId, score, transactionId);\n pipeline.srem(oldUserProductKey, transactionId);\n pipeline.sadd(newUserProductKey, transactionId);\n await pipeline.exec();\n return true;\n }\n\n async deletePurchases(userId: string, productId: string): Promise<number> {\n const userProductKey = PUR_USER_PRODUCT_PREFIX + userId + ':' + productId;\n const ids = await this.redis.smembers(userProductKey);\n if (ids.length === 0) return 0;\n\n const pipeline = this.redis.multi();\n for (const id of ids) {\n pipeline.del(PUR_TX_PREFIX + id);\n pipeline.zrem(PUR_USER_PREFIX + userId, id);\n pipeline.srem(PUR_ALL, id);\n }\n pipeline.del(userProductKey);\n await pipeline.exec();\n return ids.length;\n }\n\n async deletePurchaseByTransactionId(transactionId: string): Promise<boolean> {\n const txKey = PUR_TX_PREFIX + transactionId;\n const raw = await this.redis.get(txKey);\n if (!raw) return false;\n const existing = JSON.parse(raw) as PurchaseInfo;\n\n const pipeline = this.redis.multi();\n pipeline.del(txKey);\n pipeline.zrem(PUR_USER_PREFIX + existing.userId, transactionId);\n pipeline.srem(PUR_USER_PRODUCT_PREFIX + existing.userId + ':' + existing.productId, transactionId);\n pipeline.srem(PUR_ALL, transactionId);\n await pipeline.exec();\n return true;\n }\n\n async listAll(): Promise<PurchaseInfo[]> {\n const ids = await this.redis.smembers(PUR_ALL);\n if (ids.length === 0) return [];\n const raws = await this.redis.mget(...ids.map((id) => PUR_TX_PREFIX + id));\n return raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as PurchaseInfo);\n }\n}\n\n/**\n * Redis-backed cache adapter — share Apple API JWT / Google OAuth tokens across cluster nodes.\n *\n * Implements `CacheAdapter` so it plugs into the same default-cache slot used\n * by the Apple JWT minter and Google OAuth token minter.\n */\nexport class RedisCacheAdapter implements CacheAdapter {\n constructor(\n private readonly redis: IORedis,\n private readonly prefix = 'onesub:cache:',\n ) {}\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const raw = await this.redis.get(this.prefix + key);\n return raw ? (JSON.parse(raw) as T) : null;\n }\n\n async set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void> {\n const fullKey = this.prefix + key;\n const payload = JSON.stringify(value);\n if (ttlSeconds && ttlSeconds > 0) {\n await this.redis.set(fullKey, payload, 'EX', ttlSeconds);\n } else {\n await this.redis.set(fullKey, payload);\n }\n }\n\n async del(key: string): Promise<void> {\n await this.redis.del(this.prefix + key);\n }\n}\n\nconst WEBHOOK_EVENT_PREFIX = 'onesub:webhook:event:';\nconst DEFAULT_WEBHOOK_TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days — covers Apple's 3-day retry window\n\n/**\n * Redis-backed webhook idempotency store.\n *\n * Uses `SET key '1' EX ttl NX` — a single atomic command that sets the key\n * only if it does not already exist. Returns 'OK' (new event) or null\n * (already seen), with no race between a GET and a subsequent SET.\n *\n * Prefer this over `CacheWebhookEventStore(new RedisCacheAdapter(...))` for\n * production deployments; the cache-based variant is non-atomic under\n * concurrent retries.\n */\nexport class RedisWebhookEventStore implements WebhookEventStore {\n constructor(\n private readonly redis: IORedis,\n private readonly ttlSeconds = DEFAULT_WEBHOOK_TTL_SECONDS,\n ) {}\n\n async markIfNew(provider: 'apple' | 'google', eventId: string): Promise<boolean> {\n const key = WEBHOOK_EVENT_PREFIX + provider + ':' + eventId;\n const result = await this.redis.set(key, '1', 'EX', this.ttlSeconds, 'NX');\n return result === 'OK';\n }\n\n async unmark(provider: 'apple' | 'google', eventId: string): Promise<void> {\n await this.redis.del(WEBHOOK_EVENT_PREFIX + provider + ':' + eventId);\n }\n}\n","import { log } from './logger.js';\n\n/**\n * Webhook processing queue.\n *\n * The default in-process queue runs the handler synchronously inside the HTTP\n * request — the same behavior you got before this interface existed. For\n * production deployments that want decoupled retries, swap in a queue-backed\n * implementation (BullMQ, SQS, etc.) so a slow DB or Apple/Google API call\n * doesn't block the webhook from acknowledging.\n *\n * Failure semantics:\n * - Handler throws → queue records the failure and (for retrying queues)\n * re-runs with backoff. After max attempts the job\n * lands in the dead-letter store.\n * - Handler returns → ack to source.\n *\n * The webhook routes ack (200) as soon as `enqueue()` resolves. For the\n * in-process queue that still means \"after the handler ran\" (enqueue runs it\n * synchronously); for BullMQ it means \"job persisted to Redis\" and processing\n * happens in the worker.\n */\nexport interface WebhookJob<T = unknown> {\n provider: 'apple' | 'google';\n /** Source-supplied event id (notificationUUID / messageId) for tracing. */\n eventId: string;\n payload: T;\n}\n\nexport type WebhookHandler<T = unknown> = (job: WebhookJob<T>) => Promise<void>;\n\nexport interface WebhookQueue {\n /**\n * Enqueue a job. Returns once the job is durably accepted (synchronous for\n * in-process; persisted-to-Redis for BullMQ).\n */\n enqueue<T>(job: WebhookJob<T>): Promise<void>;\n /** Register the worker handler. Called once during middleware setup. */\n setHandler<T>(handler: WebhookHandler<T>): void;\n /** Optional: list jobs in the dead-letter queue (for the admin replay UI). */\n listDeadLetters?(): Promise<DeadLetterRecord[]>;\n /** Optional: replay a specific dead-letter job back through the handler. */\n replayDeadLetter?(id: string): Promise<void>;\n /** Optional: graceful shutdown. */\n close?(): Promise<void>;\n}\n\nexport interface DeadLetterRecord {\n id: string;\n job: WebhookJob;\n attempts: number;\n lastError: string;\n failedAt: string;\n}\n\n/**\n * Synchronous, in-process implementation. Default — no extra infra required.\n * Handler runs inside the HTTP request, so route latency = handler latency.\n *\n * Failures are NOT retried — the original Apple/Google source retry policy\n * is the durability layer (4xx = no retry, 5xx = source retries). This\n * matches the pre-queue behavior.\n */\nexport class InProcessWebhookQueue implements WebhookQueue {\n private handler: WebhookHandler | null = null;\n\n setHandler<T>(handler: WebhookHandler<T>): void {\n this.handler = handler as WebhookHandler;\n }\n\n async enqueue<T>(job: WebhookJob<T>): Promise<void> {\n if (!this.handler) {\n throw new Error('[onesub] webhook queue has no handler registered');\n }\n await this.handler(job);\n }\n}\n\n/**\n * BullMQ-backed implementation — durable Redis queue with retries and a\n * dead-letter list. `bullmq` is an optional peer dependency; install it\n * separately:\n *\n * npm install bullmq ioredis\n *\n * Usage:\n * import { Queue, Worker } from 'bullmq';\n * import IORedis from 'ioredis';\n * import { BullMQWebhookQueue } from '@onesub/server';\n *\n * const connection = new IORedis(process.env.REDIS_URL!, { maxRetriesPerRequest: null });\n * const queue = new BullMQWebhookQueue({ connection });\n *\n * app.use(createOneSubMiddleware({ ...config, webhookQueue: queue }));\n *\n * Note: the worker is started inside `setHandler`. The webhook route\n * `enqueue`s and 200s immediately, so source latency is dominated by the\n * Redis round-trip, not the receipt-validation pipeline.\n */\n/**\n * `bullmq` is an optional peer — we don't depend on its types at compile\n * time. The minimal-shape interfaces below keep TS strict-mode happy\n * without forcing every onesub user to install bullmq just to type-check.\n */\ninterface BullMQJob {\n id: string | number | undefined;\n data: unknown;\n attemptsMade: number;\n failedReason?: string;\n finishedOn?: number;\n retry(): Promise<unknown>;\n}\ninterface BullMQQueue {\n add(name: string, data: unknown, opts?: unknown): Promise<unknown>;\n getJob(id: string): Promise<BullMQJob | undefined>;\n getFailed(): Promise<BullMQJob[]>;\n close(): Promise<void>;\n /** Queue extends EventEmitter — optional in the shape so test stubs stay minimal. */\n on?(event: string, listener: (...args: unknown[]) => void): unknown;\n}\ninterface BullMQWorker {\n close(): Promise<void>;\n /** Worker extends EventEmitter — optional in the shape so test stubs stay minimal. */\n on?(event: string, listener: (...args: unknown[]) => void): unknown;\n}\ninterface BullMQModule {\n Queue: new (name: string, opts: { connection: unknown }) => BullMQQueue;\n Worker: new (\n name: string,\n fn: (job: { data: unknown }) => Promise<void>,\n opts: { connection: unknown; concurrency?: number },\n ) => BullMQWorker;\n}\n\nexport interface BullMQWebhookQueueOptions {\n /** ioredis connection (or compatible options). Required. */\n connection: unknown;\n /** Queue name. Defaults to 'onesub-webhooks'. */\n queueName?: string;\n /** Max retry attempts before sending the job to the dead-letter list. */\n maxAttempts?: number;\n /** Backoff (ms) between attempts. Exponential: backoffMs * 2^(attempt-1). */\n backoffMs?: number;\n /** Worker concurrency. Defaults to 4. */\n concurrency?: number;\n}\n\nexport class BullMQWebhookQueue implements WebhookQueue {\n private queueName: string;\n private maxAttempts: number;\n private backoffMs: number;\n private concurrency: number;\n private connection: unknown;\n\n // Lazy-loaded so `bullmq` doesn't have to be installed unless this class\n // is instantiated.\n private queuePromise: Promise<BullMQQueue> | null = null;\n private workerPromise: Promise<BullMQWorker> | null = null;\n private handler: WebhookHandler | null = null;\n /**\n * Captured worker-startup failure (e.g. `bullmq` not installed). `setHandler`\n * is synchronous, so it can't reject — the error is surfaced on the next\n * `enqueue()` instead of becoming an unhandled rejection that kills the\n * process.\n */\n private workerStartupError: Error | null = null;\n\n constructor(opts: BullMQWebhookQueueOptions) {\n this.connection = opts.connection;\n this.queueName = opts.queueName ?? 'onesub-webhooks';\n this.maxAttempts = opts.maxAttempts ?? 5;\n this.backoffMs = opts.backoffMs ?? 1000;\n this.concurrency = opts.concurrency ?? 4;\n }\n\n private async getBullMQ(): Promise<BullMQModule> {\n return import('bullmq' as string).catch(() => {\n throw new Error('[onesub] BullMQWebhookQueue requires the `bullmq` package. Run: npm install bullmq');\n }) as Promise<BullMQModule>;\n }\n\n private getQueue(): Promise<BullMQQueue> {\n if (!this.queuePromise) {\n this.queuePromise = (async () => {\n const { Queue } = await this.getBullMQ();\n const queue = new Queue(this.queueName, { connection: this.connection });\n // Without an 'error' listener, a Redis hiccup on the EventEmitter\n // becomes an uncaught 'error' event and crashes the process.\n queue.on?.('error', (err) => {\n log.error('[onesub] BullMQ queue error:', err);\n });\n return queue;\n })();\n }\n return this.queuePromise;\n }\n\n setHandler<T>(handler: WebhookHandler<T>): void {\n this.handler = handler as WebhookHandler;\n if (!this.workerPromise) {\n this.workerPromise = (async () => {\n const { Worker } = await this.getBullMQ();\n const worker = new Worker(\n this.queueName,\n async (job) => {\n if (!this.handler) throw new Error('[onesub] handler not set');\n await this.handler(job.data as WebhookJob);\n },\n {\n connection: this.connection,\n concurrency: this.concurrency,\n },\n );\n // Same rationale as the queue's 'error' listener above. Job failures\n // are NOT this event — they go through the retry/dead-letter flow.\n worker.on?.('error', (err) => {\n log.error('[onesub] BullMQ worker error:', err);\n });\n return worker;\n })();\n // The worker starts in the background — swallow the rejection here\n // (otherwise: unhandled rejection → process crash when bullmq is\n // missing) and surface it on the next enqueue instead.\n this.workerPromise.catch((err) => {\n this.workerStartupError = err instanceof Error ? err : new Error(String(err));\n log.error('[onesub] BullMQ worker failed to start:', err);\n });\n }\n }\n\n async enqueue<T>(job: WebhookJob<T>): Promise<void> {\n if (this.workerStartupError) throw this.workerStartupError;\n const queue = await this.getQueue();\n await queue.add('webhook', job, {\n attempts: this.maxAttempts,\n backoff: { type: 'exponential', delay: this.backoffMs },\n removeOnFail: false,\n removeOnComplete: { age: 24 * 60 * 60, count: 1000 },\n jobId: `${job.provider}:${job.eventId}`,\n });\n }\n\n async listDeadLetters(): Promise<DeadLetterRecord[]> {\n const queue = await this.getQueue();\n const failed = await queue.getFailed();\n return failed.map((j) => ({\n id: String(j.id),\n job: j.data as WebhookJob,\n attempts: j.attemptsMade,\n lastError: j.failedReason ?? 'unknown',\n failedAt: new Date(j.finishedOn ?? Date.now()).toISOString(),\n }));\n }\n\n async replayDeadLetter(id: string): Promise<void> {\n const queue = await this.getQueue();\n const job = await queue.getJob(id);\n if (!job) throw new Error(`[onesub] dead-letter job ${id} not found`);\n await job.retry();\n }\n\n async close(): Promise<void> {\n if (this.workerPromise) {\n // A rejected workerPromise (startup failure) has nothing to close.\n const worker = await this.workerPromise.catch(() => null);\n if (worker) await worker.close();\n }\n if (this.queuePromise) {\n const queue = await this.queuePromise.catch(() => null);\n if (queue) await queue.close();\n }\n }\n}\n","/**\n * OpenAPI 3.1 spec for the onesub HTTP surface.\n *\n * Hand-maintained — kept in sync with the routes by a parity test (see\n * __tests__/openapi.test.ts) that mounts every router via\n * `createOneSubMiddleware`, walks the express router stack, and asserts every\n * mounted path+method is described here (and vice versa — no stale spec\n * entries). Generated clients live downstream:\n *\n * - swagger-typescript-api → typed fetch client\n * - openapi-generator-cli → Kotlin/Swift/etc. clients\n *\n * Why hand-maintained instead of zod-to-openapi: the routes use zod for\n * input validation but the request/response shapes the public docs need are\n * a thin slice of those internal types. Hand-writing keeps the doc readable\n * without coupling client output to internal validation strictness changes.\n */\n\nimport { ONESUB_ERROR_CODE, ROUTES } from '@onesub/shared';\n\nexport interface OpenAPIDoc {\n openapi: '3.1.0';\n info: {\n title: string;\n version: string;\n description: string;\n };\n servers: Array<{ url: string; description?: string }>;\n paths: Record<string, unknown>;\n components: {\n schemas: Record<string, unknown>;\n securitySchemes: Record<string, unknown>;\n };\n}\n\n// ── shared fragments ─────────────────────────────────────────────────────────\n// Every onesub 4xx/5xx goes through `sendError` (errors.ts) and carries\n// `{ error, errorCode }`; some routes append shape-compat fields such as\n// `valid: false` / `subscription: null`.\n\nconst ERROR_CONTENT = {\n 'application/json': { schema: { $ref: '#/components/schemas/ErrorResponse' } },\n};\n\n/** Error-response entry: description + the canonical `{ error, errorCode }` body. */\nconst err = (description: string) => ({ description, content: ERROR_CONTENT });\n\n/** `X-Admin-Secret` header — required on every admin + metrics route. */\nconst ADMIN_SECRET_PARAM = {\n in: 'header',\n name: 'X-Admin-Secret',\n required: true,\n schema: { type: 'string' },\n};\n\n/** Shared `from`/`to`/`groupBy` query params for windowed metrics endpoints. */\nconst METRICS_RANGE_PARAMS = [\n { in: 'query', name: 'from', required: true, schema: { type: 'string', format: 'date-time' }, description: 'Window start (ISO 8601).' },\n { in: 'query', name: 'to', required: true, schema: { type: 'string', format: 'date-time' }, description: 'Window end (ISO 8601).' },\n { in: 'query', name: 'groupBy', schema: { type: 'string', enum: ['none', 'day'] }, description: \"'day' adds a zero-filled daily `buckets` series to the response.\" },\n];\n\nexport const ONESUB_OPENAPI: OpenAPIDoc = {\n openapi: '3.1.0',\n info: {\n title: 'onesub HTTP API',\n version: '1.0.0',\n description:\n 'Receipt validation, subscription status, one-time purchases, entitlements, webhooks, metrics, and admin endpoints exposed by `createOneSubMiddleware`.',\n },\n servers: [\n { url: 'http://localhost:4100', description: 'Local dev (examples/server)' },\n ],\n paths: {\n // ── public: subscriptions ────────────────────────────────────────────────\n [ROUTES.VALIDATE]: {\n post: {\n summary: 'Validate an Apple/Google receipt and persist subscription state.',\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { $ref: '#/components/schemas/ValidateRequest' } } },\n },\n responses: {\n 200: { description: 'Validation succeeded; subscription state persisted.', content: { 'application/json': { schema: { $ref: '#/components/schemas/ValidateResponse' } } } },\n 400: err('Bad request — missing fields, malformed receipt, package mismatch.'),\n 409: err('Receipt is account-bound to a different userId (TRANSACTION_BELONGS_TO_OTHER_USER).'),\n 422: err('Receipt validation failed (RECEIPT_VALIDATION_FAILED).'),\n },\n },\n },\n [ROUTES.STATUS]: {\n get: {\n summary: 'Fetch the most recent subscription state for a user.',\n parameters: [{ in: 'query', name: 'userId', required: true, schema: { type: 'string' } }],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/StatusResponse' } } } },\n 400: err('Missing userId or userId too long (INVALID_INPUT / USER_ID_TOO_LONG).'),\n },\n },\n },\n // ── public: one-time purchases ───────────────────────────────────────────\n [ROUTES.VALIDATE_PURCHASE]: {\n post: {\n summary: 'Validate a consumable / non-consumable receipt and record the purchase.',\n description:\n 'Non-consumables are idempotent: an already-owned product returns the stored purchase with `action: \"restored\"`. Consumable receipts can only be redeemed once.',\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { $ref: '#/components/schemas/ValidatePurchaseRequest' } } },\n },\n responses: {\n 200: { description: 'Purchase validated — freshly recorded (`action: \"new\"`) or restored.', content: { 'application/json': { schema: { $ref: '#/components/schemas/ValidatePurchaseResponse' } } } },\n 400: err('Bad request — missing/invalid fields (INVALID_INPUT).'),\n 409: err('Receipt is account-bound or already redeemed by another user (TRANSACTION_BELONGS_TO_OTHER_USER).'),\n 422: err('Receipt validation failed (RECEIPT_VALIDATION_FAILED).'),\n },\n },\n },\n [ROUTES.PURCHASE_STATUS]: {\n get: {\n summary: 'List all recorded purchases for a user, optionally filtered by product.',\n parameters: [\n { in: 'query', name: 'userId', required: true, schema: { type: 'string' } },\n { in: 'query', name: 'productId', schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/PurchaseStatusResponse' } } } },\n 400: err('Missing/invalid userId (INVALID_INPUT).'),\n },\n },\n },\n // ── webhooks ─────────────────────────────────────────────────────────────\n [ROUTES.WEBHOOK_APPLE]: {\n post: {\n summary: 'Apple App Store Server Notification V2 receiver.',\n description:\n 'Apple POSTs `{ signedPayload }`. JWS-verified, idempotent when `webhookEventStore` is configured.',\n responses: {\n 200: { description: 'Acknowledged.' },\n 400: err('Missing or invalid signedPayload.'),\n },\n },\n },\n [ROUTES.WEBHOOK_GOOGLE]: {\n post: {\n summary: 'Google Play Real-Time Developer Notification (Pub/Sub push) receiver.',\n responses: {\n 200: { description: 'Acknowledged.' },\n 400: err('Missing message.data or package mismatch.'),\n 401: err('pushAudience configured and JWT verification failed.'),\n },\n },\n },\n // ── entitlements (mounted when config.entitlements is set) ──────────────\n [ROUTES.ENTITLEMENT]: {\n get: {\n summary: 'Evaluate a single configured entitlement for a user.',\n parameters: [\n { in: 'query', name: 'userId', required: true, schema: { type: 'string' } },\n { in: 'query', name: 'id', required: true, schema: { type: 'string' }, description: 'Entitlement id from `config.entitlements`.' },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/EntitlementResponse' } } } },\n 400: err('userId and id are required (INVALID_INPUT).'),\n 404: err('Unknown entitlement id (ENTITLEMENT_NOT_FOUND).'),\n },\n },\n },\n [ROUTES.ENTITLEMENTS]: {\n get: {\n summary: 'Evaluate every configured entitlement for a user.',\n parameters: [{ in: 'query', name: 'userId', required: true, schema: { type: 'string' } }],\n responses: {\n 200: { description: 'Map of entitlementId → EntitlementStatus.', content: { 'application/json': { schema: { $ref: '#/components/schemas/EntitlementsResponse' } } } },\n 400: err('userId is required (INVALID_INPUT).'),\n },\n },\n },\n // ── admin (mounted when config.adminSecret is set) ───────────────────────\n [ROUTES.ADMIN_SUBSCRIPTIONS]: {\n get: {\n summary: 'Filtered, paginated subscription list (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'query', name: 'userId', schema: { type: 'string' } },\n { in: 'query', name: 'status', schema: { type: 'string', enum: ['active', 'grace_period', 'on_hold', 'paused', 'expired', 'canceled', 'none'] } },\n { in: 'query', name: 'productId', schema: { type: 'string' } },\n { in: 'query', name: 'platform', schema: { type: 'string', enum: ['apple', 'google'] } },\n { in: 'query', name: 'limit', schema: { type: 'integer', minimum: 1, maximum: 200 } },\n { in: 'query', name: 'offset', schema: { type: 'integer', minimum: 0 } },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/ListSubscriptionsResponse' } } } },\n 400: err('Invalid filter/pagination params (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/admin/subscriptions/{transactionId}': {\n get: {\n summary: 'Single subscription record by originalTransactionId (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'transactionId', required: true, schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/SubscriptionInfo' } } } },\n 400: err('transactionId required (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n 404: err('No record for this transactionId (TRANSACTION_NOT_FOUND).'),\n },\n },\n },\n '/onesub/admin/customers/{userId}': {\n get: {\n summary: 'Customer profile bundle: subscriptions + purchases + entitlements (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'userId', required: true, schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK — empty arrays mean \"no record of this user\" (never 404).', content: { 'application/json': { schema: { $ref: '#/components/schemas/CustomerProfileResponse' } } } },\n 400: err('userId required (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/admin/sync-apple/{originalTransactionId}': {\n post: {\n summary: 'Reconcile one subscription from the Apple Status API into the local store (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'originalTransactionId', required: true, schema: { type: 'string' } },\n { in: 'query', name: 'sandbox', schema: { type: 'string', enum: ['true'] }, description: 'Force the sandbox Status API host (otherwise inferred from the stored record).' },\n ],\n responses: {\n 200: { description: 'Fresh state fetched and upserted.', content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' }, subscription: { $ref: '#/components/schemas/SubscriptionInfo' } } } } } },\n 400: err('originalTransactionId missing or Apple API credentials not configured (INVALID_INPUT / APPLE_CONFIG_MISSING).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n 404: err('Apple Status API returned no record (TRANSACTION_NOT_FOUND).'),\n },\n },\n },\n '/onesub/purchase/admin/{userId}/{productId}': {\n delete: {\n summary: 'Reset a purchase so the user can test the flow again (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'userId', required: true, schema: { type: 'string' } },\n { in: 'path', name: 'productId', required: true, schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' }, deleted: { type: 'integer', description: 'Number of purchase rows removed.' } } } } } },\n 400: err('userId and productId required (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/purchase/admin/transfer': {\n post: {\n summary: 'Reassign a transactionId to a new userId — device/account migration (admin).',\n parameters: [ADMIN_SECRET_PARAM],\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { type: 'object', required: ['transactionId', 'newUserId'], properties: { transactionId: { type: 'string' }, newUserId: { type: 'string' } } } } },\n },\n responses: {\n 200: { description: 'Transferred.', content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' }, purchase: { $ref: '#/components/schemas/PurchaseInfo' } } } } } },\n 400: err('Missing/invalid body fields (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n 404: err('No purchase with this transactionId (TRANSACTION_NOT_FOUND).'),\n },\n },\n },\n '/onesub/purchase/admin/grant': {\n post: {\n summary: 'Manually insert a purchase record, skipping store verification (admin).',\n parameters: [ADMIN_SECRET_PARAM],\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { $ref: '#/components/schemas/GrantPurchaseRequest' } } },\n },\n responses: {\n 200: { description: 'Granted.', content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' }, purchase: { $ref: '#/components/schemas/PurchaseInfo' } } } } } },\n 400: err('Missing/invalid body fields (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/admin/webhook-deadletters': {\n get: {\n summary: 'List failed webhook jobs (when a queue with DLQ support is configured).',\n parameters: [ADMIN_SECRET_PARAM],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { type: 'object', properties: { items: { type: 'array', items: { type: 'object', description: 'DeadLetterRecord: { id, job, attemptsMade, lastError, failedAt }.' } } } } } } },\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/admin/webhook-replay/{id}': {\n post: {\n summary: 'Replay a dead-letter job through the webhook handler.',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'id', required: true, schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK' },\n 400: err('id required (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n // ── metrics (mounted when config.adminSecret is set) ─────────────────────\n [ROUTES.METRICS_ACTIVE]: {\n get: {\n summary: 'Snapshot of currently-entitled users (active subs + non-consumable owners).',\n parameters: [ADMIN_SECRET_PARAM],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/MetricsActiveResponse' } } } },\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n [ROUTES.METRICS_STARTED]: {\n get: {\n summary: 'Subscriptions started (purchasedAt) within a window.',\n parameters: [ADMIN_SECRET_PARAM, ...METRICS_RANGE_PARAMS],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/MetricsCountResponse' } } } },\n 400: err('from/to missing, non-ISO, or from > to (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n [ROUTES.METRICS_EXPIRED]: {\n get: {\n summary: 'Subscriptions expired/canceled (expiresAt) within a window.',\n parameters: [ADMIN_SECRET_PARAM, ...METRICS_RANGE_PARAMS],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/MetricsCountResponse' } } } },\n 400: err('from/to missing, non-ISO, or from > to (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n [ROUTES.METRICS_PURCHASES_STARTED]: {\n get: {\n summary: 'Non-consumable purchases started (purchasedAt) within a window.',\n parameters: [ADMIN_SECRET_PARAM, ...METRICS_RANGE_PARAMS],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/MetricsCountResponse' } } } },\n 400: err('from/to missing, non-ISO, or from > to (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n // ── Apple Promotional Offer signing (mounted when offerKeyId + offerPrivateKey set) ──\n [ROUTES.APPLE_OFFER_SIGNATURE]: {\n post: {\n summary: 'Sign an Apple Promotional Offer payload server-side (ES256).',\n description:\n 'The client passes the returned `{ keyId, nonce, timestamp, signature }` to StoreKit to redeem the offer. When `config.adminSecret` is set, the request must carry `X-Onesub-Offer-Secret` with the same value.',\n parameters: [\n { in: 'header', name: 'X-Onesub-Offer-Secret', required: false, schema: { type: 'string' }, description: 'Required (must equal config.adminSecret) when adminSecret is configured.' },\n ],\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { $ref: '#/components/schemas/OfferSignatureRequest' } } },\n },\n responses: {\n 200: { description: 'Signed.', content: { 'application/json': { schema: { $ref: '#/components/schemas/OfferSignatureResponse' } } } },\n 400: err('Missing/invalid body fields or apple.bundleId not configured (INVALID_INPUT / APPLE_CONFIG_MISSING).'),\n 401: err('Offer secret missing or wrong (UNAUTHORIZED).'),\n },\n },\n },\n },\n components: {\n schemas: {\n ErrorResponse: {\n type: 'object',\n required: ['error', 'errorCode'],\n description:\n 'Canonical error body — every onesub 4xx/5xx carries it. Some routes append shape-compat fields (e.g. `valid: false`, `subscription: null`, `purchases: []`).',\n properties: {\n error: { type: 'string', description: 'Human-readable message.' },\n errorCode: { type: 'string', enum: Object.values(ONESUB_ERROR_CODE), description: 'Machine-readable canonical code.' },\n },\n },\n ValidateRequest: {\n type: 'object',\n required: ['userId', 'platform', 'productId', 'receipt'],\n properties: {\n userId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n productId: { type: 'string' },\n receipt: { type: 'string', description: 'Apple JWS or Google purchaseToken' },\n purchaseType: { type: 'string', enum: ['subscription', 'consumable', 'non_consumable'] },\n },\n },\n ValidateResponse: {\n type: 'object',\n properties: {\n valid: { type: 'boolean' },\n subscription: { $ref: '#/components/schemas/SubscriptionInfo' },\n purchase: { $ref: '#/components/schemas/PurchaseInfo' },\n },\n },\n ValidatePurchaseRequest: {\n type: 'object',\n required: ['userId', 'platform', 'productId', 'receipt', 'type'],\n properties: {\n userId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n productId: { type: 'string' },\n receipt: { type: 'string', description: 'Apple JWS or Google purchaseToken' },\n type: { type: 'string', enum: ['consumable', 'non_consumable'] },\n },\n },\n ValidatePurchaseResponse: {\n type: 'object',\n properties: {\n valid: { type: 'boolean' },\n purchase: { $ref: '#/components/schemas/PurchaseInfo' },\n action: { type: 'string', enum: ['new', 'restored'], description: 'Present on valid:true — \"restored\" means the transactionId was already recorded (idempotent retry or reassignment).' },\n },\n },\n PurchaseStatusResponse: {\n type: 'object',\n properties: {\n purchases: { type: 'array', items: { $ref: '#/components/schemas/PurchaseInfo' } },\n },\n },\n StatusResponse: {\n type: 'object',\n properties: {\n active: { type: 'boolean' },\n subscription: { $ref: '#/components/schemas/SubscriptionInfo' },\n },\n },\n SubscriptionInfo: {\n type: 'object',\n properties: {\n originalTransactionId: { type: 'string' },\n userId: { type: 'string' },\n productId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n status: { type: 'string', enum: ['active', 'grace_period', 'on_hold', 'paused', 'expired', 'canceled', 'none'] },\n expiresAt: { type: 'string', format: 'date-time' },\n purchasedAt: { type: 'string', format: 'date-time' },\n willRenew: { type: 'boolean' },\n },\n },\n PurchaseInfo: {\n type: 'object',\n properties: {\n transactionId: { type: 'string' },\n userId: { type: 'string' },\n productId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n type: { type: 'string', enum: ['consumable', 'non_consumable'] },\n quantity: { type: 'integer' },\n purchasedAt: { type: 'string', format: 'date-time' },\n },\n },\n EntitlementStatus: {\n type: 'object',\n required: ['active', 'source'],\n properties: {\n active: { type: 'boolean' },\n source: { type: ['string', 'null'], enum: ['subscription', 'purchase', null], description: 'Where the entitlement came from when active; null when not active.' },\n productId: { type: 'string', description: 'Matched productId (only when active).' },\n expiresAt: { type: 'string', format: 'date-time', description: 'Only when source === \"subscription\".' },\n },\n },\n EntitlementResponse: {\n allOf: [\n { $ref: '#/components/schemas/EntitlementStatus' },\n { type: 'object', required: ['id'], properties: { id: { type: 'string', description: 'The entitlement id queried, echoed back.' } } },\n ],\n },\n EntitlementsResponse: {\n type: 'object',\n properties: {\n entitlements: { type: 'object', additionalProperties: { $ref: '#/components/schemas/EntitlementStatus' } },\n },\n },\n ListSubscriptionsResponse: {\n type: 'object',\n properties: {\n items: { type: 'array', items: { $ref: '#/components/schemas/SubscriptionInfo' } },\n total: { type: 'integer', description: 'Total matches before limit/offset.' },\n limit: { type: 'integer' },\n offset: { type: 'integer' },\n },\n },\n CustomerProfileResponse: {\n type: 'object',\n properties: {\n userId: { type: 'string' },\n subscriptions: { type: 'array', items: { $ref: '#/components/schemas/SubscriptionInfo' } },\n purchases: { type: 'array', items: { $ref: '#/components/schemas/PurchaseInfo' } },\n entitlements: { type: 'object', additionalProperties: { $ref: '#/components/schemas/EntitlementStatus' }, description: 'Omitted when the server has no entitlements config.' },\n },\n },\n GrantPurchaseRequest: {\n type: 'object',\n required: ['userId', 'productId', 'platform'],\n properties: {\n userId: { type: 'string' },\n productId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n type: { type: 'string', enum: ['consumable', 'non_consumable'], default: 'non_consumable' },\n transactionId: { type: 'string', description: 'Auto-generated `admin_grant_*` id when omitted.' },\n },\n },\n MetricsActiveResponse: {\n type: 'object',\n properties: {\n total: { type: 'integer', description: 'Active subs + grace_period subs + non-consumable owners.' },\n activeSubscriptions: { type: 'integer' },\n gracePeriodSubscriptions: { type: 'integer' },\n nonConsumablePurchases: { type: 'integer' },\n byProduct: { type: 'object', additionalProperties: { type: 'integer' }, description: 'Subscription product distribution (subs only).' },\n byProductPurchases: { type: 'object', additionalProperties: { type: 'integer' }, description: 'Non-consumable purchase distribution.' },\n byPlatform: { type: 'object', additionalProperties: { type: 'integer' } },\n },\n },\n MetricsBucket: {\n type: 'object',\n properties: {\n date: { type: 'string', description: 'UTC calendar day, YYYY-MM-DD.' },\n count: { type: 'integer' },\n },\n },\n MetricsCountResponse: {\n type: 'object',\n properties: {\n from: { type: 'string', format: 'date-time' },\n to: { type: 'string', format: 'date-time' },\n total: { type: 'integer' },\n byProduct: { type: 'object', additionalProperties: { type: 'integer' } },\n byPlatform: { type: 'object', additionalProperties: { type: 'integer' } },\n buckets: { type: 'array', items: { $ref: '#/components/schemas/MetricsBucket' }, description: 'Only present with ?groupBy=day — zero-filled, sorted ascending.' },\n },\n },\n OfferSignatureRequest: {\n type: 'object',\n required: ['productId', 'offerId', 'applicationUsername'],\n properties: {\n productId: { type: 'string' },\n offerId: { type: 'string', description: 'Promotional offer id defined in App Store Connect.' },\n applicationUsername: { type: 'string', description: 'Unique per-request UUID (the nonce seed).' },\n },\n },\n OfferSignatureResponse: {\n type: 'object',\n required: ['keyId', 'nonce', 'timestamp', 'signature'],\n properties: {\n keyId: { type: 'string' },\n nonce: { type: 'string' },\n timestamp: { type: 'integer', description: 'Milliseconds since epoch.' },\n signature: { type: 'string', description: 'Base64 ES256 signature for StoreKit.' },\n },\n },\n },\n securitySchemes: {\n AdminSecret: {\n type: 'apiKey',\n in: 'header',\n name: 'X-Admin-Secret',\n },\n },\n },\n};\n\n/**\n * Mount a route that serves the OpenAPI document. Useful for hosts that want\n * to expose `/openapi.json` to API explorers / SDK generators.\n *\n * app.use('/openapi.json', openapiHandler());\n */\nexport function openapiHandler() {\n return (_req: import('express').Request, res: import('express').Response) => {\n res.json(ONESUB_OPENAPI);\n };\n}\n","/**\n * Optional OpenTelemetry tracing.\n *\n * `@opentelemetry/api` is an optional peer dependency — when it's installed\n * onesub wraps its hot paths in spans. When it isn't, every span helper is\n * a zero-cost no-op (no dynamic import, no Promise overhead).\n *\n * Why a custom helper instead of @opentelemetry/instrumentation-express:\n * the express instrumentation traces every request, but onesub's interesting\n * spans are specific operations (Apple JWT mint, Google OAuth refresh,\n * receipt validation, webhook dispatch). Hand-spanned hot paths give\n * actionable traces without doubling latency-tracker overhead.\n */\n\nimport { createRequire } from 'node:module';\n\ntype Tracer = {\n startActiveSpan: <T>(name: string, fn: (span: Span) => T) => T;\n};\n\ntype Span = {\n setAttribute: (key: string, value: string | number | boolean) => void;\n recordException: (err: unknown) => void;\n setStatus: (status: { code: number; message?: string }) => void;\n end: () => void;\n};\n\nconst NOOP_SPAN: Span = {\n setAttribute: () => {},\n recordException: () => {},\n setStatus: () => {},\n end: () => {},\n};\n\n/**\n * Lazy-loaded tracer. We don't reach for `@opentelemetry/api` until the\n * first `withSpan` call so process startup pays nothing when otel is absent.\n */\nlet cachedTracer: Tracer | null = null;\nlet resolved = false;\n\nfunction getTracer(): Tracer | null {\n if (resolved) return cachedTracer;\n resolved = true;\n try {\n // Resolve synchronously — otel api is CJS-friendly. In the CJS bundle the\n // native `require` exists, so use it directly; in ESM we mint one via\n // createRequire(import.meta.url). The `typeof require` guard matters for\n // the CJS output: tsup's esbuild pass lowers `import.meta` to an empty\n // object there (shims are off in tsup.config.ts), so that expression must\n // only be evaluated on the ESM path — where it is fully supported.\n const req: NodeRequire =\n typeof require === 'function' ? require : createRequire(import.meta.url);\n const otel = req('@opentelemetry/api') as { trace: { getTracer: (n: string, v: string) => Tracer } };\n cachedTracer = otel.trace.getTracer('@onesub/server', '1.0.0');\n return cachedTracer;\n } catch {\n return null;\n }\n}\n\n/**\n * Wrap an async operation in a span. When otel isn't installed the function\n * runs unmodified — no span object is allocated.\n *\n * Use for operations whose latency or failure rate is operationally\n * interesting: receipt validation, store writes, outbound API calls.\n */\nexport async function withSpan<T>(\n name: string,\n attributes: Record<string, string | number | boolean>,\n fn: (span: Span) => Promise<T>,\n): Promise<T> {\n const tracer = getTracer();\n if (!tracer) return fn(NOOP_SPAN);\n\n return tracer.startActiveSpan(name, async (span) => {\n for (const [k, v] of Object.entries(attributes)) span.setAttribute(k, v);\n try {\n const result = await fn(span);\n // status code 1 = OK, 2 = ERROR (avoid importing the enum from otel api)\n span.setStatus({ code: 1 });\n return result;\n } catch (err) {\n span.recordException(err);\n span.setStatus({ code: 2, message: (err as Error).message });\n throw err;\n } finally {\n span.end();\n }\n });\n}\n","import express, { Router } from 'express';\nimport type { OneSubServerConfig } from '@onesub/shared';\nimport { DEFAULT_PORT } from '@onesub/shared';\nimport { InMemorySubscriptionStore, InMemoryPurchaseStore } from './store.js';\nimport type { SubscriptionStore, PurchaseStore } from './store.js';\nimport { createValidateRouter } from './routes/validate.js';\nimport { createStatusRouter } from './routes/status.js';\nimport { createWebhookRouter } from './routes/webhook.js';\nimport { createPurchaseRouter } from './routes/purchase.js';\nimport { createAdminRouter } from './routes/admin.js';\nimport { createEntitlementRouter } from './routes/entitlements.js';\nimport { createMetricsRouter } from './routes/metrics.js';\nimport { createAppleOfferRouter } from './routes/apple-offer.js';\nimport { setLogger } from './logger.js';\nimport type { CacheAdapter } from './cache.js';\nimport { setDefaultCache } from './cache.js';\nimport type { WebhookEventStore } from './webhook-events.js';\nimport type { WebhookQueue } from './webhook-queue.js';\n\n/**\n * Extended config with optional pluggable stores.\n */\nexport interface OneSubMiddlewareConfig extends OneSubServerConfig {\n /**\n * Custom subscription store. Defaults to InMemorySubscriptionStore.\n * For production, provide a PostgreSQL or Redis backed implementation.\n */\n store?: SubscriptionStore;\n /**\n * Custom purchase store for consumables and non-consumables.\n * Defaults to InMemoryPurchaseStore.\n * For production, provide a PostgreSQL backed implementation.\n */\n purchaseStore?: PurchaseStore;\n /**\n * Cache backend for short-lived secrets (Apple JWT, Google OAuth token).\n * Default: in-memory, process-local. For multi-instance deployments pass a\n * `RedisCacheAdapter` so all nodes share a single mint per TTL window.\n */\n cache?: CacheAdapter;\n /**\n * Webhook idempotency store. Default: none (rely on Apple/Google retry +\n * downstream store PKs). For production strongly recommended — pass an\n * `InMemoryWebhookEventStore` (single instance) or `CacheWebhookEventStore`\n * backed by Redis (multi-instance) so duplicate notifications never apply\n * twice.\n */\n webhookEventStore?: WebhookEventStore;\n /**\n * Async webhook queue. Default: none — webhook routes process notifications\n * synchronously inside the request (route latency = handler latency;\n * Apple/Google source retries on 5xx are the durability layer).\n *\n * When set, the webhook routes keep the cheap gating inline (validation,\n * JWS/push-token verification, bundleId/packageName checks, idempotency\n * dedup) and `enqueue()` the decoded work, acking 200 as soon as the job is\n * accepted. The state-mutating processing runs in the queue handler, which\n * is registered once here at middleware creation via `setHandler()`.\n *\n * Pass a `BullMQWebhookQueue` for durable retries with backoff; jobs that\n * exhaust their attempts land in the dead-letter list, accessible via\n * `GET /onesub/admin/webhook-deadletters` and replayable via\n * `POST /onesub/admin/webhook-replay/:id` (requires `adminSecret`).\n */\n webhookQueue?: WebhookQueue;\n}\n\n/**\n * Create an Express Router with all OneSub routes mounted.\n *\n * Mount this in your existing Express app:\n * ```ts\n * import { createOneSubMiddleware } from '@onesub/server';\n * app.use(createOneSubMiddleware(config));\n * ```\n *\n * The routes registered are (all prefixed with nothing — mount at root or use\n * a prefix via `app.use('/prefix', createOneSubMiddleware(config))`):\n *\n * POST /onesub/validate\n * GET /onesub/status\n * POST /onesub/webhook/apple\n * POST /onesub/webhook/google\n */\nexport function createOneSubMiddleware(config: OneSubMiddlewareConfig): Router {\n setLogger(config.logger);\n\n // Hard guard — mockMode accepts ANY receipt as valid. Letting this run on\n // a production server would be a fraud disaster. `skipJwsVerification` has\n // a similar shape but only degrades Apple signature checking; this one is\n // strictly worse, so the check is an error, not a warning.\n if ((config.apple?.mockMode || config.google?.mockMode) && process.env['NODE_ENV'] === 'production') {\n throw new Error(\n '[onesub] apple.mockMode / google.mockMode cannot be enabled when NODE_ENV=production — these modes accept any receipt as valid.',\n );\n }\n\n const store: SubscriptionStore = config.store ?? new InMemorySubscriptionStore();\n const purchaseStore: PurchaseStore = config.purchaseStore ?? new InMemoryPurchaseStore();\n\n // Swap the global cache adapter once when middleware is created. This is\n // the cheapest way to share a Redis-backed cache between all providers\n // without threading the adapter through every internal call site.\n if (config.cache) setDefaultCache(config.cache);\n\n const router = Router();\n\n // Parse JSON bodies for all OneSub routes (50 kb cap to prevent payload flooding)\n router.use(express.json({ limit: '50kb' }));\n\n router.use(createValidateRouter(config, store));\n router.use(createStatusRouter(store));\n router.use(createWebhookRouter(config, store, purchaseStore, config.webhookEventStore, config.webhookQueue));\n router.use(createPurchaseRouter(config, purchaseStore));\n\n // Admin routes — only mounted when config.adminSecret is set\n const adminRouter = createAdminRouter(config, purchaseStore, store, config.webhookQueue);\n if (adminRouter) router.use(adminRouter);\n\n // Entitlement routes — only mounted when config.entitlements is set\n const entitlementRouter = createEntitlementRouter(config, store, purchaseStore);\n if (entitlementRouter) router.use(entitlementRouter);\n\n // Metrics routes — only mounted when config.adminSecret is set (same gate\n // as admin routes; metrics expose aggregate operational data)\n const metricsRouter = createMetricsRouter(config, store, purchaseStore);\n if (metricsRouter) router.use(metricsRouter);\n\n // Apple Promotional Offer signing — only mounted when offerKeyId + offerPrivateKey are set\n const appleOfferRouter = createAppleOfferRouter(config);\n if (appleOfferRouter) router.use(appleOfferRouter);\n\n return router;\n}\n\n/**\n * Create a standalone Express application with all OneSub routes.\n *\n * ```ts\n * import { createOneSubServer } from '@onesub/server';\n * createOneSubServer(config).listen(4100);\n * ```\n */\nexport function createOneSubServer(config: OneSubMiddlewareConfig): ReturnType<typeof express> {\n const app = express();\n\n // Health check\n app.get('/health', (_req, res) => {\n res.json({ ok: true, service: 'onesub' });\n });\n\n app.use(createOneSubMiddleware(config));\n\n return app;\n}\n\n// Named re-exports for consumers who want to bring their own store\nexport { InMemorySubscriptionStore, InMemoryPurchaseStore } from './store.js';\nexport type { SubscriptionStore, PurchaseStore } from './store.js';\nexport { PostgresSubscriptionStore, PostgresPurchaseStore } from './stores/postgres.js';\nexport {\n RedisSubscriptionStore,\n RedisPurchaseStore,\n RedisCacheAdapter,\n RedisWebhookEventStore,\n} from './stores/redis.js';\n\n// Cache adapters\nexport type { CacheAdapter } from './cache.js';\nexport { InMemoryCacheAdapter, getDefaultCache, setDefaultCache } from './cache.js';\n\n// Webhook hardening primitives\nexport type { WebhookEventStore } from './webhook-events.js';\nexport { InMemoryWebhookEventStore, CacheWebhookEventStore, unmarkWebhookEvent } from './webhook-events.js';\nexport type { WebhookQueue, WebhookJob, WebhookHandler, DeadLetterRecord } from './webhook-queue.js';\nexport { InProcessWebhookQueue, BullMQWebhookQueue } from './webhook-queue.js';\n// Post-gate webhook processors — the same functions the built-in queue\n// handler runs. Exported so hosts running a dedicated worker process (e.g.\n// a standalone BullMQ Worker) can call `queue.setHandler(...)` themselves\n// with identical processing semantics.\nexport { processAppleNotification } from './routes/webhook-apple.js';\nexport type { AppleWebhookWork } from './routes/webhook-apple.js';\nexport { processGoogleNotification } from './routes/webhook-google.js';\nexport type { GoogleWebhookWork } from './routes/webhook-google.js';\n\n// OpenAPI document — for hosts that want to expose `/openapi.json` and\n// generate clients from the spec.\nexport { ONESUB_OPENAPI, openapiHandler } from './openapi.js';\nexport type { OpenAPIDoc } from './openapi.js';\n\n// OpenTelemetry helpers — exposed so host apps can wrap their own\n// integrations in spans that inherit the same tracer config.\nexport { withSpan } from './tracing.js';\n\n// Provider functions for direct (non-HTTP) usage\nexport {\n validateAppleReceipt,\n fetchAppleSubscriptionStatus,\n fetchAppleTransactionHistory,\n signApplePromotionalOffer,\n} from './providers/apple.js';\nexport { validateGoogleReceipt } from './providers/google.js';\n\n// Entitlement evaluator — exported so hosts can evaluate entitlements\n// in-process (e.g. from non-HTTP background workers, custom routes).\nexport { evaluateEntitlement } from './routes/entitlements.js';\n\n// Logger plumbing — expose so non-middleware callers (direct provider use)\n// can still redirect logs.\nexport { setLogger, log } from './logger.js';\n\n// Allow running directly: node dist/index.js\n// Reads config from environment variables for quick local testing.\nconst isMain =\n typeof process !== 'undefined' &&\n process.argv[1] != null &&\n new URL(import.meta.url).pathname.endsWith(process.argv[1].replace(/\\\\/g, '/'));\n\nif (isMain) {\n const config: OneSubMiddlewareConfig = {\n apple: process.env['APPLE_BUNDLE_ID']\n ? {\n bundleId: process.env['APPLE_BUNDLE_ID'],\n sharedSecret: process.env['APPLE_SHARED_SECRET'],\n keyId: process.env['APPLE_KEY_ID'],\n issuerId: process.env['APPLE_ISSUER_ID'],\n privateKey: process.env['APPLE_PRIVATE_KEY'],\n }\n : undefined,\n google: process.env['GOOGLE_PACKAGE_NAME']\n ? {\n packageName: process.env['GOOGLE_PACKAGE_NAME'],\n serviceAccountKey: process.env['GOOGLE_SERVICE_ACCOUNT_KEY'],\n }\n : undefined,\n database: {\n url: process.env['DATABASE_URL'] ?? '',\n },\n webhookSecret: process.env['WEBHOOK_SECRET'],\n };\n\n const port = process.env['PORT'] ? parseInt(process.env['PORT'], 10) : DEFAULT_PORT;\n\n createOneSubServer(config).listen(port, () => {\n console.log(`[onesub] Server listening on port ${port}`);\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/store.ts","../src/providers/apple-root-ca.ts","../src/logger.ts","../src/http.ts","../src/cache.ts","../src/providers/mock.ts","../src/providers/apple.ts","../src/providers/google.ts","../src/errors.ts","../src/apps.ts","../src/routes/validate.ts","../src/routes/status.ts","../src/webhook-events.ts","../src/routes/webhook-apple.ts","../src/routes/webhook-google.ts","../src/routes/webhook.ts","../src/routes/purchase.ts","../src/routes/entitlements.ts","../src/routes/secret-compare.ts","../src/routes/admin.ts","../src/routes/metrics.ts","../src/routes/apple-offer.ts","../src/stores/schema.ts","../src/stores/postgres.ts","../src/stores/redis.ts","../src/webhook-queue.ts","../src/openapi.ts","../src/tracing.ts","../src/index.ts"],"names":["err","MOCK_RECEIPT_PREFIX","createHash","SUBSCRIPTION_STATUS","X509Certificate","decodeJwt","decodeProtectedHeader","importX509","jwtVerify","importPKCS8","SignJWT","randomUUID","createSign","ONESUB_ERROR_CODE","z","Router","ROUTES","NO_SUB","response","createRemoteJWKSet","existing","notificationType","purchaseToken","PURCHASE_TYPE","timingSafeEqual","ADMIN_SECRET_HEADER","filtered","raws","createRequire","express","DEFAULT_PORT"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { SubscriptionInfo, PurchaseInfo, SubscriptionStatus, Platform } from '@onesub/shared';\n\n/** Filter options for SubscriptionStore.listFiltered. All fields optional. */\nexport interface ListFilteredOptions {\n userId?: string;\n status?: SubscriptionStatus;\n productId?: string;\n platform?: Platform;\n /** Max items to return. Defaults to 50, capped at 200 by the route handler. */\n limit?: number;\n /** Pagination cursor. Defaults to 0. */\n offset?: number;\n}\n\n/** Result of SubscriptionStore.listFiltered. */\nexport interface ListFilteredResult {\n items: SubscriptionInfo[];\n /** Total matches before limit/offset — used for pagination UI. */\n total: number;\n limit: number;\n offset: number;\n}\n\n/**\n * Pluggable subscription store interface.\n * Default is in-memory. Replace with a PostgreSQL/Redis implementation\n * by passing `store` in OneSubServerConfig.\n */\nexport interface SubscriptionStore {\n save(sub: SubscriptionInfo): Promise<void>;\n /**\n * Returns the most recent subscription for the user, or null if none exist.\n * Use this for legacy single-product checks (`/onesub/status`); for\n * entitlements that span multiple productIds, prefer `getAllByUserId`.\n */\n getByUserId(userId: string): Promise<SubscriptionInfo | null>;\n getByTransactionId(txId: string): Promise<SubscriptionInfo | null>;\n /**\n * Returns every subscription record in the store. Used by metrics\n * aggregation. Hosts shouldn't expose this through unauthenticated routes —\n * the built-in `/onesub/metrics/*` endpoints gate it behind `adminSecret`.\n */\n listAll(): Promise<SubscriptionInfo[]>;\n /**\n * Filtered, paginated subscription list. Used by the dashboard's\n * subscriptions page and any admin tool that needs to enumerate records\n * without pulling the entire table.\n *\n * Filter semantics: each non-undefined field is an AND condition.\n * Sorting: most-recently-updated first (PostgresStore uses `updated_at DESC`;\n * InMemoryStore approximates via insertion order with newest at the front).\n */\n listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult>;\n /**\n * Returns every subscription record for the user (across all productIds),\n * ordered most-recent-first. Used by entitlement evaluation, which needs to\n * see all of a user's active subscriptions to decide whether any of them\n * grants the requested entitlement.\n *\n * Implementations: in-memory currently coalesces by userId so this returns\n * at most one row; Postgres returns the full history.\n */\n getAllByUserId(userId: string): Promise<SubscriptionInfo[]>;\n}\n\n/**\n * In-memory implementation — suitable for development and testing.\n * Data is lost on process restart.\n */\nexport class InMemorySubscriptionStore implements SubscriptionStore {\n // Multiple records per user (different originalTransactionIds) — required\n // for entitlement evaluation across multiple productIds. Ordering is\n // last-written-first so getByUserId returns \"most recent\" naturally.\n private readonly byUserId = new Map<string, SubscriptionInfo[]>();\n private readonly byTransactionId = new Map<string, SubscriptionInfo>();\n\n async save(sub: SubscriptionInfo): Promise<void> {\n // If this transaction was previously bound to a different userId (the\n // validate route rebinds ownership on re-validation), remove the stale\n // copy from the old user's index — otherwise webhooks (which update by\n // transactionId) can never touch it and the old user keeps a frozen\n // 'active' record forever.\n const prev = this.byTransactionId.get(sub.originalTransactionId);\n if (prev && prev.userId !== sub.userId) {\n const oldList = (this.byUserId.get(prev.userId) ?? []).filter(\n (s) => s.originalTransactionId !== sub.originalTransactionId,\n );\n if (oldList.length) this.byUserId.set(prev.userId, oldList);\n else this.byUserId.delete(prev.userId);\n }\n\n this.byTransactionId.set(sub.originalTransactionId, sub);\n\n const existing = this.byUserId.get(sub.userId) ?? [];\n // Replace any prior record with the same originalTransactionId, then\n // unshift to the front so the latest write is index 0 (= getByUserId result).\n const filtered = existing.filter((s) => s.originalTransactionId !== sub.originalTransactionId);\n filtered.unshift(sub);\n this.byUserId.set(sub.userId, filtered);\n }\n\n async getByUserId(userId: string): Promise<SubscriptionInfo | null> {\n const list = this.byUserId.get(userId);\n return list?.[0] ?? null;\n }\n\n async getAllByUserId(userId: string): Promise<SubscriptionInfo[]> {\n return [...(this.byUserId.get(userId) ?? [])];\n }\n\n async getByTransactionId(txId: string): Promise<SubscriptionInfo | null> {\n return this.byTransactionId.get(txId) ?? null;\n }\n\n async listAll(): Promise<SubscriptionInfo[]> {\n return [...this.byTransactionId.values()];\n }\n\n async listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult> {\n const limit = opts.limit ?? 50;\n const offset = opts.offset ?? 0;\n // Iterate via byUserId so insertion-order is \"newest first\" within each\n // user (matches PostgresStore's `updated_at DESC` semantic). Across users\n // we collect in Map iteration order — stable for a given run.\n const all: SubscriptionInfo[] = [];\n for (const list of this.byUserId.values()) {\n for (const s of list) all.push(s);\n }\n const filtered = all.filter((s) => {\n if (opts.userId && s.userId !== opts.userId) return false;\n if (opts.status && s.status !== opts.status) return false;\n if (opts.productId && s.productId !== opts.productId) return false;\n if (opts.platform && s.platform !== opts.platform) return false;\n return true;\n });\n return {\n items: filtered.slice(offset, offset + limit),\n total: filtered.length,\n limit,\n offset,\n };\n }\n}\n\n/**\n * Pluggable purchase store interface for consumables and non-consumables.\n */\nexport interface PurchaseStore {\n savePurchase(purchase: PurchaseInfo): Promise<void>;\n getPurchasesByUserId(userId: string): Promise<PurchaseInfo[]>;\n getPurchaseByTransactionId(txId: string): Promise<PurchaseInfo | null>;\n /** Returns every purchase record. Used by metrics aggregation; admin-gated. */\n listAll(): Promise<PurchaseInfo[]>;\n /** For non-consumables: check if a user has already purchased a product. */\n hasPurchased(userId: string, productId: string): Promise<boolean>;\n /**\n * Admin: delete all purchases matching userId + productId.\n * Used by the admin reset endpoint to allow re-testing non-consumables.\n * Returns the number of rows deleted.\n */\n deletePurchases(userId: string, productId: string): Promise<number>;\n /**\n * Delete a single purchase by its transactionId.\n * Used by the refund/voided-purchase webhook path to revoke entitlement\n * for the exact transaction that was refunded — without touching sibling\n * consumable purchases of the same user/product.\n * Returns true if a row was deleted.\n */\n deletePurchaseByTransactionId(transactionId: string): Promise<boolean>;\n /**\n * Reassign a transaction's owner to a new userId.\n * Used when the validate route encounters TRANSACTION_BELONGS_TO_OTHER_USER\n * for a genuinely-signed JWS — the Apple receipt proves the caller owns the\n * original Apple account, so it's safe to transfer ownership (device\n * reinstall, account migration).\n * Returns true if a row was updated, false if the transactionId was not found.\n */\n reassignPurchase(transactionId: string, newUserId: string): Promise<boolean>;\n}\n\n/**\n * In-memory implementation of PurchaseStore — suitable for development and testing.\n * Data is lost on process restart.\n */\nexport class InMemoryPurchaseStore implements PurchaseStore {\n private readonly byTransactionId = new Map<string, PurchaseInfo>();\n private readonly byUserId = new Map<string, PurchaseInfo[]>();\n\n async savePurchase(purchase: PurchaseInfo): Promise<void> {\n const existing = this.byTransactionId.get(purchase.transactionId);\n if (existing) {\n if (existing.userId !== purchase.userId) {\n const err = new Error('TRANSACTION_BELONGS_TO_OTHER_USER') as Error & { code?: string };\n err.code = 'TRANSACTION_BELONGS_TO_OTHER_USER';\n throw err;\n }\n return; // same user — idempotent\n }\n this.byTransactionId.set(purchase.transactionId, purchase);\n const list = this.byUserId.get(purchase.userId) ?? [];\n list.push(purchase);\n this.byUserId.set(purchase.userId, list);\n }\n\n async getPurchasesByUserId(userId: string): Promise<PurchaseInfo[]> {\n return this.byUserId.get(userId) ?? [];\n }\n\n async getPurchaseByTransactionId(txId: string): Promise<PurchaseInfo | null> {\n return this.byTransactionId.get(txId) ?? null;\n }\n\n async hasPurchased(userId: string, productId: string): Promise<boolean> {\n const purchases = this.byUserId.get(userId);\n if (!purchases) return false;\n return purchases.some((p) => p.productId === productId);\n }\n\n async reassignPurchase(transactionId: string, newUserId: string): Promise<boolean> {\n const existing = this.byTransactionId.get(transactionId);\n if (!existing) return false;\n const oldUserId = existing.userId;\n if (oldUserId === newUserId) return true;\n const updated = { ...existing, userId: newUserId };\n this.byTransactionId.set(transactionId, updated);\n // remove from old userId index\n const oldList = (this.byUserId.get(oldUserId) ?? []).filter((p) => p.transactionId !== transactionId);\n if (oldList.length) this.byUserId.set(oldUserId, oldList);\n else this.byUserId.delete(oldUserId);\n // add to new userId index\n const newList = this.byUserId.get(newUserId) ?? [];\n newList.push(updated);\n this.byUserId.set(newUserId, newList);\n return true;\n }\n\n async deletePurchases(userId: string, productId: string): Promise<number> {\n const list = this.byUserId.get(userId) ?? [];\n const kept = list.filter((p) => p.productId !== productId);\n const deleted = list.length - kept.length;\n this.byUserId.set(userId, kept);\n for (const p of list) {\n if (p.productId === productId) this.byTransactionId.delete(p.transactionId);\n }\n return deleted;\n }\n\n async listAll(): Promise<PurchaseInfo[]> {\n return [...this.byTransactionId.values()];\n }\n\n async deletePurchaseByTransactionId(transactionId: string): Promise<boolean> {\n const existing = this.byTransactionId.get(transactionId);\n if (!existing) return false;\n this.byTransactionId.delete(transactionId);\n const list = (this.byUserId.get(existing.userId) ?? []).filter(\n (p) => p.transactionId !== transactionId,\n );\n if (list.length) this.byUserId.set(existing.userId, list);\n else this.byUserId.delete(existing.userId);\n return true;\n }\n}\n","/**\n * Apple Root CA certificates used to verify StoreKit 2 signed transactions.\n *\n * Apple signs JWS transactions with an ECDSA key; the signing certificate\n * chain is embedded in the JWS `x5c` header. To trust the leaf key we must\n * validate the chain all the way up to one of Apple's published root CAs.\n *\n * Source: https://www.apple.com/certificateauthority/\n * These certificates are public and static. Update this list if Apple\n * publishes a new root — existing JWS signed with older roots continue to\n * validate against their original root.\n */\n\n/**\n * Apple Root CA - G3 (current root for StoreKit 2 / App Store Server API).\n * SHA-256 fingerprint: 63343ABF B89A6A03 EB994052 C5ADBBF8 A87E2C46 81F6F0FC C8A4DA48 95F0FBBF\n * Valid: 2014-04-30 — 2039-04-30\n */\nexport const APPLE_ROOT_CA_G3_PEM = `-----BEGIN CERTIFICATE-----\nMIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwS\nQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u\nIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN\nMTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBS\nb290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y\naXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49\nAgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtf\nTjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517\nIDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySr\nMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gA\nMGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4\nat+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM\n6BgD56KyKA==\n-----END CERTIFICATE-----`;\n\n/**\n * All Apple root CAs accepted for StoreKit 2 JWS validation.\n * Currently only G3; add G4 here when Apple publishes it.\n */\nexport const APPLE_ROOT_CA_PEMS = [APPLE_ROOT_CA_G3_PEM];\n","import type { OneSubLogger } from '@onesub/shared';\n\n/**\n * Process-wide logger used by @onesub/server's providers and routes.\n *\n * Defaults to `console`. `createOneSubMiddleware()` calls `setLogger()` once\n * during setup if `config.logger` is provided. All internal call sites should\n * import `log` from this module instead of calling `console.*` directly so\n * operators can redirect logs (pino / winston / bunyan) with a single config\n * setting.\n */\n\nlet current: OneSubLogger = console;\n\nexport function setLogger(logger: OneSubLogger | undefined): void {\n if (logger) current = logger;\n}\n\nexport const log: OneSubLogger = {\n info: (...args) => current.info(...args),\n warn: (...args) => current.warn(...args),\n error: (...args) => current.error(...args),\n};\n","/**\n * Outbound HTTP helpers shared by Apple/Google providers.\n *\n * Why timeouts: Node's global fetch has no default timeout. If the upstream\n * (Apple App Store Server API, Google Play Developer API, Google OAuth) hangs,\n * webhook handlers and validate routes hang with it — Apple/Google retry on\n * their side, so a hung outbound call cascades into request pile-up here.\n */\n\n/** Default timeout for outbound API calls (10s). */\nexport const DEFAULT_FETCH_TIMEOUT_MS = 10_000;\n\n/**\n * fetch with an `AbortController`-based timeout. Throws an `AbortError`-like\n * Error when the timer fires. Caller is expected to catch and decide\n * (return null, log, propagate as 5xx, etc.).\n */\nexport async function fetchWithTimeout(\n input: RequestInfo | URL,\n init?: RequestInit,\n timeoutMs = DEFAULT_FETCH_TIMEOUT_MS,\n): Promise<Response> {\n const controller = new AbortController();\n // Allow caller-provided signal to compose with our timeout.\n const callerSignal = init?.signal;\n if (callerSignal) {\n if (callerSignal.aborted) controller.abort(callerSignal.reason);\n else callerSignal.addEventListener('abort', () => controller.abort(callerSignal.reason), { once: true });\n }\n\n const timer = setTimeout(() => {\n controller.abort(new Error(`[onesub] fetch timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n try {\n return await fetch(input, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * Pluggable cache adapter.\n *\n * Used internally by the Apple JWT minter, Google OAuth token minter, and any\n * future per-process cache. The default `InMemoryCacheAdapter` matches the\n * pre-adapter behavior — module-level state, lost on restart, not shared\n * between cluster nodes.\n *\n * For multi-instance deployments, pass a `RedisCacheAdapter` in\n * `OneSubMiddlewareConfig.cache` so all nodes share the same JWKS / token\n * cache and avoid every node re-minting on its own schedule.\n */\nexport interface CacheAdapter {\n get<T = unknown>(key: string): Promise<T | null>;\n /** TTL in seconds. Pass 0 / undefined for no expiry. */\n set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void>;\n del(key: string): Promise<void>;\n}\n\ninterface Entry {\n value: unknown;\n /** ms-since-epoch expiry; 0 = never expires */\n expiresAt: number;\n}\n\n/** Sweep expired entries once every this many set() calls. */\nconst SWEEP_EVERY_N_SETS = 256;\n\n/**\n * Process-local in-memory cache. Default for single-instance deployments.\n */\nexport class InMemoryCacheAdapter implements CacheAdapter {\n private readonly entries = new Map<string, Entry>();\n private setsSinceSweep = 0;\n\n async get<T>(key: string): Promise<T | null> {\n const entry = this.entries.get(key);\n if (!entry) return null;\n if (entry.expiresAt !== 0 && entry.expiresAt < Date.now()) {\n this.entries.delete(key);\n return null;\n }\n return entry.value as T;\n }\n\n async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {\n const expiresAt = ttlSeconds && ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : 0;\n this.entries.set(key, { value, expiresAt });\n // Lazy sweep: get() only evicts the key it touches, so write-once-never-\n // read keys (e.g. webhook event dedup markers) would otherwise accumulate\n // forever. Amortized O(1) per set — the O(n) scan runs once per\n // SWEEP_EVERY_N_SETS writes.\n if (++this.setsSinceSweep >= SWEEP_EVERY_N_SETS) {\n this.sweepExpired();\n }\n }\n\n async del(key: string): Promise<void> {\n this.entries.delete(key);\n }\n\n /** Evict every entry whose TTL has elapsed. */\n private sweepExpired(): void {\n this.setsSinceSweep = 0;\n const now = Date.now();\n for (const [key, entry] of this.entries) {\n if (entry.expiresAt !== 0 && entry.expiresAt < now) this.entries.delete(key);\n }\n }\n\n /** Test helper — clear everything. Not part of the public CacheAdapter contract. */\n clear(): void {\n this.entries.clear();\n this.setsSinceSweep = 0;\n }\n\n /** Test helper — number of entries currently held (including not-yet-swept expired ones). */\n get size(): number {\n return this.entries.size;\n }\n}\n\n/**\n * Module-level singleton used by providers when no per-call adapter is\n * supplied. Keeps the existing single-instance behavior with zero config.\n */\nlet defaultAdapter: CacheAdapter = new InMemoryCacheAdapter();\n\nexport function getDefaultCache(): CacheAdapter {\n return defaultAdapter;\n}\n\nexport function setDefaultCache(adapter: CacheAdapter): void {\n defaultAdapter = adapter;\n}\n\n","import { createHash } from 'node:crypto';\nimport type { SubscriptionInfo } from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS, MOCK_RECEIPT_PREFIX } from '@onesub/shared';\nimport { log } from '../logger.js';\n\n/**\n * Mock provider — returns deterministic receipt-validation results based on\n * the receipt string pattern. Used when `config.{apple,google}.mockMode` is\n * true so developers / CI / AI agents can exercise the full onesub flow\n * without App Store Connect or Play Developer API credentials.\n *\n * ──────────────────────────────────────────────────────────────────────\n * Receipt prefix (see `MOCK_RECEIPT_PREFIX` in @onesub/shared)\n * ──────────────────────────────────────────────────────────────────────\n * REVOKED → null (revoked/refunded)\n * EXPIRED → null (> 72h old)\n * INVALID / BAD_SIG → null (signature / integrity fail)\n * NETWORK_ERROR → throws (simulates upstream failure)\n * SANDBOX → valid; subscription has a shorter expiry (~1h)\n * <anything else> → valid; transactionId = sha256(receipt)[:24]\n *\n * transactionId is derived from sha256(receipt) so the same receipt always\n * produces the same id — useful for replay / idempotency testing.\n */\n\nexport type MockReceiptOutcome =\n | { kind: 'valid' }\n | { kind: 'sandbox' }\n | { kind: 'revoked' }\n | { kind: 'expired' }\n | { kind: 'invalid' }\n | { kind: 'network-error' };\n\nexport function classifyMockReceipt(receipt: string): MockReceiptOutcome {\n if (receipt.startsWith(MOCK_RECEIPT_PREFIX.NETWORK_ERROR)) return { kind: 'network-error' };\n if (receipt.startsWith(MOCK_RECEIPT_PREFIX.REVOKED)) return { kind: 'revoked' };\n if (receipt.startsWith(MOCK_RECEIPT_PREFIX.EXPIRED)) return { kind: 'expired' };\n if (\n receipt.startsWith(MOCK_RECEIPT_PREFIX.INVALID) ||\n receipt.startsWith(MOCK_RECEIPT_PREFIX.BAD_SIG)\n ) {\n return { kind: 'invalid' };\n }\n if (receipt.startsWith(MOCK_RECEIPT_PREFIX.SANDBOX)) return { kind: 'sandbox' };\n return { kind: 'valid' };\n}\n\n/**\n * Gate the outcome against `valid` / `sandbox`. Throws for network-error,\n * logs + returns false for rejection cases, returns true for pass-through.\n * Every mock validator uses this so their rejection behavior stays in sync.\n */\nfunction outcomePasses(outcome: MockReceiptOutcome, tag: string): boolean {\n if (outcome.kind === 'network-error') {\n throw new Error(`[onesub/mock/${tag}] simulated upstream network error`);\n }\n if (outcome.kind === 'valid' || outcome.kind === 'sandbox') return true;\n log.warn(`[onesub/mock/${tag}] receipt rejected`, { outcome: outcome.kind });\n return false;\n}\n\nfunction deterministicTransactionId(prefix: string, receipt: string): string {\n const digest = createHash('sha256').update(receipt).digest('hex').slice(0, 24);\n return `${prefix}_${digest}`;\n}\n\n/**\n * Test convention: `...#token=<value>` in a mock receipt surfaces the store's\n * account-binding field (Apple appAccountToken / boundAccountId, Google\n * obfuscatedExternalAccountId), exercising the account-binding guard in the\n * validate routes. Returns undefined when no token is baked into the receipt.\n */\nfunction extractBoundToken(receipt: string): string | undefined {\n return receipt.match(/#token=([^#]+)/)?.[1];\n}\n\nconst HOURS = 60 * 60 * 1000;\nconst DAYS = 24 * HOURS;\n\nexport function mockValidateAppleSubscription(receipt: string): SubscriptionInfo | null {\n const outcome = classifyMockReceipt(receipt);\n if (!outcomePasses(outcome, 'apple')) return null;\n const now = Date.now();\n const expiresAt = outcome.kind === 'sandbox' ? now + 1 * HOURS : now + 30 * DAYS;\n const boundToken = extractBoundToken(receipt);\n return {\n userId: '',\n productId: 'mock_subscription',\n platform: 'apple',\n status: SUBSCRIPTION_STATUS.ACTIVE,\n expiresAt: new Date(expiresAt).toISOString(),\n originalTransactionId: deterministicTransactionId('mock_apple_orig', receipt),\n purchasedAt: new Date(now).toISOString(),\n willRenew: true,\n ...(boundToken ? { boundAccountId: boundToken } : {}),\n };\n}\n\nexport function mockValidateAppleProduct(\n receipt: string,\n expectedProductId?: string,\n): { transactionId: string; productId: string; purchasedAt: string; appAccountToken?: string } | null {\n const outcome = classifyMockReceipt(receipt);\n if (!outcomePasses(outcome, 'apple')) return null;\n const productId = expectedProductId ?? 'mock_product';\n const boundToken = extractBoundToken(receipt);\n return {\n transactionId: deterministicTransactionId(`mock_apple_${productId}`, receipt),\n productId,\n purchasedAt: new Date().toISOString(),\n ...(boundToken ? { appAccountToken: boundToken } : {}),\n };\n}\n\nexport function mockValidateGoogleSubscription(\n receipt: string,\n productId: string,\n): SubscriptionInfo | null {\n const outcome = classifyMockReceipt(receipt);\n if (!outcomePasses(outcome, 'google')) return null;\n const now = Date.now();\n const expiresAt = outcome.kind === 'sandbox' ? now + 1 * HOURS : now + 30 * DAYS;\n const boundToken = extractBoundToken(receipt);\n return {\n userId: '',\n productId,\n platform: 'google',\n status: SUBSCRIPTION_STATUS.ACTIVE,\n expiresAt: new Date(expiresAt).toISOString(),\n // Same keying as production: Google records are keyed by the purchaseToken\n // (the receipt itself) so mock webhooks can find them by token.\n originalTransactionId: receipt,\n purchasedAt: new Date(now).toISOString(),\n willRenew: true,\n ...(boundToken ? { boundAccountId: boundToken } : {}),\n };\n}\n\nexport function mockValidateGoogleProduct(\n receipt: string,\n productId: string,\n): { transactionId: string; purchasedAt: string; obfuscatedExternalAccountId?: string } | null {\n const outcome = classifyMockReceipt(receipt);\n if (!outcomePasses(outcome, 'google')) return null;\n const boundToken = extractBoundToken(receipt);\n return {\n transactionId: deterministicTransactionId(`mock_google_${productId}`, receipt),\n purchasedAt: new Date().toISOString(),\n ...(boundToken ? { obfuscatedExternalAccountId: boundToken } : {}),\n };\n}\n","import { decodeJwt, decodeProtectedHeader, importPKCS8, importX509, jwtVerify, SignJWT } from 'jose';\nimport { X509Certificate, createSign, randomUUID } from 'node:crypto';\nimport type {\n SubscriptionInfo,\n AppleNotificationPayload,\n AppleConsumptionRequest,\n OneSubServerConfig,\n} from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS } from '@onesub/shared';\nimport { APPLE_ROOT_CA_PEMS } from './apple-root-ca.js';\nimport { log } from '../logger.js';\nimport { fetchWithTimeout } from '../http.js';\nimport { getDefaultCache } from '../cache.js';\nimport {\n mockValidateAppleSubscription,\n mockValidateAppleProduct,\n} from './mock.js';\n\ntype AppleConfig = NonNullable<OneSubServerConfig['apple']>;\n\n/**\n * Decoded Apple signed transaction (JWS payload).\n * Mirrors the fields from App Store Server API / StoreKit 2.\n */\ninterface AppleTransactionPayload {\n bundleId?: string;\n productId?: string;\n originalTransactionId?: string;\n transactionId?: string;\n purchaseDate?: number; // ms since epoch\n originalPurchaseDate?: number;\n expiresDate?: number; // ms since epoch\n inAppOwnershipType?: string; // 'PURCHASED' | 'FAMILY_SHARED'\n appAccountToken?: string; // UUID linked at purchase time (= userId for onesub)\n type?: string; // 'Auto-Renewable Subscription' | 'Consumable' | 'Non-Consumable'\n isUpgraded?: boolean;\n revocationDate?: number;\n [key: string]: unknown;\n}\n\nconst EMPTY_APP_ACCOUNT_TOKEN = '00000000-0000-0000-0000-000000000000';\n\n/**\n * Unity IAP can send Guid.Empty when no Apple account token was configured.\n * Apple includes that sentinel in the signed transaction, so normalize only\n * that exact UUID to an absent token before any account-binding decision.\n */\nfunction normalizeAppleAppAccountToken(token: string | undefined): string | undefined {\n return token === EMPTY_APP_ACCOUNT_TOKEN ? undefined : token;\n}\n\n/**\n * Decoded Apple signed renewal info (JWS payload).\n */\ninterface AppleRenewalPayload {\n autoRenewStatus?: number; // 1 = will renew, 0 = canceled\n expirationIntent?: number; // 1=Customer canceled, 2=Billing error, etc.\n originalTransactionId?: string;\n productId?: string;\n [key: string]: unknown;\n}\n\nfunction derBase64ToPem(der: string): string {\n return (\n '-----BEGIN CERTIFICATE-----\\n' +\n (der.match(/.{1,64}/g) ?? []).join('\\n') +\n '\\n-----END CERTIFICATE-----'\n );\n}\n\nconst APPLE_ROOT_CERTS = APPLE_ROOT_CA_PEMS.map((pem) => new X509Certificate(pem));\n\n/**\n * BasicConstraints enforcement for certificates acting as ISSUERS in an x5c\n * chain. Without it, any end-entity (leaf) certificate — which Apple issues\n * WITHOUT CA privileges — could be abused to sign a forged \"leaf\" and splice\n * it into an otherwise signature-valid chain.\n *\n * We enforce `basicConstraints CA=true` via node's `X509Certificate.ca`.\n * The KeyUsage keyCertSign bit is NOT checked because node:crypto does not\n * expose the KeyUsage bit string: `X509Certificate.keyUsage` actually returns\n * the Extended Key Usage OIDs (it reads NID_ext_key_usage), so `.ca` is the\n * only reliably available signal in Node 20/22. `.ca` alone is sufficient to\n * block leaf-as-issuer splicing.\n *\n * The LEAF certificate (index 0) must NOT be passed here — leaves are not\n * CAs and are never issuers.\n *\n * Exported for unit testing.\n */\nexport function assertIssuerCanSign(cert: Pick<X509Certificate, 'ca'>, index: number): void {\n if (!cert.ca) {\n throw new Error(\n `[onesub/apple] cert[${index}] is used as an issuer but is not a CA (basicConstraints CA=false)`,\n );\n }\n}\n\n/**\n * Validate that the x5c chain from the JWS terminates at one of Apple's\n * bundled root CAs. Each cert in the chain must be signed by the next (or\n * the bundled root), every issuing cert must be a CA (BasicConstraints), and\n * all certs must currently be within their validity window.\n *\n * Returns the leaf certificate PEM on success. Throws on any failure.\n */\nfunction verifyAppleCertChain(x5c: string[]): string {\n if (x5c.length === 0) {\n throw new Error('[onesub/apple] empty x5c');\n }\n\n const chain = x5c.map((der) => new X509Certificate(derBase64ToPem(der)));\n const now = new Date();\n\n // Validity window + leaf→intermediate→... signature chain\n for (let i = 0; i < chain.length; i++) {\n const cert = chain[i];\n if (new Date(cert.validFrom) > now || new Date(cert.validTo) < now) {\n throw new Error(`[onesub/apple] cert[${i}] outside validity window`);\n }\n // Every cert above the leaf signs the cert below it → must be a CA.\n if (i >= 1) assertIssuerCanSign(cert, i);\n if (i + 1 < chain.length) {\n if (!cert.checkIssued(chain[i + 1]) || !cert.verify(chain[i + 1].publicKey)) {\n throw new Error(`[onesub/apple] cert[${i}] not signed by cert[${i + 1}]`);\n }\n }\n }\n\n // Final cert in chain (typically intermediate) must be signed by an Apple root.\n // Accept either (a) explicit signature match or (b) the cert itself being one\n // of our bundled Apple roots (happens when Apple embeds the full chain).\n const top = chain[chain.length - 1];\n const topDer = top.raw.toString('base64');\n const trustsRoot = APPLE_ROOT_CERTS.some((root) => {\n if (root.raw.toString('base64') === topDer) return true;\n // A bundled root acting as the issuer of `top` must itself be a CA\n // (always true for Apple's roots; kept as defense in depth).\n if (!root.ca) return false;\n if (!top.checkIssued(root)) return false;\n try { return top.verify(root.publicKey); } catch { return false; }\n });\n if (!trustsRoot) {\n throw new Error('[onesub/apple] cert chain does not terminate at a trusted Apple root');\n }\n\n return chain[0].toString();\n}\n\n/**\n * Decode and verify a StoreKit 2 signed transaction JWS.\n *\n * 1. Extract the x5c certificate chain from the JWS header.\n * 2. Validate the chain terminates at a bundled Apple Root CA (G3).\n * 3. Use the leaf certificate's public key to verify the JWS signature.\n *\n * skipVerification=true skips all of the above and just decodes the payload\n * (dev/test only).\n */\nexport async function decodeJws<T>(jws: string, skipVerification = false): Promise<T> {\n if (skipVerification) {\n if (process.env['NODE_ENV'] === 'production') {\n log.warn(\n '[onesub/apple] WARNING: skipJwsVerification is enabled in production. ' +\n 'JWS signatures are NOT being verified. This is a security risk.',\n );\n }\n return decodeJwt(jws) as T;\n }\n\n const header = decodeProtectedHeader(jws) as { x5c?: string[]; alg?: string };\n const x5c = header.x5c;\n if (!x5c || x5c.length === 0) {\n throw new Error('[onesub/apple] JWS header missing x5c certificate chain');\n }\n\n const leafPem = verifyAppleCertChain(x5c);\n const alg = header.alg ?? 'ES256';\n const key = await importX509(leafPem, alg);\n\n const { payload } = await jwtVerify(jws, key);\n return payload as T;\n}\n\n/**\n * Derive a SubscriptionStatus from the transaction + renewal payloads.\n */\nfunction deriveStatus(\n tx: AppleTransactionPayload,\n renewal: AppleRenewalPayload | null\n): SubscriptionInfo['status'] {\n if (tx.revocationDate) return SUBSCRIPTION_STATUS.CANCELED;\n\n const now = Date.now();\n const expires = tx.expiresDate ?? 0;\n\n if (expires > now) return SUBSCRIPTION_STATUS.ACTIVE;\n\n // Expired — check if it was voluntarily canceled\n if (renewal?.autoRenewStatus === 0) return SUBSCRIPTION_STATUS.CANCELED;\n\n return SUBSCRIPTION_STATUS.EXPIRED;\n}\n\n/**\n * Validate an Apple receipt string.\n *\n * StoreKit 2 receipts are JWS-encoded signed transactions (the `signedTransaction`\n * field from the client). For legacy base64 receipts from StoreKit 1, you would\n * POST to verifyReceipt — that path is left as a stub here because Apple is\n * deprecating it.\n */\nexport async function validateAppleReceipt(\n receipt: string,\n config: AppleConfig\n): Promise<SubscriptionInfo | null> {\n if (config.mockMode) {\n const mock = mockValidateAppleSubscription(receipt);\n if (mock) {\n const boundAccountId = normalizeAppleAppAccountToken(mock.boundAccountId);\n if (boundAccountId) mock.boundAccountId = boundAccountId;\n else delete mock.boundAccountId;\n }\n return mock;\n }\n let tx: AppleTransactionPayload;\n\n try {\n tx = await decodeJws<AppleTransactionPayload>(receipt, config.skipJwsVerification);\n } catch (err) {\n const preview = receipt.slice(0, 60);\n const parts = receipt.split('.').length;\n log.warn(\n '[onesub/apple] Failed to decode receipt as JWS:',\n (err as Error)?.message ?? err,\n `| preview: \"${preview}...\" (len=${receipt.length}, parts=${parts})`,\n );\n return null;\n }\n\n if (!tx.originalTransactionId || !tx.productId || !tx.expiresDate) {\n return null;\n }\n\n // Validate bundle ID — missing or mismatched both rejected\n if (!tx.bundleId || tx.bundleId !== config.bundleId) {\n log.warn('[onesub/apple] Bundle ID mismatch:', tx.bundleId, '!==', config.bundleId);\n return null;\n }\n\n // Reject Sandbox receipts in production unless explicitly allowed.\n // Set ONESUB_ALLOW_SANDBOX=true to permit TestFlight sandbox receipts on\n // production servers (useful during QA before App Store release).\n if (\n process.env['NODE_ENV'] === 'production' &&\n tx.environment !== 'Production' &&\n process.env['ONESUB_ALLOW_SANDBOX'] !== 'true'\n ) {\n log.warn('[onesub/apple] Sandbox receipt rejected in production:', tx.environment);\n return null;\n }\n\n const status = deriveStatus(tx, null);\n const purchasedAt = tx.originalPurchaseDate ?? tx.purchaseDate ?? Date.now();\n const appAccountToken = normalizeAppleAppAccountToken(tx.appAccountToken);\n\n return {\n userId: '', // caller fills this in from the request body\n productId: tx.productId,\n platform: 'apple',\n status,\n expiresAt: new Date(tx.expiresDate).toISOString(),\n originalTransactionId: tx.originalTransactionId,\n purchasedAt: new Date(purchasedAt).toISOString(),\n willRenew: status === SUBSCRIPTION_STATUS.ACTIVE, // refined by renewal info in webhook\n ...(appAccountToken ? { boundAccountId: appAccountToken } : {}),\n };\n}\n\n/**\n * Result of validating an Apple consumable or non-consumable receipt.\n */\nexport interface AppleProductResult {\n /** Per-transaction unique ID (transactionId, not originalTransactionId) */\n transactionId: string;\n productId: string;\n purchasedAt: string;\n /**\n * appAccountToken baked into the JWS at purchase time (UUID the client passed\n * to StoreKit). When present it binds the purchase to a stable account\n * identity; the validate route rejects attributing/reassigning the purchase to\n * a userId that doesn't match it. Absent for purchases made before the client\n * started setting appAccountToken (backward-compatible).\n */\n appAccountToken?: string;\n}\n\n/**\n * Validate an Apple StoreKit 2 JWS signedTransaction for a consumable or\n * non-consumable product.\n *\n * Differences from validateAppleReceipt (subscriptions):\n * - Checks tx.type is 'Consumable' or 'Non-Consumable' (not a subscription)\n * - Uses tx.transactionId (per-purchase unique) instead of originalTransactionId\n * - Enforces a 72-hour receipt age limit to block replay attacks\n * - Does NOT check expiresDate (one-time purchases don't expire)\n */\nexport async function validateAppleConsumableReceipt(\n signedTransaction: string,\n config: AppleConfig,\n expectedProductId?: string,\n): Promise<AppleProductResult | null> {\n if (config.mockMode) {\n const mock = mockValidateAppleProduct(signedTransaction, expectedProductId);\n if (mock) {\n const appAccountToken = normalizeAppleAppAccountToken(mock.appAccountToken);\n if (appAccountToken) mock.appAccountToken = appAccountToken;\n else delete mock.appAccountToken;\n }\n return mock;\n }\n let tx: AppleTransactionPayload;\n\n try {\n tx = await decodeJws<AppleTransactionPayload>(signedTransaction, config.skipJwsVerification);\n } catch (err) {\n const preview = signedTransaction.slice(0, 60);\n const parts = signedTransaction.split('.').length;\n const looksLikeJws = parts === 3;\n log.warn(\n '[onesub/apple] Failed to decode consumable JWS:',\n (err as Error)?.message ?? err,\n `| receipt preview: \"${preview}...\" (len=${signedTransaction.length}, parts=${parts}, looksLikeJws=${looksLikeJws})`,\n );\n return null;\n }\n\n // bundleId must be present and match\n if (!tx.bundleId || tx.bundleId !== config.bundleId) {\n log.warn('[onesub/apple] Bundle ID mismatch:', tx.bundleId, '!==', config.bundleId);\n return null;\n }\n\n // Must be a one-time purchase type (not a subscription)\n if (tx.type !== 'Consumable' && tx.type !== 'Non-Consumable') {\n log.warn('[onesub/apple] Invalid purchase type for product validation:', tx.type);\n return null;\n }\n\n // Reject Sandbox receipts in production unless explicitly allowed.\n // Set ONESUB_ALLOW_SANDBOX=true to permit TestFlight sandbox receipts on\n // production servers (useful during QA before App Store release).\n if (\n process.env['NODE_ENV'] === 'production' &&\n tx.environment !== 'Production' &&\n process.env['ONESUB_ALLOW_SANDBOX'] !== 'true'\n ) {\n log.warn('[onesub/apple] Sandbox receipt rejected in production:', tx.environment);\n return null;\n }\n\n if (!tx.productId) {\n log.warn('[onesub/apple] No productId in transaction');\n return null;\n }\n\n if (expectedProductId && tx.productId !== expectedProductId) {\n log.warn('[onesub/apple] Product ID mismatch:', tx.productId, '!==', expectedProductId);\n return null;\n }\n\n // Reject refunded purchases\n if (tx.revocationDate) {\n log.warn('[onesub/apple] Purchase was revoked/refunded');\n return null;\n }\n\n // Reject receipts older than the configured window (replay attack\n // prevention). Raise productReceiptMaxAgeHours when validating historical\n // receipts on purpose (migrations from another IAP backend, e2e tests).\n const maxAgeMs = (config.productReceiptMaxAgeHours ?? 72) * 60 * 60 * 1000;\n if (tx.purchaseDate && Date.now() - tx.purchaseDate > maxAgeMs) {\n log.warn(`[onesub/apple] Consumable receipt too old (>${config.productReceiptMaxAgeHours ?? 72}h)`);\n return null;\n }\n\n // For consumables, transactionId is per-purchase unique.\n // originalTransactionId is shared across re-purchases, so it must not be used\n // as the deduplication key for consumables.\n const transactionId = tx.transactionId ?? tx.originalTransactionId;\n if (!transactionId) {\n log.warn('[onesub/apple] No transactionId in consumable transaction');\n return null;\n }\n\n return {\n transactionId,\n productId: tx.productId,\n purchasedAt: tx.purchaseDate\n ? new Date(tx.purchaseDate).toISOString()\n : new Date().toISOString(),\n appAccountToken: normalizeAppleAppAccountToken(tx.appAccountToken),\n };\n}\n\n/**\n * Decode an Apple Server Notification V2 payload.\n * Returns the derived subscription update to be merged into the store.\n */\nexport async function decodeAppleNotification(\n payload: AppleNotificationPayload,\n skipJwsVerification = false\n): Promise<{\n originalTransactionId: string;\n /** Per-transaction id (different from originalTransactionId for consumables / re-purchases). */\n transactionId: string | null;\n /** 'Auto-Renewable Subscription' | 'Consumable' | 'Non-Consumable' | 'Non-Renewing Subscription' */\n type: string | null;\n productId: string | null;\n bundleId: string | null;\n /** 'Production' | 'Sandbox' — drives which Apple API host to call back. */\n environment: 'Production' | 'Sandbox';\n status: SubscriptionInfo['status'];\n willRenew: boolean;\n /** May be null for non-subscription notifications (consumable refund). */\n expiresAt: string | null;\n /**\n * UUID the app set via setAppAccountToken at purchase time. For onesub this\n * maps directly to userId. Present for both PURCHASED and FAMILY_SHARED\n * ownership types when the host linked a user account at checkout time.\n * Null when not set (sandbox, or host didn't call setAppAccountToken).\n */\n appAccountToken: string | null;\n /**\n * 'PURCHASED' — the user bought this directly.\n * 'FAMILY_SHARED' — a family member of the purchaser. The originalTransactionId\n * and appAccountToken belong to the family member, not the purchaser.\n */\n inAppOwnershipType: string | null;\n} | null> {\n const { signedTransactionInfo, signedRenewalInfo } = payload.data;\n\n let tx: AppleTransactionPayload;\n let renewal: AppleRenewalPayload | null = null;\n\n try {\n tx = await decodeJws<AppleTransactionPayload>(signedTransactionInfo, skipJwsVerification);\n } catch {\n return null;\n }\n\n try {\n renewal = await decodeJws<AppleRenewalPayload>(signedRenewalInfo, skipJwsVerification);\n } catch {\n // renewal info is optional for some notification types\n }\n\n // expiresDate is only required for subscription notifications.\n // Consumable / non-consumable refund notifications (REFUND for IAP) have no expiry.\n if (!tx.originalTransactionId) return null;\n\n const status = deriveStatus(tx, renewal);\n const willRenew = renewal?.autoRenewStatus === 1;\n\n // environment is on the notification data envelope but Apple also includes\n // it inside the signed transaction payload. Prefer the JWS-protected one.\n const txEnv = (tx as { environment?: string }).environment;\n const dataEnv = payload.data.environment;\n const environment: 'Production' | 'Sandbox' =\n txEnv === 'Sandbox' || dataEnv === 'Sandbox' ? 'Sandbox' : 'Production';\n\n return {\n originalTransactionId: tx.originalTransactionId,\n transactionId: tx.transactionId ?? null,\n type: tx.type ?? null,\n productId: tx.productId ?? null,\n bundleId: tx.bundleId ?? payload.data.bundleId ?? null,\n environment,\n status,\n willRenew,\n expiresAt: tx.expiresDate ? new Date(tx.expiresDate).toISOString() : null,\n appAccountToken: normalizeAppleAppAccountToken(tx.appAccountToken) ?? null,\n inAppOwnershipType: tx.inAppOwnershipType ?? null,\n };\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// App Store Server API — outbound calls (consumption response, etc.)\n// ───────────────────────────────────────────────────────────────────────────\n\n/**\n * JWT cache for the App Store Server API. Apple caps token TTL at 20 minutes;\n * we mint with that TTL and re-use until 60 seconds before expiry (so\n * long-running webhook bursts don't pay the ECDSA-sign cost on every request).\n *\n * Cache backend: `getDefaultCache()` (in-memory by default, swappable with\n * Redis / Memcached via `setDefaultCache()`). When the cache is shared across\n * cluster nodes only one node mints per TTL window.\n *\n * Keyed by `${issuerId}|${keyId}` since rotating either invalidates the JWT.\n * Concurrent mints inside a single process are deduped via `appleJwtMintPromises`.\n */\nconst appleJwtMintPromises = new Map<string, Promise<string>>();\n\nconst APPLE_JWT_TTL_MS = 20 * 60 * 1000;\nconst APPLE_JWT_REFRESH_BEFORE_MS = 60 * 1000;\n\n/**\n * Mint a JWT for the App Store Server API (audience `appstoreconnect-v1`).\n * Requires keyId, issuerId, and privateKey (PKCS8 ES256 from App Store Connect).\n * Token TTL is capped at 20 minutes per Apple's spec.\n */\nasync function makeAppleApiJwt(config: AppleConfig): Promise<string> {\n const { issuerId, keyId, privateKey, bundleId } = config;\n if (!issuerId || !keyId || !privateKey) {\n throw new Error('[onesub/apple] App Store Server API requires issuerId, keyId, and privateKey');\n }\n\n const cache = getDefaultCache();\n const cacheKey = `apple:jwt:${issuerId}|${keyId}`;\n const now = Date.now();\n\n const cached = await cache.get<{ token: string; expiresAt: number }>(cacheKey);\n if (cached && cached.expiresAt - now > APPLE_JWT_REFRESH_BEFORE_MS) {\n return cached.token;\n }\n\n const inflight = appleJwtMintPromises.get(cacheKey);\n if (inflight) return inflight;\n\n const promise = (async () => {\n const key = await importPKCS8(privateKey, 'ES256');\n const issuedAt = Math.floor(Date.now() / 1000);\n const token = await new SignJWT({ bid: bundleId })\n .setProtectedHeader({ alg: 'ES256', kid: keyId, typ: 'JWT' })\n .setIssuer(issuerId)\n .setAudience('appstoreconnect-v1')\n .setIssuedAt(issuedAt)\n .setExpirationTime(issuedAt + Math.floor(APPLE_JWT_TTL_MS / 1000))\n .sign(key);\n const expiresAt = Date.now() + APPLE_JWT_TTL_MS;\n // TTL slightly under the 20m spec so cache eviction matches token expiry.\n await cache.set(cacheKey, { token, expiresAt }, 19 * 60);\n return token;\n })().finally(() => {\n appleJwtMintPromises.delete(cacheKey);\n });\n appleJwtMintPromises.set(cacheKey, promise);\n return promise;\n}\n\n/** Test-only: clear the Apple JWT cache. Not exported. */\nfunction clearAppleJwtCacheForTests(): void {\n const cache = getDefaultCache();\n // Best-effort wipe — tests use the in-memory adapter, which exposes clear().\n if ('clear' in cache && typeof (cache as { clear?: () => void }).clear === 'function') {\n (cache as { clear: () => void }).clear();\n }\n appleJwtMintPromises.clear();\n}\n// Expose for the test suite without polluting the public API surface.\nexport const __testing = { clearAppleJwtCacheForTests };\n\n/**\n * PUT a ConsumptionRequest to Apple's\n * /inApps/v1/transactions/consumption/{transactionId} endpoint.\n *\n * Apple sends CONSUMPTION_REQUEST notifications when a customer asks for a\n * refund on a consumable. Without a response Apple has no usage signal and\n * tends to grant the refund. This call provides the data Apple uses to weigh\n * the refund decision.\n *\n * Fire-and-forget: failures are logged, never thrown — the webhook should\n * still 200 to Apple even if our outbound call fails.\n */\nexport async function sendAppleConsumptionResponse(\n transactionId: string,\n body: AppleConsumptionRequest,\n config: AppleConfig,\n options?: { sandbox?: boolean },\n): Promise<void> {\n if (config.mockMode) return;\n\n let jwt: string;\n try {\n jwt = await makeAppleApiJwt(config);\n } catch (err) {\n log.warn('[onesub/apple] Cannot send consumption response — JWT mint failed:', err);\n return;\n }\n\n const host = options?.sandbox\n ? 'api.storekit-sandbox.itunes.apple.com'\n : 'api.storekit.itunes.apple.com';\n const url = `https://${host}/inApps/v1/transactions/consumption/${encodeURIComponent(transactionId)}`;\n\n try {\n const resp = await fetchWithTimeout(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${jwt}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n if (!resp.ok) {\n const text = await resp.text();\n log.warn(`[onesub/apple] Consumption response API error ${resp.status}: ${text}`);\n }\n } catch (err) {\n log.warn('[onesub/apple] Consumption response network error:', err);\n }\n}\n\n/**\n * Apple's status code in the GET /inApps/v1/subscriptions/{originalTxId} response.\n * https://developer.apple.com/documentation/appstoreserverapi/status\n */\nconst APPLE_SUBSCRIPTION_STATUS_CODE = {\n ACTIVE: 1,\n EXPIRED: 2,\n BILLING_RETRY: 3,\n GRACE_PERIOD: 4,\n REVOKED: 5,\n} as const;\n\nfunction mapAppleStatusCode(code: number): SubscriptionInfo['status'] {\n switch (code) {\n case APPLE_SUBSCRIPTION_STATUS_CODE.ACTIVE:\n return SUBSCRIPTION_STATUS.ACTIVE;\n case APPLE_SUBSCRIPTION_STATUS_CODE.GRACE_PERIOD:\n return SUBSCRIPTION_STATUS.GRACE_PERIOD;\n case APPLE_SUBSCRIPTION_STATUS_CODE.BILLING_RETRY:\n return SUBSCRIPTION_STATUS.ON_HOLD;\n case APPLE_SUBSCRIPTION_STATUS_CODE.REVOKED:\n return SUBSCRIPTION_STATUS.CANCELED;\n case APPLE_SUBSCRIPTION_STATUS_CODE.EXPIRED:\n default:\n return SUBSCRIPTION_STATUS.EXPIRED;\n }\n}\n\n/**\n * Shape of the GET /inApps/v1/subscriptions/{originalTransactionId} response.\n * Only the fields we read.\n */\ninterface AppleSubscriptionStatusResponse {\n data?: Array<{\n subscriptionGroupIdentifier?: string;\n lastTransactions?: Array<{\n originalTransactionId?: string;\n status?: number;\n signedTransactionInfo?: string;\n signedRenewalInfo?: string;\n }>;\n }>;\n bundleId?: string;\n environment?: 'Production' | 'Sandbox';\n}\n\n/**\n * Fetch the current state of a subscription directly from Apple's App Store\n * Server API — the canonical source of truth when webhooks have been missed,\n * delivered out of order, or the local store has no record at all.\n *\n * GET /inApps/v1/subscriptions/{originalTransactionId}\n * https://developer.apple.com/documentation/appstoreserverapi/get_all_subscription_statuses\n *\n * Returns null on:\n * - Missing API credentials (issuerId/keyId/privateKey)\n * - Network or auth failure\n * - Empty response (transaction not found)\n *\n * Hosts can call this directly (e.g. from a reconciliation cron) or it runs\n * automatically as the unknown-transaction fallback inside the Apple webhook.\n */\nexport async function fetchAppleSubscriptionStatus(\n originalTransactionId: string,\n config: AppleConfig,\n options?: { sandbox?: boolean },\n): Promise<SubscriptionInfo | null> {\n if (config.mockMode) return null;\n\n let jwt: string;\n try {\n jwt = await makeAppleApiJwt(config);\n } catch (err) {\n log.warn('[onesub/apple] Cannot fetch subscription status — JWT mint failed:', err);\n return null;\n }\n\n const host = options?.sandbox\n ? 'api.storekit-sandbox.itunes.apple.com'\n : 'api.storekit.itunes.apple.com';\n const url = `https://${host}/inApps/v1/subscriptions/${encodeURIComponent(originalTransactionId)}`;\n\n let body: AppleSubscriptionStatusResponse;\n try {\n const resp = await fetchWithTimeout(url, {\n headers: { Authorization: `Bearer ${jwt}` },\n });\n if (!resp.ok) {\n const text = await resp.text();\n log.warn(`[onesub/apple] Status API error ${resp.status}: ${text}`);\n return null;\n }\n body = (await resp.json()) as AppleSubscriptionStatusResponse;\n } catch (err) {\n log.warn('[onesub/apple] Status API network error:', err);\n return null;\n }\n\n // Find the lastTransactions entry matching the requested originalTransactionId.\n // Apple groups by subscriptionGroupIdentifier; one originalTxId is in exactly one group.\n const entry = body.data\n ?.flatMap((g) => g.lastTransactions ?? [])\n .find((t) => t.originalTransactionId === originalTransactionId);\n\n if (!entry || entry.status == null || !entry.signedTransactionInfo) {\n log.warn('[onesub/apple] Status API returned no matching transaction for', originalTransactionId);\n return null;\n }\n\n let tx: AppleTransactionPayload;\n try {\n tx = await decodeJws<AppleTransactionPayload>(entry.signedTransactionInfo, config.skipJwsVerification);\n } catch (err) {\n log.warn('[onesub/apple] Failed to decode signedTransactionInfo from Status API:', err);\n return null;\n }\n\n let renewal: AppleRenewalPayload | null = null;\n if (entry.signedRenewalInfo) {\n try {\n renewal = await decodeJws<AppleRenewalPayload>(entry.signedRenewalInfo, config.skipJwsVerification);\n } catch {\n // renewal info is optional\n }\n }\n\n if (!tx.productId || !tx.expiresDate) {\n log.warn('[onesub/apple] Status API transaction missing productId or expiresDate');\n return null;\n }\n\n const status = mapAppleStatusCode(entry.status);\n const purchasedAt = tx.originalPurchaseDate ?? tx.purchaseDate ?? Date.now();\n\n return {\n userId: '', // caller fills this in\n productId: tx.productId,\n platform: 'apple',\n status,\n expiresAt: new Date(tx.expiresDate).toISOString(),\n originalTransactionId,\n purchasedAt: new Date(purchasedAt).toISOString(),\n willRenew: renewal?.autoRenewStatus === 1,\n };\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// App Store Server API — Transaction History\n// ───────────────────────────────────────────────────────────────────────────\n\ninterface AppleTransactionHistoryResponse {\n signedTransactions?: string[];\n revision?: string;\n hasMore?: boolean;\n bundleId?: string;\n appAppleId?: number;\n environment?: 'Production' | 'Sandbox';\n}\n\n/**\n * Decoded lightweight transaction record from transaction history.\n */\nexport interface AppleTransactionRecord {\n originalTransactionId: string;\n transactionId: string;\n productId: string;\n type: string;\n purchasedAt: string;\n expiresAt: string | null;\n appAccountToken: string | null;\n inAppOwnershipType: string | null;\n revocationDate: string | null;\n}\n\n/**\n * Pagination safety cap for the Transaction History API. Apple returns ~20\n * transactions per page, so 50 pages ≈ 1000 transactions — far beyond any\n * realistic single-subscription history. Guards against a misbehaving\n * endpoint keeping the loop alive forever.\n */\nconst MAX_HISTORY_PAGES = 50;\n\n/**\n * Fetch all transactions for an originalTransactionId from the App Store\n * Transaction History API (GET /inApps/v2/history/{originalTransactionId}).\n *\n * Paginates automatically until `hasMore` is false. Returns all decoded\n * transaction records, oldest-first (Apple's default sort).\n *\n * Returns null when API credentials are missing or the call fails.\n */\nexport async function fetchAppleTransactionHistory(\n originalTransactionId: string,\n config: AppleConfig,\n options?: { sandbox?: boolean },\n): Promise<AppleTransactionRecord[] | null> {\n if (config.mockMode) return null;\n\n let jwt: string;\n try {\n jwt = await makeAppleApiJwt(config);\n } catch (err) {\n log.warn('[onesub/apple] Cannot fetch transaction history — JWT mint failed:', err);\n return null;\n }\n\n const host = options?.sandbox\n ? 'api.storekit-sandbox.itunes.apple.com'\n : 'api.storekit.itunes.apple.com';\n\n const results: AppleTransactionRecord[] = [];\n let revision: string | undefined;\n let pageCount = 0;\n\n for (;;) {\n const url = new URL(`https://${host}/inApps/v2/history/${encodeURIComponent(originalTransactionId)}`);\n if (revision) url.searchParams.set('revision', revision);\n\n let page: AppleTransactionHistoryResponse;\n try {\n const resp = await fetchWithTimeout(url.toString(), {\n headers: { Authorization: `Bearer ${jwt}` },\n });\n if (!resp.ok) {\n const text = await resp.text();\n log.warn(`[onesub/apple] Transaction History API error ${resp.status}: ${text}`);\n return null;\n }\n page = (await resp.json()) as AppleTransactionHistoryResponse;\n } catch (err) {\n log.warn('[onesub/apple] Transaction History API network error:', err);\n return null;\n }\n\n for (const signed of page.signedTransactions ?? []) {\n try {\n const tx = await decodeJws<AppleTransactionPayload>(signed, config.skipJwsVerification);\n if (!tx.originalTransactionId || !tx.transactionId || !tx.productId) continue;\n results.push({\n originalTransactionId: tx.originalTransactionId,\n transactionId: tx.transactionId,\n productId: tx.productId,\n type: tx.type ?? 'Unknown',\n purchasedAt: tx.purchaseDate ? new Date(tx.purchaseDate).toISOString() : new Date().toISOString(),\n expiresAt: tx.expiresDate ? new Date(tx.expiresDate).toISOString() : null,\n appAccountToken: normalizeAppleAppAccountToken(tx.appAccountToken) ?? null,\n inAppOwnershipType: tx.inAppOwnershipType ?? null,\n revocationDate: tx.revocationDate ? new Date(tx.revocationDate).toISOString() : null,\n });\n } catch {\n // skip malformed JWS entries\n }\n }\n\n pageCount++;\n if (!page.hasMore) break;\n // Infinite-loop guards: `hasMore: true` with a missing or unchanged\n // revision cursor would refetch the same page forever.\n if (!page.revision || page.revision === revision) {\n log.warn(\n '[onesub/apple] Transaction History API returned hasMore without a new revision cursor — stopping pagination (partial history returned)',\n );\n break;\n }\n if (pageCount >= MAX_HISTORY_PAGES) {\n log.warn(\n `[onesub/apple] Transaction History pagination hit the ${MAX_HISTORY_PAGES}-page cap — stopping (partial history returned)`,\n );\n break;\n }\n revision = page.revision;\n }\n\n return results;\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// Apple Promotional Offer — server-side signature\n// ───────────────────────────────────────────────────────────────────────────\n\n/**\n * Input for signing an Apple Promotional Offer payload.\n * https://developer.apple.com/documentation/storekit/generating-a-signature-for-promotional-offers\n */\nexport interface AppleOfferSignatureInput {\n /** App Bundle ID (e.g. com.example.app) */\n bundleId: string;\n /** The product ID of the subscription being offered */\n productId: string;\n /** The promotional offer ID defined in App Store Connect */\n offerId: string;\n /** A unique UUID generated per request (the nonce) */\n applicationUsername: string;\n}\n\n/**\n * Result returned to the client so StoreKit can verify the offer.\n */\nexport interface AppleOfferSignatureResult {\n keyId: string;\n nonce: string;\n timestamp: number;\n signature: string;\n}\n\n/**\n * Sign an Apple Promotional Offer payload with ES256 (ECDSA P-256).\n *\n * The message to sign is:\n * appBundleId + '⁣' + keyIdentifier + '⁣' + productIdentifier + '⁣'\n * + offerIdentifier + '⁣' + applicationUsername + '⁣'\n * + nonce (lowercase) + '⁣' + timestamp (ms, string)\n *\n * Requires `config.offerKeyId` and `config.offerPrivateKey` (PEM ES256 key from\n * App Store Connect → Subscriptions → Promotional Offers → Keys).\n *\n * Throws if the required keys are not configured.\n */\nexport async function signApplePromotionalOffer(\n input: AppleOfferSignatureInput,\n config: AppleConfig,\n): Promise<AppleOfferSignatureResult> {\n const { offerKeyId, offerPrivateKey } = config as AppleConfig & { offerKeyId?: string; offerPrivateKey?: string };\n if (!offerKeyId || !offerPrivateKey) {\n throw new Error('[onesub/apple] Promotional Offer signing requires config.apple.offerKeyId and config.apple.offerPrivateKey');\n }\n\n const nonce = randomUUID().toLowerCase();\n const timestamp = Date.now();\n const separator = '⁣';\n\n const message = [\n input.bundleId,\n offerKeyId,\n input.productId,\n input.offerId,\n input.applicationUsername,\n nonce,\n String(timestamp),\n ].join(separator);\n\n if (!offerPrivateKey.includes('BEGIN') || !offerPrivateKey.includes('END')) {\n throw new Error('[onesub/apple] offerPrivateKey does not appear to be PEM-encoded');\n }\n\n let signatureBuffer: Buffer;\n try {\n const sign = createSign('SHA256');\n sign.update(message);\n sign.end();\n signatureBuffer = sign.sign(offerPrivateKey);\n } catch (err) {\n throw new Error(`[onesub/apple] Failed to sign promotional offer — check that offerPrivateKey is a valid ES256 PEM key: ${(err as Error).message}`);\n }\n const signature = signatureBuffer.toString('base64');\n\n return { keyId: offerKeyId, nonce, timestamp, signature };\n}\n","import type { SubscriptionInfo, GoogleNotificationPayload, OneSubServerConfig } from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS } from '@onesub/shared';\nimport { log } from '../logger.js';\nimport { fetchWithTimeout } from '../http.js';\nimport { getDefaultCache } from '../cache.js';\nimport {\n mockValidateGoogleSubscription,\n mockValidateGoogleProduct,\n} from './mock.js';\n\ntype GoogleConfig = NonNullable<OneSubServerConfig['google']>;\n\n/**\n * Google Play Developer API v3 — SubscriptionPurchaseV2 resource (partial).\n * https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.subscriptionsv2/get\n *\n * V2 differences from the deprecated V1 (purchases.subscriptions):\n * - URL doesn't take productId — one token can map to multiple lineItems\n * - subscriptionState is a string enum (explicit grace/hold/paused) instead\n * of being inferred from expiryTime\n * - lineItems[] carries per-product productId + expiryTime + autoRenewingPlan\n * - linkedPurchaseToken first-class (upgrade/downgrade chain tracking)\n * - latestOrderId replaces orderId as the canonical transaction id\n */\ninterface GoogleSubscriptionPurchaseV2 {\n kind?: string; // 'androidpublisher#subscriptionPurchaseV2'\n startTime?: string; // RFC3339 (ISO 8601)\n regionCode?: string;\n lineItems?: Array<{\n productId?: string;\n expiryTime?: string; // RFC3339\n autoRenewingPlan?: {\n autoRenewEnabled?: boolean;\n priceChangeDetails?: unknown;\n };\n prepaidPlan?: {\n allowExtendAfterTime?: string;\n };\n offerDetails?: unknown;\n }>;\n subscriptionState?:\n | 'SUBSCRIPTION_STATE_UNSPECIFIED'\n | 'SUBSCRIPTION_STATE_PENDING'\n | 'SUBSCRIPTION_STATE_ACTIVE'\n | 'SUBSCRIPTION_STATE_PAUSED'\n | 'SUBSCRIPTION_STATE_IN_GRACE_PERIOD'\n | 'SUBSCRIPTION_STATE_ON_HOLD'\n | 'SUBSCRIPTION_STATE_CANCELED'\n | 'SUBSCRIPTION_STATE_EXPIRED';\n latestOrderId?: string;\n linkedPurchaseToken?: string;\n externalAccountIdentifiers?: {\n externalAccountId?: string;\n obfuscatedExternalAccountId?: string;\n obfuscatedExternalProfileId?: string;\n };\n acknowledgementState?:\n | 'ACKNOWLEDGEMENT_STATE_UNSPECIFIED'\n | 'ACKNOWLEDGEMENT_STATE_PENDING'\n | 'ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED';\n pausedStateContext?: { autoResumeTime?: string };\n canceledStateContext?: unknown;\n testPurchase?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Google RTDN notification types.\n * https://developer.android.com/google/play/billing/rtdn-reference\n */\nconst GOOGLE_NOTIFICATION_TYPE = {\n SUBSCRIPTION_RECOVERED: 1,\n SUBSCRIPTION_RENEWED: 2,\n SUBSCRIPTION_CANCELED: 3,\n SUBSCRIPTION_PURCHASED: 4,\n SUBSCRIPTION_ON_HOLD: 5,\n SUBSCRIPTION_IN_GRACE_PERIOD: 6,\n SUBSCRIPTION_RESTARTED: 7,\n SUBSCRIPTION_PRICE_CHANGE_CONFIRMED: 8,\n SUBSCRIPTION_DEFERRED: 9,\n SUBSCRIPTION_PAUSED: 10,\n SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED: 11,\n SUBSCRIPTION_REVOKED: 12,\n SUBSCRIPTION_EXPIRED: 13,\n} as const;\n\ntype GoogleNotificationType = (typeof GOOGLE_NOTIFICATION_TYPE)[keyof typeof GOOGLE_NOTIFICATION_TYPE];\n\n/**\n * Parsed Google RTDN inner message.\n */\ninterface GoogleDeveloperNotification {\n version?: string;\n packageName: string;\n eventTimeMillis: string;\n subscriptionNotification?: {\n version: string;\n notificationType: GoogleNotificationType;\n purchaseToken: string;\n subscriptionId: string;\n };\n voidedPurchaseNotification?: {\n purchaseToken: string;\n orderId: string;\n /** 1 = Subscription, 2 = One-time product */\n productType: 1 | 2;\n /** 1 = Full refund, 2 = Quantity-based partial refund (consumables) */\n refundType: 1 | 2;\n };\n oneTimeProductNotification?: {\n version: string;\n /** 1 = PURCHASED, 2 = CANCELED */\n notificationType: 1 | 2;\n purchaseToken: string;\n /** Product SKU (productId) */\n sku: string;\n };\n testNotification?: {\n version: string;\n };\n}\n\n/**\n * Decoded Google RTDN oneTimeProductNotification — sent when a consumable or\n * non-consumable product is purchased or canceled before acknowledgment.\n *\n * https://developer.android.com/google/play/billing/rtdn-reference#one-time\n */\nexport interface GoogleOneTimeProductNotification {\n /** 1 = PURCHASED, 2 = CANCELED (user canceled before purchase completed) */\n notificationType: 1 | 2;\n purchaseToken: string;\n /** Product SKU / productId */\n sku: string;\n packageName: string;\n}\n\n/**\n * Google Play Developer API v3 — ProductPurchase resource (consumable / non-consumable).\n * https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.products\n */\ninterface GoogleProductPurchase {\n purchaseTimeMillis?: string;\n purchaseState?: number; // 0 = Purchased, 1 = Canceled, 2 = Pending\n consumptionState?: number; // 0 = Not consumed, 1 = Consumed\n orderId?: string;\n /** Account identity set by the client at purchase time (obfuscatedAccountIdAndroid). */\n obfuscatedExternalAccountId?: string;\n [key: string]: unknown;\n}\n\n\n/**\n * Per-key in-flight token mint promises. Used to deduplicate concurrent\n * refreshes inside a single process — the cluster-wide cache layer sits in\n * `getDefaultCache()` underneath. Without this map every concurrent caller in\n * the same process would fire a separate network refresh while waiting for\n * the new value to land in Redis / memory.\n */\nconst refreshPromises = new Map<string, Promise<string>>();\n\n/**\n * Build the cache key for a given service account.\n *\n * Hashing the key (instead of using it raw) keeps the key length bounded for\n * cache backends that have key-size limits, and avoids leaking partial JSON\n * into log lines that print cache keys.\n */\nfunction googleTokenCacheKey(serviceAccountKey: string): string {\n let hash = 0;\n for (let i = 0; i < serviceAccountKey.length; i++) {\n hash = (hash * 31 + serviceAccountKey.charCodeAt(i)) | 0;\n }\n return `google:oauth:${hash}`;\n}\n\n/**\n * Obtain a Google OAuth2 access token, returning a cached token if it has more\n * than 60 seconds of remaining validity. Google tokens are valid for 3600 seconds,\n * so this avoids a network round-trip on every API call.\n *\n * Cache layer: `getDefaultCache()` (in-memory by default, swappable with\n * Redis / Memcached via `setDefaultCache()`). When the cache is shared across\n * cluster nodes only one node refreshes per TTL window.\n *\n * Promise deduplication prevents a thundering herd inside a single process:\n * concurrent callers that arrive while the token is being refreshed all await\n * the same in-flight request instead of each issuing their own.\n */\nasync function getCachedAccessToken(serviceAccountKey: string): Promise<string> {\n const cache = getDefaultCache();\n const cacheKey = googleTokenCacheKey(serviceAccountKey);\n\n const cached = await cache.get<{ token: string; expiresAt: number }>(cacheKey);\n if (cached && cached.expiresAt - Date.now() > 60_000) {\n return cached.token;\n }\n\n const inflight = refreshPromises.get(cacheKey);\n if (inflight) return inflight;\n\n const promise = getAccessToken(serviceAccountKey)\n .then(async (token) => {\n const expiresAt = Date.now() + 3_600_000;\n // TTL slightly under the 1h Google validity — cache eviction matches token expiry.\n await cache.set(cacheKey, { token, expiresAt }, 3_540);\n return token;\n })\n .finally(() => {\n refreshPromises.delete(cacheKey);\n });\n refreshPromises.set(cacheKey, promise);\n return promise;\n}\n\n/**\n * Obtain a Google OAuth2 access token using a service account key (JWT assertion flow).\n * The serviceAccountKey is expected to be a JSON string of the service account credentials.\n */\nasync function getAccessToken(serviceAccountKey: string): Promise<string> {\n let key: {\n client_email: string;\n private_key: string;\n token_uri?: string;\n };\n\n try {\n key = JSON.parse(serviceAccountKey) as typeof key;\n } catch {\n throw new Error('[onesub/google] Invalid serviceAccountKey JSON');\n }\n\n const tokenUri = key.token_uri ?? 'https://oauth2.googleapis.com/token';\n const scope = 'https://www.googleapis.com/auth/androidpublisher';\n const now = Math.floor(Date.now() / 1000);\n\n // Build a JWT assertion\n const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');\n const payload = Buffer.from(\n JSON.stringify({\n iss: key.client_email,\n scope,\n aud: tokenUri,\n iat: now,\n exp: now + 3600,\n })\n ).toString('base64url');\n\n const signingInput = `${header}.${payload}`;\n\n // Sign with the private key using the native crypto module\n const { createSign } = await import('crypto');\n const sign = createSign('RSA-SHA256');\n sign.update(signingInput);\n const signature = sign.sign(key.private_key, 'base64url');\n\n const assertion = `${signingInput}.${signature}`;\n\n const resp = await fetchWithTimeout(tokenUri, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n assertion,\n }),\n });\n\n if (!resp.ok) {\n throw new Error(`[onesub/google] Token request failed: ${resp.status}`);\n }\n\n const data = (await resp.json()) as { access_token?: string };\n if (!data.access_token) throw new Error('[onesub/google] No access_token in response');\n return data.access_token;\n}\n\n/**\n * Fetch a subscription purchase from the Google Play Developer API\n * (purchases.subscriptionsv2.get). The v2 endpoint takes only the purchaseToken\n * — the productId comes back in lineItems, which lets one token represent a\n * multi-product subscription.\n */\nasync function fetchSubscriptionPurchaseV2(\n packageName: string,\n purchaseToken: string,\n accessToken: string,\n): Promise<GoogleSubscriptionPurchaseV2> {\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(packageName)}/purchases/subscriptionsv2/tokens/` +\n `${encodeURIComponent(purchaseToken)}`;\n\n const resp = await fetchWithTimeout(url, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n throw new Error(`[onesub/google] Play API v2 error ${resp.status}: ${body}`);\n }\n\n return resp.json() as Promise<GoogleSubscriptionPurchaseV2>;\n}\n\n/**\n * Fetch a one-time product purchase from the Google Play Developer API.\n * Uses purchases.products instead of purchases.subscriptions.\n */\nasync function fetchProductPurchase(\n packageName: string,\n productId: string,\n purchaseToken: string,\n accessToken: string,\n): Promise<GoogleProductPurchase> {\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(packageName)}/purchases/products/` +\n `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}`;\n\n const resp = await fetchWithTimeout(url, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n throw new Error(`[onesub/google] Play Products API error ${resp.status}: ${body}`);\n }\n\n return resp.json() as Promise<GoogleProductPurchase>;\n}\n\n/**\n * Acknowledge a Google Play subscription purchase.\n * Google auto-refunds purchases that are not acknowledged within 3 days.\n *\n * Idempotent on Google's side: a no-op when the purchase has already been\n * acknowledged. Fire-and-forget — entitlement was already granted, so failures\n * are logged but not surfaced to the caller (operations should monitor logs).\n */\nexport async function acknowledgeGoogleSubscription(\n purchaseToken: string,\n productId: string,\n config: GoogleConfig,\n): Promise<void> {\n if (config.mockMode) return;\n if (!config.serviceAccountKey) return;\n if (!config.packageName) return;\n\n let accessToken: string;\n try {\n accessToken = await getCachedAccessToken(config.serviceAccountKey);\n } catch (err) {\n log.warn('[onesub/google] Could not get access token for subscription ack:', err);\n return;\n }\n\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(config.packageName)}/purchases/subscriptions/` +\n `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}:acknowledge`;\n\n try {\n const resp = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n body: '{}',\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n log.warn(`[onesub/google] Subscription acknowledge API error ${resp.status}: ${body} — auto-refund risk`);\n }\n } catch (err) {\n log.warn('[onesub/google] Subscription acknowledge network error — auto-refund risk:', err);\n }\n}\n\n/**\n * Acknowledge a Google Play one-time product purchase (non-consumable).\n * Consumables do not need this — :consume implicitly acknowledges.\n *\n * Same fire-and-forget semantics as acknowledgeGoogleSubscription.\n */\nexport async function acknowledgeGoogleProduct(\n purchaseToken: string,\n productId: string,\n config: GoogleConfig,\n): Promise<void> {\n if (config.mockMode) return;\n if (!config.serviceAccountKey) return;\n if (!config.packageName) return;\n\n let accessToken: string;\n try {\n accessToken = await getCachedAccessToken(config.serviceAccountKey);\n } catch (err) {\n log.warn('[onesub/google] Could not get access token for product ack:', err);\n return;\n }\n\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(config.packageName)}/purchases/products/` +\n `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}:acknowledge`;\n\n try {\n const resp = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n body: '{}',\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n log.warn(`[onesub/google] Product acknowledge API error ${resp.status}: ${body} — auto-refund risk`);\n }\n } catch (err) {\n log.warn('[onesub/google] Product acknowledge network error — auto-refund risk:', err);\n }\n}\n\n/**\n * Consume a one-time product purchase via the Google Play Developer API.\n * Must be called for consumables after the entitlement is granted to the user.\n * Google auto-refunds unconsumed purchases after 3 days.\n *\n * This function does not throw — callers should log failures and monitor,\n * as the entitlement has already been granted.\n */\nexport async function consumeGoogleProductReceipt(\n purchaseToken: string,\n productId: string,\n config: GoogleConfig,\n): Promise<void> {\n if (!config.serviceAccountKey) return;\n if (!config.packageName) return;\n\n let accessToken: string;\n try {\n accessToken = await getCachedAccessToken(config.serviceAccountKey);\n } catch (err) {\n log.warn('[onesub/google] Could not get access token for consume:', err);\n return;\n }\n\n const url =\n `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/` +\n `${encodeURIComponent(config.packageName)}/purchases/products/` +\n `${encodeURIComponent(productId)}/tokens/${encodeURIComponent(purchaseToken)}:consume`;\n\n try {\n const resp = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!resp.ok) {\n const body = await resp.text();\n log.warn(`[onesub/google] Consume API error ${resp.status}: ${body} — auto-refund risk`);\n }\n } catch (err) {\n log.warn('[onesub/google] Consume network error — auto-refund risk:', err);\n }\n}\n\n/**\n * Map a v2 subscriptionState string to onesub SubscriptionStatus.\n *\n * Returns null when the state is unrecognised or PENDING (initial purchase\n * not yet settled — entitlement should not be granted yet).\n */\nfunction deriveStatusV2(\n state: GoogleSubscriptionPurchaseV2['subscriptionState'],\n): SubscriptionInfo['status'] | null {\n switch (state) {\n case 'SUBSCRIPTION_STATE_ACTIVE':\n return SUBSCRIPTION_STATUS.ACTIVE;\n case 'SUBSCRIPTION_STATE_IN_GRACE_PERIOD':\n return SUBSCRIPTION_STATUS.GRACE_PERIOD;\n case 'SUBSCRIPTION_STATE_ON_HOLD':\n return SUBSCRIPTION_STATUS.ON_HOLD;\n case 'SUBSCRIPTION_STATE_PAUSED':\n return SUBSCRIPTION_STATUS.PAUSED;\n case 'SUBSCRIPTION_STATE_CANCELED':\n return SUBSCRIPTION_STATUS.CANCELED;\n case 'SUBSCRIPTION_STATE_EXPIRED':\n return SUBSCRIPTION_STATUS.EXPIRED;\n case 'SUBSCRIPTION_STATE_PENDING':\n case 'SUBSCRIPTION_STATE_UNSPECIFIED':\n default:\n return null;\n }\n}\n\n/**\n * Validate a Google Play purchase token via purchases.subscriptionsv2.get.\n *\n * The productId argument is used to pick the matching `lineItems` entry. If\n * the response has no lineItem with that productId, validation fails — the\n * token does not entitle the caller to that product. (For multi-product\n * subscriptions, callers can read other lineItems out of the API directly.)\n *\n * @param receipt The purchaseToken from the client (Google Play Billing)\n * @param productId Expected subscription productId — must match a lineItem\n * @param config Google config with packageName + optional serviceAccountKey\n */\nexport async function validateGoogleReceipt(\n receipt: string,\n productId: string,\n config: GoogleConfig\n): Promise<SubscriptionInfo | null> {\n if (config.mockMode) return mockValidateGoogleSubscription(receipt, productId);\n if (!config.serviceAccountKey) {\n log.warn('[onesub/google] No serviceAccountKey provided — cannot call Play API');\n return null;\n }\n if (!config.packageName) {\n log.warn('[onesub/google] No packageName provided — cannot call Play API');\n return null;\n }\n\n let purchase: GoogleSubscriptionPurchaseV2;\n try {\n const token = await getCachedAccessToken(config.serviceAccountKey);\n purchase = await fetchSubscriptionPurchaseV2(config.packageName, receipt, token);\n } catch (err) {\n log.error('[onesub/google] Receipt validation failed:', err);\n return null;\n }\n\n const status = deriveStatusV2(purchase.subscriptionState);\n if (!status) {\n log.warn(\n '[onesub/google] Unrecognised or pending subscriptionState — rejecting:',\n purchase.subscriptionState,\n );\n return null;\n }\n\n // Pick the lineItem matching the requested productId. v2 supports multi-product\n // subscriptions so the same token can carry several lineItems; we only entitle\n // the caller for the productId they explicitly asked about.\n const lineItem = purchase.lineItems?.find((item) => item.productId === productId);\n if (!lineItem) {\n log.warn(\n '[onesub/google] productId not found in subscription lineItems:',\n productId,\n 'available:',\n purchase.lineItems?.map((i) => i.productId).join(', ') ?? '(none)',\n );\n return null;\n }\n\n const expiresAt = lineItem.expiryTime ?? new Date().toISOString();\n const purchasedAt = purchase.startTime ?? new Date().toISOString();\n const willRenew = lineItem.autoRenewingPlan?.autoRenewEnabled ?? false;\n\n // Surface autoResumeTime only when the subscription is actually paused —\n // pausedStateContext is undefined for any other state, but be defensive in\n // case Google starts including it on adjacent states (e.g. recently-resumed).\n const autoResumeTime =\n status === SUBSCRIPTION_STATUS.PAUSED\n ? purchase.pausedStateContext?.autoResumeTime\n : undefined;\n\n const boundAccountId = purchase.externalAccountIdentifiers?.obfuscatedExternalAccountId;\n\n return {\n userId: '', // caller fills this in\n productId,\n platform: 'google',\n status,\n expiresAt,\n // Keyed by the purchaseToken, NOT latestOrderId: RTDN webhooks and\n // linkedPurchaseToken chains only carry the token, so records keyed by\n // order id are invisible to the entire webhook lifecycle (refund/cancel/\n // renewal would never find them).\n originalTransactionId: receipt,\n purchasedAt,\n willRenew,\n // Surface the linked token so host apps (and our webhook userId-continuity\n // logic) can follow upgrade/downgrade chains.\n linkedPurchaseToken: purchase.linkedPurchaseToken,\n autoResumeTime,\n ...(boundAccountId ? { boundAccountId } : {}),\n };\n}\n\n/**\n * Result of validating a Google Play product (consumable / non-consumable) receipt.\n */\nexport interface GoogleProductResult {\n /** orderId — unique per Google Play transaction, safe deduplication key */\n transactionId: string;\n purchasedAt: string;\n /**\n * Account identity the client baked in at purchase time\n * (`obfuscatedAccountIdAndroid` → Play `obfuscatedExternalAccountId`). When\n * present the validate route rejects attributing/reassigning the purchase to\n * a non-matching userId. Absent for purchases made before the client set it\n * (backward-compatible).\n */\n obfuscatedExternalAccountId?: string;\n}\n\n/**\n * Validate a Google Play purchase token for a one-time product (consumable or\n * non-consumable). Uses purchases.products, not purchases.subscriptions.\n *\n * Security checks applied beyond basic API verification:\n * - purchaseState must be 0 (completed)\n * - consumptionState checked for consumables: already-consumed tokens indicate replay\n * - Receipt age limited to 72 hours\n * - orderId used as transactionId (per-purchase unique, unlike purchaseToken)\n */\nexport async function validateGoogleProductReceipt(\n purchaseToken: string,\n productId: string,\n config: GoogleConfig,\n type: 'consumable' | 'non_consumable' = 'non_consumable',\n): Promise<GoogleProductResult | null> {\n if (config.mockMode) return mockValidateGoogleProduct(purchaseToken, productId);\n if (!config.serviceAccountKey) {\n log.warn('[onesub/google] No serviceAccountKey — cannot validate product receipt');\n return null;\n }\n if (!config.packageName) {\n log.warn('[onesub/google] No packageName — cannot validate product receipt');\n return null;\n }\n\n let purchase: GoogleProductPurchase;\n try {\n const token = await getCachedAccessToken(config.serviceAccountKey);\n purchase = await fetchProductPurchase(config.packageName, productId, purchaseToken, token);\n } catch (err) {\n log.error('[onesub/google] Product receipt validation failed:', err);\n return null;\n }\n\n // purchaseState 0 = completed (1 = canceled, 2 = pending)\n if (purchase.purchaseState !== 0) {\n log.warn('[onesub/google] Purchase not completed, state:', purchase.purchaseState);\n return null;\n }\n\n // For consumables: consumptionState 1 means already consumed by a previous request.\n // This is the primary replay-attack signal for consumables on Android.\n if (type === 'consumable' && purchase.consumptionState === 1) {\n log.warn('[onesub/google] Consumable already consumed — possible replay attack');\n return null;\n }\n\n // Reject receipts older than the configured window (see\n // productReceiptMaxAgeHours — raise it for migrations / e2e).\n if (purchase.purchaseTimeMillis) {\n const purchaseTime = parseInt(purchase.purchaseTimeMillis, 10);\n const maxAgeMs = (config.productReceiptMaxAgeHours ?? 72) * 60 * 60 * 1000;\n if (Date.now() - purchaseTime > maxAgeMs) {\n log.warn(`[onesub/google] Product receipt too old (>${config.productReceiptMaxAgeHours ?? 72}h)`);\n return null;\n }\n }\n\n if (!purchase.orderId) {\n log.warn('[onesub/google] No orderId in product purchase');\n return null;\n }\n\n return {\n transactionId: purchase.orderId,\n purchasedAt: purchase.purchaseTimeMillis\n ? new Date(parseInt(purchase.purchaseTimeMillis, 10)).toISOString()\n : new Date().toISOString(),\n obfuscatedExternalAccountId: purchase.obfuscatedExternalAccountId,\n };\n}\n\n/**\n * Decode a Google RTDN Pub/Sub notification.\n * Returns the extracted subscription notification data, or null for test pings\n * and notification kinds handled by other decoders (e.g. voided purchases).\n */\nexport function decodeGoogleNotification(payload: GoogleNotificationPayload): {\n notificationType: GoogleNotificationType;\n purchaseToken: string;\n subscriptionId: string;\n packageName: string;\n} | null {\n let notification: GoogleDeveloperNotification;\n\n try {\n const json = Buffer.from(payload.message.data, 'base64').toString('utf-8');\n notification = JSON.parse(json) as GoogleDeveloperNotification;\n } catch {\n return null;\n }\n\n if (!notification.subscriptionNotification) {\n // test notification, voided purchase, or unsupported type\n return null;\n }\n\n const { notificationType, purchaseToken, subscriptionId } = notification.subscriptionNotification;\n\n return {\n notificationType,\n purchaseToken,\n subscriptionId,\n packageName: notification.packageName,\n };\n}\n\n/**\n * Decoded Google RTDN voidedPurchaseNotification — sent when a purchase\n * (subscription or one-time product) is refunded, charged back, or revoked.\n *\n * https://developer.android.com/google/play/billing/rtdn-reference#voided-purchase\n */\nexport interface GoogleVoidedNotification {\n /** Subscription purchaseToken or one-time product purchaseToken */\n purchaseToken: string;\n /** GPA.* — matches the orderId stored as transactionId for one-time products */\n orderId: string;\n /** 1 = Subscription, 2 = One-time product */\n productType: 1 | 2;\n /** 1 = Full refund, 2 = Quantity-based partial refund (consumables) */\n refundType: 1 | 2;\n packageName: string;\n}\n\n/**\n * Decode a Google RTDN voidedPurchaseNotification, if the payload is one.\n * Returns null when the payload is a different notification kind.\n */\nexport function decodeGoogleVoidedNotification(\n payload: GoogleNotificationPayload,\n): GoogleVoidedNotification | null {\n let notification: GoogleDeveloperNotification;\n\n try {\n const json = Buffer.from(payload.message.data, 'base64').toString('utf-8');\n notification = JSON.parse(json) as GoogleDeveloperNotification;\n } catch {\n return null;\n }\n\n if (!notification.voidedPurchaseNotification) return null;\n\n const { purchaseToken, orderId, productType, refundType } = notification.voidedPurchaseNotification;\n\n return {\n purchaseToken,\n orderId,\n productType,\n refundType,\n packageName: notification.packageName,\n };\n}\n\n/**\n * Decode a Google RTDN oneTimeProductNotification, if the payload is one.\n * Returns null when the payload is a different notification kind.\n *\n * Note: the notification does NOT carry userId context. For PURCHASED, the\n * receipt must be acknowledged via acknowledgeGoogleProduct to prevent the\n * 3-day auto-refund window. The userId is only known after the client calls\n * POST /onesub/purchase/validate, which is the authoritative record-creation\n * path. The webhook handler therefore acknowledges without creating a record.\n */\nexport function decodeGoogleOneTimeProductNotification(\n payload: GoogleNotificationPayload,\n): GoogleOneTimeProductNotification | null {\n let notification: GoogleDeveloperNotification;\n try {\n const json = Buffer.from(payload.message.data, 'base64').toString('utf-8');\n notification = JSON.parse(json) as GoogleDeveloperNotification;\n } catch {\n return null;\n }\n if (!notification.oneTimeProductNotification) return null;\n const { notificationType, purchaseToken, sku } = notification.oneTimeProductNotification;\n if (notificationType !== 1 && notificationType !== 2) {\n log.warn('[onesub/google] Unknown oneTimeProductNotification type:', notificationType);\n return null;\n }\n return { notificationType, purchaseToken, sku, packageName: notification.packageName };\n}\n\n/**\n * Determine if a Google RTDN notification type represents an active subscription\n * inside the paid window (excludes grace period — that's a separate state now).\n */\nexport function isGoogleActiveNotification(notificationType: GoogleNotificationType): boolean {\n return (\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_PURCHASED ||\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_RENEWED ||\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_RECOVERED ||\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_RESTARTED\n );\n}\n\nexport function isGoogleCanceledNotification(notificationType: GoogleNotificationType): boolean {\n return (\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_CANCELED ||\n notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_REVOKED\n );\n}\n\nexport function isGoogleExpiredNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_EXPIRED;\n}\n\n/**\n * Subscription is in the store-granted grace period — payment failed but the\n * user retains access while Google retries. Maps to SUBSCRIPTION_STATUS.GRACE_PERIOD.\n */\nexport function isGoogleGracePeriodNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_IN_GRACE_PERIOD;\n}\n\n/**\n * Subscription is on hold — grace period ended, retry continuing, entitlement\n * REVOKED. Maps to SUBSCRIPTION_STATUS.ON_HOLD.\n */\nexport function isGoogleOnHoldNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_ON_HOLD;\n}\n\n/**\n * User-voluntary pause — entitlement REVOKED until autoResumeTime or manual\n * resume. Maps to SUBSCRIPTION_STATUS.PAUSED.\n */\nexport function isGooglePausedNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_PAUSED;\n}\n\n/**\n * User agreed to a developer-initiated price change. The new price applies\n * on the next renewal. Subscription remains active in the meantime.\n */\nexport function isGooglePriceChangeConfirmedNotification(notificationType: GoogleNotificationType): boolean {\n return notificationType === GOOGLE_NOTIFICATION_TYPE.SUBSCRIPTION_PRICE_CHANGE_CONFIRMED;\n}\n","import type { Response } from 'express';\nimport { z } from 'zod';\nimport type { OneSubErrorCode } from '@onesub/shared';\nimport { ONESUB_ERROR_CODE } from '@onesub/shared';\n\n/**\n * Send a structured error response. Every onesub HTTP endpoint uses this\n * so consumers can rely on `errorCode` being present on every 4xx/5xx body.\n *\n * `extra` lets callers inject route-specific defaults (e.g. `purchase: null`\n * for ValidatePurchaseResponse shape compatibility).\n */\nexport function sendError(\n res: Response,\n status: number,\n code: OneSubErrorCode,\n error: string,\n extra: Record<string, unknown> = {},\n): void {\n res.status(status).json({ ...extra, error, errorCode: code });\n}\n\n/**\n * 400 response for a failed zod parse. The ZodError's per-issue messages\n * are joined for the human-readable `error` field.\n */\nexport function sendZodError(\n res: Response,\n err: z.ZodError,\n extra: Record<string, unknown> = {},\n): void {\n sendError(\n res,\n 400,\n ONESUB_ERROR_CODE.INVALID_INPUT,\n err.issues.map((e: { message: string }) => e.message).join(', '),\n extra,\n );\n}\n","import type { OneSubAppConfig, OneSubServerConfig } from '@onesub/shared';\nimport { log } from './logger.js';\n\n/**\n * Resolves which app an incoming receipt belongs to, so one onesub instance can\n * serve N bundles.\n *\n * A single-app deployment configures `apple`/`google` at the top level and never\n * touches `apps` — that config stays the default and nothing here changes its\n * behaviour.\n */\nexport interface AppRegistry {\n readonly apps: OneSubAppConfig[];\n readonly defaultApp: OneSubAppConfig | undefined;\n /**\n * Returns the server config with `apple`/`google` swapped to the matched app's\n * credentials, so downstream providers keep reading `config.apple` /\n * `config.google` without knowing about multi-app.\n */\n configFor(hint: AppHint): OneSubServerConfig;\n}\n\nexport interface AppHint {\n /** `appId` from the request body, when the client sends one. */\n appId?: string | null | undefined;\n /** Bundle id read out of an Apple receipt — the receipt names its own app. */\n bundleId?: string | null | undefined;\n}\n\nfunction appName(app: OneSubAppConfig): string {\n return app.id || app.apple?.bundleId || app.google?.packageName || 'default';\n}\n\nconst registryCache = new WeakMap<OneSubServerConfig, AppRegistry>();\n\n/**\n * Registry for this config, built once. Routes and webhook handlers all resolve\n * through the same instance so the multi-app summary is logged a single time.\n */\nexport function getAppRegistry(config: OneSubServerConfig): AppRegistry {\n const cached = registryCache.get(config);\n if (cached) return cached;\n const registry = buildAppRegistry(config);\n registryCache.set(config, registry);\n return registry;\n}\n\nexport function buildAppRegistry(config: OneSubServerConfig): AppRegistry {\n const apps: OneSubAppConfig[] = [...(config.apps ?? [])];\n\n // The top-level apple/google config is an app in its own right. Keeping it in\n // the list means a single-app deployment resolves through the same path.\n if (config.apple || config.google) {\n const id = config.defaultAppId || config.apple?.bundleId || config.google?.packageName || 'default';\n if (!apps.some((a) => a.id === id)) {\n apps.unshift({ id, apple: config.apple, google: config.google });\n }\n }\n\n const defaultApp =\n apps.find((a) => a.id === config.defaultAppId) ??\n // No explicit default: fall back to the top-level config, which unshifted to\n // the front above. Never silently pick an arbitrary app when several exist —\n // that would validate one app's receipt against another's credentials.\n (config.apple || config.google || apps.length === 1 ? apps[0] : undefined);\n\n if (apps.length > 1) {\n log.info('[onesub] Multi-app mode:', apps.map(appName).join(', '), '| default:', defaultApp ? appName(defaultApp) : '(none)');\n }\n\n function match(hint: AppHint): OneSubAppConfig | undefined {\n if (hint.appId) {\n const byId = apps.find(\n (a) =>\n a.id === hint.appId ||\n a.apple?.bundleId === hint.appId ||\n a.google?.packageName === hint.appId,\n );\n if (byId) return byId;\n // An appId we don't serve must not silently fall through to the default —\n // that would validate it against some other app's credentials.\n log.warn('[onesub] Unknown appId:', hint.appId);\n return undefined;\n }\n\n // Apple receipts carry their own bundleId, so an Apple client needs no appId.\n if (hint.bundleId) {\n const byBundle = apps.find((a) => a.apple?.bundleId === hint.bundleId);\n if (byBundle) return byBundle;\n log.warn('[onesub] No app configured for bundleId:', hint.bundleId);\n return undefined;\n }\n\n return defaultApp;\n }\n\n return {\n apps,\n defaultApp,\n configFor(hint: AppHint): OneSubServerConfig {\n const app = match(hint);\n if (!app) {\n // Hand back a config with no providers: the route then reports the usual\n // \"config missing\" error rather than validating against the wrong app.\n return { ...config, apple: undefined, google: undefined };\n }\n return { ...config, apple: app.apple, google: app.google };\n },\n };\n}\n\n/**\n * Reads the bundleId out of an Apple JWS **without verifying its signature**,\n * only to decide which app's credentials to validate it with.\n *\n * Trusting this to pick the app is safe: the chosen app's validator then verifies\n * the signature against Apple's roots and re-checks the bundleId, so a forged\n * payload claiming another app's bundle fails there.\n */\nexport function peekAppleBundleId(jws: string): string | undefined {\n try {\n const payload = jws.split('.')[1];\n if (!payload) return undefined;\n const json = Buffer.from(payload, 'base64url').toString('utf-8');\n const claims = JSON.parse(json) as { bundleId?: string };\n return typeof claims.bundleId === 'string' ? claims.bundleId : undefined;\n } catch {\n return undefined;\n }\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type { ValidateReceiptResponse, OneSubServerConfig } from '@onesub/shared';\nimport { ROUTES, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { SubscriptionStore } from '../store.js';\nimport { validateAppleReceipt } from '../providers/apple.js';\nimport { validateGoogleReceipt, acknowledgeGoogleSubscription } from '../providers/google.js';\nimport { log } from '../logger.js';\nimport { sendError, sendZodError } from '../errors.js';\nimport { getAppRegistry, peekAppleBundleId } from '../apps.js';\n\nconst NO_SUB = { valid: false, subscription: null } as const;\n\nconst validateSchema = z.object({\n platform: z.enum(['apple', 'google']),\n receipt: z.string().min(1).max(10000),\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256),\n /** Which app this receipt belongs to. Optional — see OneSubServerConfig.apps. */\n appId: z.string().min(1).max(256).optional(),\n});\n\nexport function createValidateRouter(\n config: OneSubServerConfig,\n store: SubscriptionStore\n): Router {\n const router = Router();\n const registry = getAppRegistry(config);\n\n router.post(ROUTES.VALIDATE, async (req: Request, res: Response) => {\n let platform: string;\n let receipt: string;\n let userId: string;\n let productId: string;\n let appId: string | undefined;\n\n try {\n ({ platform, receipt, userId, productId, appId } = validateSchema.parse(req.body));\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err, NO_SUB);\n return;\n }\n throw err;\n }\n\n try {\n let sub = null;\n\n // An Apple receipt names its own app, so an Apple client needs no appId.\n // A Google purchase token does not, so it relies on appId (or the default).\n const appConfig = registry.configFor({\n appId,\n bundleId: platform === 'apple' ? peekAppleBundleId(receipt) : undefined,\n });\n\n if (platform === 'apple') {\n if (!appConfig.apple) {\n sendError(res, 500, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, 'Apple configuration not provided', NO_SUB);\n return;\n }\n sub = await validateAppleReceipt(receipt, appConfig.apple);\n } else {\n if (!appConfig.google) {\n sendError(res, 500, ONESUB_ERROR_CODE.GOOGLE_CONFIG_MISSING, 'Google configuration not provided', NO_SUB);\n return;\n }\n sub = await validateGoogleReceipt(receipt, productId, appConfig.google);\n }\n\n if (!sub) {\n sendError(res, 422, ONESUB_ERROR_CODE.RECEIPT_VALIDATION_FAILED, 'Receipt validation failed', NO_SUB);\n return;\n }\n\n // Account-binding guard (payment-bypass defense), mirroring the one-time\n // purchase route: when the receipt carries an account identity baked in at\n // purchase time (Apple appAccountToken / Google obfuscatedExternalAccountId),\n // the subscription may only be bound to a matching userId. Without this, a\n // leaked/shared receipt can be re-bound to any attacker-chosen userId.\n // Apple compares case-insensitively (appAccountToken is normalized to a\n // lowercase UUID); Google ids are verbatim — a case-insensitive compare\n // would let a case-flipped userId through on hosts with case-sensitive ids.\n const boundAccountId = sub.boundAccountId;\n delete sub.boundAccountId;\n const bindingMismatch = platform === 'apple'\n ? boundAccountId && boundAccountId.toLowerCase() !== userId.toLowerCase()\n : boundAccountId && boundAccountId !== userId;\n if (bindingMismatch) {\n log.warn(\n `[onesub/validate] account binding mismatch for ${sub.originalTransactionId}: receipt token does not match userId ${userId}`,\n );\n sendError(\n res,\n 409,\n ONESUB_ERROR_CODE.TRANSACTION_BELONGS_TO_OTHER_USER,\n 'TRANSACTION_BELONGS_TO_OTHER_USER',\n NO_SUB,\n );\n return;\n }\n\n sub.userId = userId;\n await store.save(sub);\n\n // Google requires acknowledgement within 3 days of purchase or the\n // transaction is auto-refunded. Fire-and-forget — entitlement is already\n // saved, ack is idempotent on the Play side.\n if (platform === 'google' && appConfig.google) {\n void acknowledgeGoogleSubscription(receipt, productId, appConfig.google);\n }\n\n const response: ValidateReceiptResponse = { valid: true, subscription: sub };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/validate] Unexpected error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, 'Internal server error during receipt validation', NO_SUB);\n }\n });\n\n return router;\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport type { StatusResponse } from '@onesub/shared';\nimport { ROUTES, SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { SubscriptionStore } from '../store.js';\nimport { log } from '../logger.js';\nimport { sendError } from '../errors.js';\n\nconst NO_SUB = { active: false, subscription: null } as const;\n\nexport function createStatusRouter(store: SubscriptionStore): Router {\n const router = Router();\n\n /**\n * GET /onesub/status?userId=xxx\n *\n * Returns whether the user has an active subscription and the full\n * SubscriptionInfo if one exists.\n */\n router.get(ROUTES.STATUS, async (req: Request, res: Response) => {\n const userId = req.query['userId'];\n\n if (!userId || typeof userId !== 'string') {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'Missing required query param: userId', NO_SUB);\n return;\n }\n\n if (userId.length > 256) {\n sendError(res, 400, ONESUB_ERROR_CODE.USER_ID_TOO_LONG, 'userId must not exceed 256 characters', NO_SUB);\n return;\n }\n\n try {\n const sub = await store.getByUserId(userId);\n\n if (!sub) {\n const response: StatusResponse = { active: false, subscription: null };\n res.status(200).json(response);\n return;\n }\n\n // Entitlement is valid during the store-granted grace period (Apple\n // GRACE_PERIOD subtype, Google IN_GRACE_PERIOD). on_hold is excluded —\n // the user must fix payment before they regain access.\n //\n // The expiresAt check guards two cases:\n // 1. refundPolicy='until_expiry' lets a refunded record keep\n // status='active' until the original expiry passes — without this\n // check, the user would keep entitlement forever if Apple/Google\n // never sent the matching EXPIRED notification.\n // 2. Stale records where the EXPIRED webhook was missed entirely.\n const statusAllows =\n sub.status === SUBSCRIPTION_STATUS.ACTIVE ||\n sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD;\n const notYetExpired = new Date(sub.expiresAt).getTime() > Date.now();\n const active = statusAllows && notYetExpired;\n const response: StatusResponse = { active, subscription: sub };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/status] Store error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error', NO_SUB);\n }\n });\n\n return router;\n}\n","import type { CacheAdapter } from './cache.js';\n\n/**\n * Pluggable webhook-event idempotency store.\n *\n * Apple sends `notificationUUID` and Google Pub/Sub sends `messageId` — both\n * are guaranteed unique per notification. Apple/Google will retry on any\n * non-2xx response (Apple: ~3 days, Google: configurable Pub/Sub policy), so\n * the same notification can hit our handler multiple times if the previous\n * response timed out, the DB was briefly down, etc. We must not double-apply\n * state changes (extra subscription days, refunded purchase being deleted\n * twice, consumption response being PUT twice).\n *\n * `markIfNew` returns `true` when the caller should process the event,\n * `false` when it has been seen before. Implementations decide retention —\n * once-seen IDs are kept long enough that retries from the source can't beat\n * the TTL (we recommend ≥ 7 days).\n */\nexport interface WebhookEventStore {\n /**\n * Atomically register the given event id. Returns `true` if it's new (first\n * time we've seen it), `false` if it was already seen.\n *\n * `provider` lets us key by source so Apple's notificationUUID and Google's\n * messageId never collide.\n */\n markIfNew(provider: 'apple' | 'google', eventId: string): Promise<boolean>;\n\n /**\n * Forget a previously marked event id. Called by the webhook routes when\n * processing fails AFTER `markIfNew` succeeded (e.g. the store was briefly\n * down) — without it, the source's retry would be deduped and the lifecycle\n * event permanently dropped. Optional for backward compatibility with\n * custom implementations.\n */\n unmark?(provider: 'apple' | 'google', eventId: string): Promise<void>;\n}\n\n/**\n * Best-effort unmark used by the webhook routes when INLINE processing fails\n * after `markIfNew` succeeded — without it the source's retry (Apple/Google\n * both retry on 5xx) would be deduped and the lifecycle event dropped forever.\n *\n * No-ops when the store is absent, the store doesn't implement `unmark`, or\n * no event id was marked. Swallows its own errors: a failing unmark must not\n * mask the original processing error that triggered it.\n *\n * NOTE — only the inline path (and a failed `enqueue()`) calls this. When a\n * webhook queue is configured and the job was durably enqueued, handler\n * failures are retried by the QUEUE, not the source; unmarking there would\n * let a source retry race the queue's retries and double-apply the event.\n */\nexport async function unmarkWebhookEvent(\n store: WebhookEventStore | undefined,\n provider: 'apple' | 'google',\n eventId: string | undefined,\n): Promise<void> {\n if (!store?.unmark || typeof eventId !== 'string') return;\n try {\n await store.unmark(provider, eventId);\n } catch {\n /* best-effort */\n }\n}\n\n/** Default 7-day retention — covers Apple's 3-day retry window plus headroom. */\nconst DEFAULT_TTL_SECONDS = 7 * 24 * 60 * 60;\n\n/**\n * In-memory implementation. Suitable for single-instance dev/test; for\n * production multi-instance use the Redis or Postgres variants so retries to\n * other nodes are still deduped.\n */\nexport class InMemoryWebhookEventStore implements WebhookEventStore {\n private readonly seen = new Map<string, number>();\n\n constructor(private readonly ttlSeconds = DEFAULT_TTL_SECONDS) {}\n\n async markIfNew(provider: 'apple' | 'google', eventId: string): Promise<boolean> {\n this.evictExpired();\n const key = `${provider}:${eventId}`;\n if (this.seen.has(key)) return false;\n this.seen.set(key, Date.now() + this.ttlSeconds * 1000);\n return true;\n }\n\n async unmark(provider: 'apple' | 'google', eventId: string): Promise<void> {\n this.seen.delete(`${provider}:${eventId}`);\n }\n\n private evictExpired(): void {\n const now = Date.now();\n for (const [key, expiresAt] of this.seen) {\n if (expiresAt < now) this.seen.delete(key);\n }\n }\n}\n\n/**\n * Cache-backed implementation — works with any `CacheAdapter`.\n *\n * Uses a `get` → `set` sequence, which is **not** atomic under concurrent\n * retries: two simultaneous calls for the same id can both read null and\n * both return `true`. The downstream store PKs / BullMQ jobId dedup catch\n * this worst case.\n *\n * For Redis deployments prefer `RedisWebhookEventStore` (from\n * `@onesub/server`) which uses a single `SET NX` command and is fully\n * atomic.\n */\nexport class CacheWebhookEventStore implements WebhookEventStore {\n constructor(\n private readonly cache: CacheAdapter,\n private readonly ttlSeconds = DEFAULT_TTL_SECONDS,\n ) {}\n\n async markIfNew(provider: 'apple' | 'google', eventId: string): Promise<boolean> {\n const key = `webhook:event:${provider}:${eventId}`;\n const existing = await this.cache.get<string>(key);\n if (existing) return false;\n await this.cache.set(key, '1', this.ttlSeconds);\n return true;\n }\n\n async unmark(provider: 'apple' | 'google', eventId: string): Promise<void> {\n await this.cache.del(`webhook:event:${provider}:${eventId}`);\n }\n}\n","import type { Request, Response } from 'express';\nimport { randomUUID } from 'node:crypto';\nimport type {\n AppleNotificationPayload,\n OneSubServerConfig,\n SubscriptionInfo,\n} from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { SubscriptionStore, PurchaseStore } from '../store.js';\nimport {\n decodeAppleNotification,\n decodeJws,\n sendAppleConsumptionResponse,\n fetchAppleSubscriptionStatus,\n} from '../providers/apple.js';\nimport { log } from '../logger.js';\nimport { getAppRegistry } from '../apps.js';\nimport { sendError } from '../errors.js';\nimport type { WebhookEventStore } from '../webhook-events.js';\nimport { unmarkWebhookEvent } from '../webhook-events.js';\nimport type { WebhookQueue } from '../webhook-queue.js';\n\nconst APPLE_ACTIVE_TYPES = new Set(['SUBSCRIBED', 'DID_RENEW', 'DID_RECOVER', 'OFFER_REDEEMED']);\n// Granted refunds/revocations only. CONSUMPTION_REQUEST is deliberately NOT\n// here: it is Apple *asking for consumption info* while reviewing a refund\n// request — the refund may be declined, so revoking entitlement (or deleting\n// a purchase row) on it is premature. The actual REFUND/REVOKE notification\n// follows if Apple grants it.\nconst APPLE_CANCELED_TYPES = new Set(['REVOKE', 'REFUND']);\nconst APPLE_EXPIRED_TYPES = new Set(['EXPIRED']);\n\nfunction mapAppleNotificationStatus(\n notificationType: string,\n subtype: string | undefined,\n): SubscriptionInfo['status'] | null {\n if (notificationType === 'DID_FAIL_TO_RENEW') {\n return subtype === 'GRACE_PERIOD'\n ? SUBSCRIPTION_STATUS.GRACE_PERIOD\n : SUBSCRIPTION_STATUS.ON_HOLD;\n }\n if (notificationType === 'GRACE_PERIOD_EXPIRED') return SUBSCRIPTION_STATUS.ON_HOLD;\n if (APPLE_CANCELED_TYPES.has(notificationType)) return SUBSCRIPTION_STATUS.CANCELED;\n if (APPLE_EXPIRED_TYPES.has(notificationType)) return SUBSCRIPTION_STATUS.EXPIRED;\n if (APPLE_ACTIVE_TYPES.has(notificationType)) return SUBSCRIPTION_STATUS.ACTIVE;\n return null;\n}\n\n/** Fully decoded + verified Apple notification transaction data. */\ntype DecodedAppleNotification = NonNullable<Awaited<ReturnType<typeof decodeAppleNotification>>>;\n\n/**\n * The unit of work handed to the state-mutating processor. Deliberately\n * JSON-serializable (plain strings / booleans / nulls) so it survives a\n * round-trip through a Redis-backed queue (BullMQ) unchanged.\n */\nexport interface AppleWebhookWork {\n decoded: DecodedAppleNotification;\n notificationType: string;\n subtype?: string;\n}\n\n/**\n * State-mutating half of the Apple webhook: everything AFTER the gate\n * (payload validation, JWS verification, idempotency markIfNew, bundleId\n * check) has passed. Called from two places:\n * - inline by `handleAppleWebhook` when no queue is configured\n * - by the webhook queue handler (registered in createWebhookRouter)\n * Throws on store failure — the caller decides the retry semantics (source\n * retry via 5xx inline, queue retry + dead-letter in queue mode).\n */\nexport async function processAppleNotification(\n work: AppleWebhookWork,\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Promise<void> {\n const { notificationType, subtype } = work;\n const {\n originalTransactionId,\n transactionId,\n type,\n productId,\n bundleId,\n environment,\n status,\n willRenew,\n expiresAt,\n appAccountToken,\n inAppOwnershipType,\n } = work.decoded;\n\n // The notification names its own app; use that app's Apple credentials rather\n // than whichever app happens to be the default.\n const appleConfigForApp = getAppRegistry(config).configFor({ bundleId }).apple;\n\n const mapped = mapAppleNotificationStatus(notificationType, subtype);\n const finalStatus: SubscriptionInfo['status'] = mapped ?? status;\n\n if (\n notificationType === 'CONSUMPTION_REQUEST' &&\n appleConfigForApp?.consumptionInfoProvider &&\n transactionId &&\n productId\n ) {\n const provider = appleConfigForApp.consumptionInfoProvider;\n const appleConfig = appleConfigForApp;\n void (async () => {\n try {\n const info = await provider({\n transactionId,\n originalTransactionId,\n productId,\n bundleId: bundleId ?? appleConfig.bundleId,\n environment,\n });\n if (info) {\n await sendAppleConsumptionResponse(transactionId, info, appleConfig, {\n sandbox: environment === 'Sandbox',\n });\n }\n } catch (err) {\n log.warn('[onesub/webhook/apple] consumptionInfoProvider failed:', err);\n }\n })();\n }\n\n const isOneTimePurchase = type === 'Consumable' || type === 'Non-Consumable';\n const isRefundOrRevoke = APPLE_CANCELED_TYPES.has(notificationType);\n\n // CONSUMPTION_REQUEST on a one-time purchase is informational — the\n // consumptionInfoProvider hook above is the only work to do. Falling through\n // would run the subscription lookup and a doomed Status API recovery call.\n if (isOneTimePurchase && notificationType === 'CONSUMPTION_REQUEST') {\n return;\n }\n\n if (isOneTimePurchase && isRefundOrRevoke) {\n const lookupId = transactionId ?? originalTransactionId;\n const removed = await purchaseStore.deletePurchaseByTransactionId(lookupId);\n if (!removed) {\n log.warn('[onesub/webhook/apple] IAP refund for unknown transaction:', lookupId);\n }\n return;\n }\n\n const existing = await store.getByTransactionId(originalTransactionId);\n if (existing) {\n const isSubscriptionRefund = isRefundOrRevoke && !isOneTimePurchase;\n const keepEntitlement = isSubscriptionRefund && config.refundPolicy === 'until_expiry';\n\n // If the stored userId was a fallback (originalTransactionId) and we now\n // have a real appAccountToken, correct it. Never overwrite a userId that\n // the client set explicitly (which would differ from originalTransactionId).\n const correctedUserId =\n appAccountToken && existing.userId === originalTransactionId\n ? appAccountToken\n : existing.userId;\n if (correctedUserId !== existing.userId) {\n log.info('[onesub/webhook/apple] correcting userId from originalTransactionId to appAccountToken:', correctedUserId);\n }\n\n const updated: SubscriptionInfo = keepEntitlement\n ? { ...existing, userId: correctedUserId, willRenew: false }\n : {\n ...existing,\n userId: correctedUserId,\n status: finalStatus,\n willRenew,\n expiresAt: expiresAt ?? existing.expiresAt,\n };\n await store.save(updated);\n } else if (appleConfigForApp?.issuerId && appleConfigForApp?.keyId && appleConfigForApp?.privateKey) {\n const fresh = await fetchAppleSubscriptionStatus(originalTransactionId, appleConfigForApp, {\n sandbox: environment === 'Sandbox',\n });\n if (fresh) {\n fresh.userId = appAccountToken ?? originalTransactionId;\n if (inAppOwnershipType === 'FAMILY_SHARED') {\n const source = appAccountToken ? 'appAccountToken' : 'originalTransactionId (fallback)';\n log.info(`[onesub/webhook/apple] FAMILY_SHARED — userId: ${fresh.userId} (source: ${source})`);\n }\n await store.save(fresh);\n } else {\n log.warn(\n '[onesub/webhook/apple] Unknown transaction and Status API returned no record:',\n originalTransactionId,\n );\n }\n } else {\n log.warn(\n '[onesub/webhook/apple] Received notification for unknown transaction (no API creds to recover):',\n originalTransactionId,\n );\n }\n}\n\nexport async function handleAppleWebhook(\n req: Request,\n res: Response,\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n webhookEventStore?: WebhookEventStore,\n webhookQueue?: WebhookQueue,\n): Promise<void> {\n const body = req.body as { signedPayload?: string };\n\n if (!body.signedPayload) {\n sendError(res, 400, ONESUB_ERROR_CODE.MISSING_SIGNED_PAYLOAD, 'Missing signedPayload');\n return;\n }\n\n let payload: AppleNotificationPayload;\n try {\n payload = await decodeJws<AppleNotificationPayload>(\n body.signedPayload,\n config.apple?.skipJwsVerification,\n );\n } catch (err) {\n log.error('[onesub/webhook/apple] Failed to decode signedPayload:', err);\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_SIGNED_PAYLOAD, 'Invalid signedPayload');\n return;\n }\n\n // Idempotency gate. Runs BEFORE any enqueue, so in queue mode duplicate\n // deliveries never produce a second job (complemented by the queue-level\n // jobId dedup below). Capture the marked id once here — it is the only id\n // `unmarkWebhookEvent` may ever release.\n let markedEventId: string | undefined;\n if (webhookEventStore && typeof payload.notificationUUID === 'string') {\n const fresh = await webhookEventStore.markIfNew('apple', payload.notificationUUID);\n if (!fresh) {\n log.info('[onesub/webhook/apple] dedupe: already processed', payload.notificationUUID);\n res.status(200).json({ received: true, deduped: true });\n return;\n }\n markedEventId = payload.notificationUUID;\n }\n\n const decoded = await decodeAppleNotification(payload, config.apple?.skipJwsVerification);\n if (!decoded) {\n res.status(200).json({ received: true });\n return;\n }\n\n // Signature verification proves the notification came from Apple — not that it\n // is for an app we serve. Resolve the notification's bundleId against the app\n // registry, so a multi-app instance accepts every app it is configured for and\n // still rejects everything else.\n const notifiedApp = getAppRegistry(config).configFor({ bundleId: decoded.bundleId });\n if (decoded.bundleId && !notifiedApp.apple) {\n log.warn('[onesub/webhook/apple] No app configured for bundleId:', decoded.bundleId);\n sendError(res, 400, ONESUB_ERROR_CODE.BUNDLE_ID_MISMATCH, 'Bundle ID mismatch');\n return;\n }\n\n const work: AppleWebhookWork = {\n decoded,\n notificationType: payload.notificationType,\n subtype: payload.subtype,\n };\n\n if (webhookQueue) {\n // Queue mode: the cheap gating above ran inline; the state-mutating\n // processing runs in the queue handler. Ack as soon as the job is\n // durably accepted.\n //\n // Idempotency tradeoff: once enqueued, transient handler failures are the\n // QUEUE's retries (backoff + dead-letter), not the source's — so the\n // event stays marked even if the job ultimately fails. After max attempts\n // the job lands in the dead-letter list; admin replay\n // (POST /onesub/admin/webhook-replay/:id) is the recovery path. Unmarking\n // here would let a source retry race the queue's retries and double-apply.\n try {\n await webhookQueue.enqueue<AppleWebhookWork>({\n provider: 'apple',\n // Stable job id feeds queue-level dedup (BullMQ jobId). Random\n // fallback when Apple omits the UUID — no dedup possible anyway.\n eventId: typeof payload.notificationUUID === 'string' ? payload.notificationUUID : randomUUID(),\n payload: work,\n });\n res.status(200).json({ received: true, queued: true });\n } catch (err) {\n // Enqueue itself failed (e.g. Redis down) — the job was never durably\n // accepted, so the queue can't retry it. Fall back to source-retry\n // semantics: unmark + 5xx so Apple redelivers.\n log.error('[onesub/webhook/apple] Failed to enqueue webhook job:', err);\n await unmarkWebhookEvent(webhookEventStore, 'apple', markedEventId);\n sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, 'Failed to update subscription');\n }\n return;\n }\n\n // Inline mode (no queue configured): process synchronously inside the\n // request. On failure, un-mark the idempotency key so Apple's retry is\n // processed instead of being deduped — otherwise a transient store failure\n // drops the event forever.\n try {\n await processAppleNotification(work, config, store, purchaseStore);\n res.status(200).json({ received: true });\n } catch (err) {\n log.error('[onesub/webhook/apple] Store update error:', err);\n await unmarkWebhookEvent(webhookEventStore, 'apple', markedEventId);\n sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, 'Failed to update subscription');\n }\n}\n","import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { randomUUID } from 'node:crypto';\nimport type { Request, Response } from 'express';\nimport type { GoogleNotificationPayload, OneSubServerConfig, SubscriptionInfo } from '@onesub/shared';\nimport { SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { SubscriptionStore, PurchaseStore } from '../store.js';\nimport {\n decodeGoogleNotification,\n decodeGoogleVoidedNotification,\n decodeGoogleOneTimeProductNotification,\n validateGoogleReceipt,\n acknowledgeGoogleProduct,\n isGoogleActiveNotification,\n isGoogleCanceledNotification,\n isGoogleExpiredNotification,\n isGoogleGracePeriodNotification,\n isGoogleOnHoldNotification,\n isGooglePausedNotification,\n isGooglePriceChangeConfirmedNotification,\n} from '../providers/google.js';\nimport type {\n GoogleVoidedNotification,\n GoogleOneTimeProductNotification,\n} from '../providers/google.js';\nimport { log } from '../logger.js';\nimport { getAppRegistry } from '../apps.js';\nimport { sendError } from '../errors.js';\nimport type { WebhookEventStore } from '../webhook-events.js';\nimport { unmarkWebhookEvent } from '../webhook-events.js';\nimport type { WebhookQueue } from '../webhook-queue.js';\n\nconst GOOGLE_JWKS_URL = 'https://www.googleapis.com/oauth2/v3/certs';\n\nlet googleJwks: ReturnType<typeof createRemoteJWKSet> | null = null;\n\nfunction getGoogleJwks(): ReturnType<typeof createRemoteJWKSet> {\n if (!googleJwks) googleJwks = createRemoteJWKSet(new URL(GOOGLE_JWKS_URL));\n return googleJwks;\n}\n\nexport async function verifyGooglePushToken(\n req: Request,\n expectedAudience: string,\n expectedServiceAccountEmail?: string,\n): Promise<boolean> {\n const authHeader = req.headers['authorization'];\n if (typeof authHeader !== 'string' || !authHeader.startsWith('Bearer ')) return false;\n try {\n const { payload } = await jwtVerify(authHeader.slice('Bearer '.length).trim(), getGoogleJwks(), {\n audience: expectedAudience,\n // Any Google Cloud principal can mint an OIDC token with an arbitrary\n // audience — issuer (and, when configured, the push service-account\n // email) narrows acceptance to real Pub/Sub push auth tokens. Google\n // documents both issuer forms, so accept either.\n issuer: ['https://accounts.google.com', 'accounts.google.com'],\n });\n if (expectedServiceAccountEmail) {\n if (payload['email'] !== expectedServiceAccountEmail || payload['email_verified'] !== true) {\n return false;\n }\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/** Decoded subscription RTDN — inline shape returned by decodeGoogleNotification. */\ntype DecodedGoogleSubscriptionNotification = NonNullable<ReturnType<typeof decodeGoogleNotification>>;\n\n/**\n * The unit of work handed to the state-mutating processor. Deliberately\n * JSON-serializable (a discriminated union of plain decoded notification\n * objects) so it survives a round-trip through a Redis-backed queue (BullMQ)\n * unchanged.\n */\nexport type GoogleWebhookWork =\n | { kind: 'voided'; voided: GoogleVoidedNotification }\n | { kind: 'oneTimeProduct'; oneTimeProduct: GoogleOneTimeProductNotification }\n | { kind: 'subscription'; notification: DecodedGoogleSubscriptionNotification };\n\n/** Per-kind log prefixes / error messages, kept identical to the pre-refactor inline handlers. */\nconst GOOGLE_FAILURE_MESSAGES: Record<GoogleWebhookWork['kind'], { logPrefix: string; message: string }> = {\n voided: {\n logPrefix: '[onesub/webhook/google] voided notification error:',\n message: 'Failed to process voided notification',\n },\n oneTimeProduct: {\n logPrefix: '[onesub/webhook/google] oneTimeProduct error:',\n message: 'Failed to process oneTimeProduct notification',\n },\n subscription: {\n logPrefix: '[onesub/webhook/google] Error handling notification:',\n message: 'Failed to process notification',\n },\n};\n\n/**\n * Resolves an RTDN's package to an app.\n *\n * When no configured app declares a packageName the instance is in legacy \"open\n * mode\" — it accepts notifications for any package and uses the default\n * credentials. Once any app names a package, only known packages are served.\n */\nfunction googleResolver(config: OneSubServerConfig) {\n const registry = getAppRegistry(config);\n const restricted = registry.apps.some((app) => !!app.google?.packageName);\n return {\n registry,\n serves: (packageName: string): boolean =>\n !restricted || !!registry.configFor({ appId: packageName }).google,\n googleFor: (packageName: string) =>\n restricted\n ? registry.configFor({ appId: packageName }).google\n : (registry.defaultApp?.google ?? config.google),\n };\n}\n\n/**\n * State-mutating half of the Google webhook: everything AFTER the gate\n * (push-token auth, body validation, idempotency markIfNew, packageName\n * check, RTDN decode) has passed. Called from two places:\n * - inline by `handleGoogleWebhook` when no queue is configured\n * - by the webhook queue handler (registered in createWebhookRouter)\n * Throws on store failure — the caller decides the retry semantics (source\n * retry via 5xx inline, queue retry + dead-letter in queue mode).\n */\nexport async function processGoogleNotification(\n work: GoogleWebhookWork,\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Promise<void> {\n // Each notification names its own package; use that app's Google credentials\n // rather than whichever app happens to be the default.\n const { googleFor } = googleResolver(config);\n\n if (work.kind === 'voided') {\n const { voided } = work;\n if (voided.productType === 1) {\n const existing = await store.getByTransactionId(voided.purchaseToken);\n if (existing) {\n const updated = config.refundPolicy === 'until_expiry'\n ? { ...existing, willRenew: false }\n : { ...existing, status: SUBSCRIPTION_STATUS.CANCELED };\n await store.save(updated);\n } else {\n log.warn('[onesub/webhook/google] voided subscription for unknown purchaseToken:', voided.purchaseToken);\n }\n } else {\n const removed = await purchaseStore.deletePurchaseByTransactionId(voided.orderId);\n if (!removed) {\n log.warn('[onesub/webhook/google] voided IAP for unknown orderId:', voided.orderId);\n }\n }\n return;\n }\n\n // oneTimeProductNotification — consumable / non-consumable purchase signal.\n // The notification carries no userId, so we cannot create a purchase record\n // here (the client's POST /onesub/purchase/validate is the authoritative path).\n // For PURCHASED we acknowledge to prevent the 3-day auto-refund window.\n if (work.kind === 'oneTimeProduct') {\n const { notificationType, purchaseToken, sku } = work.oneTimeProduct;\n if (notificationType === 1 /* PURCHASED */) {\n log.info('[onesub/webhook/google] oneTimeProduct PURCHASED:', sku);\n const googleCfg = googleFor(work.oneTimeProduct.packageName);\n if (googleCfg?.serviceAccountKey && googleCfg.packageName) {\n void acknowledgeGoogleProduct(purchaseToken, sku, googleCfg).catch(\n (err) => log.warn(`[onesub/webhook/google] oneTimeProduct ack failed for SKU ${sku} — 3-day auto-refund risk:`, err),\n );\n }\n } else {\n // notificationType === 2: CANCELED (user aborted before purchase completed)\n log.info('[onesub/webhook/google] oneTimeProduct CANCELED (pre-completion):', sku);\n }\n return;\n }\n\n const { notificationType, purchaseToken, subscriptionId, packageName } = work.notification;\n\n const existing = await store.getByTransactionId(purchaseToken);\n\n let finalStatus: SubscriptionInfo['status'];\n if (isGoogleActiveNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.ACTIVE;\n } else if (isGoogleGracePeriodNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.GRACE_PERIOD;\n } else if (isGoogleOnHoldNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.ON_HOLD;\n } else if (isGooglePausedNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.PAUSED;\n } else if (isGooglePriceChangeConfirmedNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.ACTIVE;\n } else if (isGoogleCanceledNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.CANCELED;\n } else if (isGoogleExpiredNotification(notificationType)) {\n finalStatus = SUBSCRIPTION_STATUS.EXPIRED;\n } else {\n // Unknown/benign notification types (e.g. PAUSE_SCHEDULE_CHANGED,\n // DEFERRED) must not resurrect a canceled/expired record — preserve the\n // stored status; the re-fetch below corrects it when credentials exist.\n finalStatus = existing?.status ?? SUBSCRIPTION_STATUS.ACTIVE;\n }\n\n const subGoogleCfg = googleFor(packageName);\n\n if (isGooglePriceChangeConfirmedNotification(notificationType) && subGoogleCfg?.onPriceChangeConfirmed) {\n const hook = subGoogleCfg.onPriceChangeConfirmed;\n void Promise.resolve()\n .then(() => hook({ purchaseToken, subscriptionId, packageName }))\n .catch((err) => log.warn('[onesub/webhook/google] onPriceChangeConfirmed hook failed:', err));\n }\n\n if (existing) {\n let updated: SubscriptionInfo = { ...existing, status: finalStatus };\n\n if (subGoogleCfg?.serviceAccountKey) {\n const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId, subGoogleCfg);\n if (fresh) {\n // Preserve grace/on-hold only when the NOTIFICATION said so — a\n // finalStatus inherited from the stored record (unknown types above)\n // must not block the re-fetched status, or a lost recovery RTDN\n // leaves the record stuck on_hold while Google reports active.\n const preserveNotificationStatus =\n isGoogleGracePeriodNotification(notificationType) ||\n isGoogleOnHoldNotification(notificationType);\n updated = {\n ...existing,\n status: preserveNotificationStatus ? finalStatus : fresh.status,\n expiresAt: fresh.expiresAt,\n willRenew: fresh.willRenew,\n autoResumeTime: fresh.autoResumeTime,\n linkedPurchaseToken: fresh.linkedPurchaseToken ?? existing.linkedPurchaseToken,\n };\n }\n }\n\n await store.save(updated);\n } else {\n if (subGoogleCfg?.serviceAccountKey) {\n const fresh = await validateGoogleReceipt(purchaseToken, subscriptionId, subGoogleCfg);\n if (fresh) {\n // Consume the account identity out of the record: it seeds the\n // placeholder userId, but must never be persisted (validate route\n // strips it the same way; stores/status would leak it otherwise).\n const boundAccountId = fresh.boundAccountId;\n delete fresh.boundAccountId;\n if (fresh.linkedPurchaseToken) {\n const previous = await store.getByTransactionId(fresh.linkedPurchaseToken);\n fresh.userId = previous ? previous.userId : boundAccountId ?? purchaseToken;\n if (previous) {\n log.info(`[onesub/webhook/google] inherited userId ${previous.userId} from linkedPurchaseToken ${fresh.linkedPurchaseToken} → new token ${purchaseToken}`);\n }\n } else {\n fresh.userId = boundAccountId ?? purchaseToken;\n }\n await store.save(fresh);\n }\n } else {\n log.warn('[onesub/webhook/google] Unknown purchase token and no serviceAccountKey to re-fetch:', purchaseToken);\n }\n }\n}\n\nexport async function handleGoogleWebhook(\n req: Request,\n res: Response,\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n webhookEventStore?: WebhookEventStore,\n webhookQueue?: WebhookQueue,\n): Promise<void> {\n // Push auth runs before the payload is decoded, so the app is not known yet.\n // Each app pushes from its own GCP project (its own service account), so accept\n // the token when it verifies against any configured app's push identity.\n const pushIdentities = getAppRegistry(config)\n .apps.map((app) => app.google)\n .filter((g): g is NonNullable<typeof g> => !!g?.pushAudience);\n\n if (pushIdentities.length > 0) {\n let authenticated = false;\n for (const google of pushIdentities) {\n if (await verifyGooglePushToken(req, google.pushAudience!, google.pushServiceAccountEmail)) {\n authenticated = true;\n break;\n }\n }\n if (!authenticated) {\n sendError(res, 401, ONESUB_ERROR_CODE.UNAUTHORIZED, 'Unauthorized');\n return;\n }\n }\n\n const body = req.body as Partial<GoogleNotificationPayload>;\n\n if (!body.message?.data) {\n sendError(res, 400, ONESUB_ERROR_CODE.MISSING_MESSAGE_DATA, 'Missing message.data');\n return;\n }\n\n // Idempotency gate. Runs BEFORE any enqueue, so in queue mode duplicate\n // Pub/Sub deliveries never produce a second job (complemented by the\n // queue-level jobId dedup below). Capture the marked id once here — it is\n // the only id `unmarkWebhookEvent` may ever release.\n let markedEventId: string | undefined;\n if (webhookEventStore && typeof body.message.messageId === 'string') {\n const fresh = await webhookEventStore.markIfNew('google', body.message.messageId);\n if (!fresh) {\n log.info('[onesub/webhook/google] dedupe: already processed', body.message.messageId);\n res.status(200).json({ received: true, deduped: true });\n return;\n }\n markedEventId = body.message.messageId;\n }\n\n // Decode + packageName gating stays inline (cheap, and 4xx responses must\n // come from the request cycle — a queued job can't tell the source \"don't\n // retry this payload\").\n let work: GoogleWebhookWork;\n\n // The RTDN names its own package. Accept it when a configured app serves that\n // package, so a multi-app instance takes notifications for every app it knows\n // and still rejects the rest.\n const { serves: servesPackage } = googleResolver(config);\n\n const voided = decodeGoogleVoidedNotification(body as GoogleNotificationPayload);\n if (voided) {\n if (!servesPackage(voided.packageName)) {\n log.warn('[onesub/webhook/google] voided package name not served:', voided.packageName);\n sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, 'Package name mismatch');\n return;\n }\n work = { kind: 'voided', voided };\n } else {\n const oneTimeProduct = decodeGoogleOneTimeProductNotification(body as GoogleNotificationPayload);\n if (oneTimeProduct) {\n if (!servesPackage(oneTimeProduct.packageName)) {\n log.warn('[onesub/webhook/google] oneTimeProduct package name not served:', oneTimeProduct.packageName);\n sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, 'Package name mismatch');\n return;\n }\n work = { kind: 'oneTimeProduct', oneTimeProduct };\n } else {\n const notification = decodeGoogleNotification(body as GoogleNotificationPayload);\n if (!notification) {\n res.status(200).json({ received: true });\n return;\n }\n if (!servesPackage(notification.packageName)) {\n log.warn('[onesub/webhook/google] Package name not served:', notification.packageName);\n sendError(res, 400, ONESUB_ERROR_CODE.PACKAGE_NAME_MISMATCH, 'Package name mismatch');\n return;\n }\n work = { kind: 'subscription', notification };\n }\n }\n\n if (webhookQueue) {\n // Queue mode: the cheap gating above ran inline; the state-mutating\n // processing runs in the queue handler. Ack as soon as the job is\n // durably accepted.\n //\n // Idempotency tradeoff: once enqueued, transient handler failures are the\n // QUEUE's retries (backoff + dead-letter), not Pub/Sub's — so the event\n // stays marked even if the job ultimately fails. After max attempts the\n // job lands in the dead-letter list; admin replay\n // (POST /onesub/admin/webhook-replay/:id) is the recovery path. Unmarking\n // here would let a Pub/Sub retry race the queue's retries and double-apply.\n try {\n await webhookQueue.enqueue<GoogleWebhookWork>({\n provider: 'google',\n // Stable job id feeds queue-level dedup (BullMQ jobId). Random\n // fallback when the push lacks a messageId — no dedup possible anyway.\n eventId:\n typeof body.message.messageId === 'string' ? body.message.messageId : randomUUID(),\n payload: work,\n });\n res.status(200).json({ received: true, queued: true });\n } catch (err) {\n // Enqueue itself failed (e.g. Redis down) — the job was never durably\n // accepted, so the queue can't retry it. Fall back to source-retry\n // semantics: unmark + 5xx so Pub/Sub redelivers.\n log.error('[onesub/webhook/google] Failed to enqueue webhook job:', err);\n await unmarkWebhookEvent(webhookEventStore, 'google', markedEventId);\n sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, GOOGLE_FAILURE_MESSAGES[work.kind].message);\n }\n return;\n }\n\n // Inline mode (no queue configured): process synchronously inside the\n // request. On failure, un-mark the idempotency key so the Pub/Sub retry is\n // processed instead of being deduped — otherwise a transient store failure\n // drops the event forever.\n try {\n await processGoogleNotification(work, config, store, purchaseStore);\n res.status(200).json({ received: true });\n } catch (err) {\n const { logPrefix, message } = GOOGLE_FAILURE_MESSAGES[work.kind];\n log.error(logPrefix, err);\n await unmarkWebhookEvent(webhookEventStore, 'google', markedEventId);\n sendError(res, 500, ONESUB_ERROR_CODE.WEBHOOK_PROCESSING_FAILED, message);\n }\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport type { OneSubServerConfig } from '@onesub/shared';\nimport { ROUTES } from '@onesub/shared';\nimport type { SubscriptionStore, PurchaseStore } from '../store.js';\nimport type { WebhookEventStore } from '../webhook-events.js';\nimport type { WebhookQueue } from '../webhook-queue.js';\nimport { handleAppleWebhook, processAppleNotification } from './webhook-apple.js';\nimport type { AppleWebhookWork } from './webhook-apple.js';\nimport { handleGoogleWebhook, processGoogleNotification } from './webhook-google.js';\nimport type { GoogleWebhookWork } from './webhook-google.js';\n\n/**\n * Retry / durability semantics.\n *\n * Inline mode (no `webhookQueue`): both Apple and Google retry on any\n * non-2xx response.\n * 4xx — payload is unusable; do NOT retry.\n * 5xx — transient failure; source will retry.\n * 2xx — processed or intentionally ignored.\n *\n * Queue mode (`webhookQueue` configured): the route still owns every 4xx\n * (validation, auth, bundleId/packageName, dedup) inline, but 200s as soon as\n * the decoded work is enqueued. Processing failures are then the QUEUE's\n * retries; exhausted jobs land in the dead-letter list for admin replay.\n */\nexport function createWebhookRouter(\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n webhookEventStore?: WebhookEventStore,\n webhookQueue?: WebhookQueue,\n): Router {\n // Register the worker handler exactly once, at router creation — it closes\n // over config/stores so job payloads stay JSON-serializable (BullMQ ships\n // them through Redis; live objects would not survive the round-trip).\n if (webhookQueue) {\n webhookQueue.setHandler(async (job) => {\n if (job.provider === 'apple') {\n await processAppleNotification(job.payload as AppleWebhookWork, config, store, purchaseStore);\n } else {\n await processGoogleNotification(job.payload as GoogleWebhookWork, config, store, purchaseStore);\n }\n });\n }\n\n const router = Router();\n\n router.post(ROUTES.WEBHOOK_APPLE, (req: Request, res: Response) =>\n handleAppleWebhook(req, res, config, store, purchaseStore, webhookEventStore, webhookQueue),\n );\n\n router.post(ROUTES.WEBHOOK_GOOGLE, (req: Request, res: Response) =>\n handleGoogleWebhook(req, res, config, store, purchaseStore, webhookEventStore, webhookQueue),\n );\n\n return router;\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type {\n ValidatePurchaseResponse,\n PurchaseStatusResponse,\n PurchaseInfo,\n OneSubServerConfig,\n} from '@onesub/shared';\nimport { ROUTES, PURCHASE_TYPE, ONESUB_ERROR_CODE } from '@onesub/shared';\nimport type { PurchaseStore } from '../store.js';\nimport { getAppRegistry, peekAppleBundleId } from '../apps.js';\nimport { validateAppleConsumableReceipt } from '../providers/apple.js';\nimport {\n validateGoogleProductReceipt,\n consumeGoogleProductReceipt,\n acknowledgeGoogleProduct,\n} from '../providers/google.js';\nimport { log } from '../logger.js';\nimport { sendError, sendZodError } from '../errors.js';\n\nconst NO_PURCHASE = { valid: false, purchase: null } as const;\n\nconst validatePurchaseSchema = z.object({\n platform: z.enum(['apple', 'google']),\n receipt: z.string().min(1).max(10000),\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256),\n type: z.enum([PURCHASE_TYPE.CONSUMABLE, PURCHASE_TYPE.NON_CONSUMABLE]),\n /** Which app this receipt belongs to. Optional — see OneSubServerConfig.apps. */\n appId: z.string().min(1).max(256).optional(),\n});\n\nconst purchaseStatusQuerySchema = z.object({\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256).optional(),\n});\n\nexport function createPurchaseRouter(\n config: OneSubServerConfig,\n purchaseStore: PurchaseStore\n): Router {\n const router = Router();\n const registry = getAppRegistry(config);\n\n /**\n * POST /onesub/purchase/validate\n *\n * Validate a receipt for a consumable or non-consumable purchase.\n * - Non-consumables: rejected if the user already owns the product.\n * - Consumables: always recorded (multiple purchases allowed).\n *\n * Uses platform-specific product validators (purchases.products API) instead\n * of the subscription validator. Key differences:\n * - Apple: validates tx.type === 'Consumable'|'Non-Consumable', uses transactionId\n * (not originalTransactionId) as the dedup key, enforces 72h receipt age\n * - Google: checks consumptionState for consumables (replay prevention),\n * enforces 72h receipt age, uses orderId as the dedup key\n */\n router.post(ROUTES.VALIDATE_PURCHASE, async (req: Request, res: Response) => {\n let body: z.infer<typeof validatePurchaseSchema>;\n\n try {\n body = validatePurchaseSchema.parse(req.body);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err, NO_PURCHASE);\n return;\n }\n throw err;\n }\n\n const { platform, receipt, userId, productId, type, appId } = body;\n\n // An Apple receipt names its own app; a Google purchase token does not, so it\n // relies on appId (or the default app).\n const appConfig = registry.configFor({\n appId,\n bundleId: platform === 'apple' ? peekAppleBundleId(receipt) : undefined,\n });\n\n try {\n // Non-consumable idempotent restore. If the user already owns this\n // product, return the *recorded* purchase — with its real transactionId —\n // as a `restored` success instead of a 409 error.\n //\n // Owning a non-consumable IS success. The old behaviour (409 +\n // `purchase: null`) forced the SDK to synthesize a restored result with\n // no transactionId, so downstream consumers that re-entitle off\n // `result.transactionId` (e.g. a host app registering its own pass)\n // received `undefined` and silently failed — the user was charged but\n // never granted. Returning the stored record keeps the receipt chain\n // intact and is safe: ownership was already proven by the prior validated\n // purchase recorded under this userId (same idempotent-restore semantics\n // as the transactionId-match path below).\n if (type === PURCHASE_TYPE.NON_CONSUMABLE) {\n const owned = (await purchaseStore.getPurchasesByUserId(userId)).find(\n (p) => p.productId === productId,\n );\n if (owned) {\n const response: ValidatePurchaseResponse = {\n valid: true,\n purchase: { ...owned, userId },\n action: 'restored',\n };\n res.status(200).json(response);\n return;\n }\n }\n\n // Validate receipt via the appropriate platform-specific product validator.\n // Note: these are separate from the subscription validators — they call\n // purchases.products (not purchases.subscriptions) and apply product-specific\n // security checks (type validation, consumptionState, receipt age).\n let transactionId: string | null = null;\n let purchasedAt: string = new Date().toISOString();\n // Account binding token baked into the receipt at purchase time (Apple\n // appAccountToken / Google obfuscatedExternalAccountId). Null when the\n // client didn't set one (legacy purchases / apps not yet on the bound SDK).\n let boundAccountId: string | null = null;\n\n if (platform === 'apple') {\n if (!appConfig.apple) {\n sendError(res, 500, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, 'Apple configuration not provided', NO_PURCHASE);\n return;\n }\n const result = await validateAppleConsumableReceipt(receipt, appConfig.apple, productId);\n if (result) {\n transactionId = result.transactionId;\n purchasedAt = result.purchasedAt;\n boundAccountId = result.appAccountToken ?? null;\n }\n } else {\n if (!appConfig.google) {\n sendError(res, 500, ONESUB_ERROR_CODE.GOOGLE_CONFIG_MISSING, 'Google configuration not provided', NO_PURCHASE);\n return;\n }\n const result = await validateGoogleProductReceipt(\n receipt,\n productId,\n appConfig.google,\n type === PURCHASE_TYPE.CONSUMABLE ? 'consumable' : 'non_consumable',\n );\n if (result) {\n transactionId = result.transactionId;\n purchasedAt = result.purchasedAt;\n boundAccountId = result.obfuscatedExternalAccountId ?? null;\n }\n }\n\n if (!transactionId) {\n sendError(res, 422, ONESUB_ERROR_CODE.RECEIPT_VALIDATION_FAILED, 'Receipt validation failed', NO_PURCHASE);\n return;\n }\n\n // Account-binding guard (payment-bypass defense). If the receipt carries an\n // appAccountToken (a stable account id the client baked in at purchase time),\n // the purchase may ONLY be attributed to a matching userId. This blocks the\n // core exploit: a leaked/valid JWS being SAVED or REASSIGNED to an\n // attacker-chosen userId (both the fresh-save and reassign paths below trust\n // the request-body userId otherwise). Backward-compatible: purchases made\n // before the client set appAccountToken have no token → guard is skipped and\n // current behaviour (incl. reinstall reassignment) is unchanged.\n // Apple compares case-insensitively (appAccountToken is normalized to a\n // lowercase UUID in the signed transaction, so a host passing the same\n // UUID uppercased must not be rejected). Google ids are verbatim — a\n // case-insensitive compare would let a case-flipped userId through on\n // hosts with case-sensitive ids (e.g. Firebase UIDs).\n const bindingMismatch = platform === 'apple'\n ? boundAccountId && boundAccountId.toLowerCase() !== userId.toLowerCase()\n : boundAccountId && boundAccountId !== userId;\n if (bindingMismatch) {\n log.warn(\n `[onesub/purchase] account binding mismatch for transaction ${transactionId}: token does not match userId ${userId}`,\n );\n sendError(\n res,\n 409,\n ONESUB_ERROR_CODE.TRANSACTION_BELONGS_TO_OTHER_USER,\n 'TRANSACTION_BELONGS_TO_OTHER_USER',\n NO_PURCHASE,\n );\n return;\n }\n\n // Check if this transactionId has been seen before.\n // - Same userId → idempotent restore (e.g. network retry)\n // - Different userId + non_consumable → reassign (device reinstall)\n // - Different userId + consumable → reject (receipts can't be reused)\n // - No existing row → fresh purchase, INSERT below\n const existing = await purchaseStore.getPurchaseByTransactionId(transactionId);\n let action: 'new' | 'restored' = 'new';\n\n if (existing) {\n action = 'restored';\n if (existing.userId !== userId) {\n if (type === PURCHASE_TYPE.NON_CONSUMABLE) {\n await purchaseStore.reassignPurchase(transactionId, userId);\n log.info(\n `[onesub/purchase] reassigned transaction ${transactionId} from ${existing.userId} to ${userId}`,\n );\n } else {\n sendError(\n res,\n 409,\n ONESUB_ERROR_CODE.TRANSACTION_BELONGS_TO_OTHER_USER,\n 'TRANSACTION_BELONGS_TO_OTHER_USER',\n NO_PURCHASE,\n );\n return;\n }\n }\n const response: ValidatePurchaseResponse = {\n valid: true,\n purchase: { ...existing, userId },\n action,\n };\n res.status(200).json(response);\n return;\n }\n\n const purchase: PurchaseInfo = {\n userId,\n productId,\n platform,\n type,\n transactionId,\n purchasedAt,\n quantity: 1,\n };\n\n await purchaseStore.savePurchase(purchase);\n\n // Google requires acknowledgement within 3 days of purchase or the\n // transaction is auto-refunded.\n // - Consumables: :consume implicitly acknowledges, so consume is enough.\n // - Non-consumables: must call :acknowledge explicitly.\n // Both must run after savePurchase — if called before, a DB failure would\n // leave the receipt acknowledged/consumed but the entitlement ungranted\n // with no retry path.\n if (platform === 'google' && appConfig.google) {\n if (type === PURCHASE_TYPE.CONSUMABLE) {\n void consumeGoogleProductReceipt(receipt, productId, appConfig.google);\n } else {\n void acknowledgeGoogleProduct(receipt, productId, appConfig.google);\n }\n }\n\n const response: ValidatePurchaseResponse = {\n valid: true,\n purchase,\n action,\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/purchase/validate] Unexpected error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, 'Internal server error during purchase validation', NO_PURCHASE);\n }\n });\n\n /**\n * GET /onesub/purchase/status?userId=xxx[&productId=yyy]\n *\n * Returns all purchases for a user, optionally filtered by productId.\n */\n router.get(ROUTES.PURCHASE_STATUS, async (req: Request, res: Response) => {\n let query: z.infer<typeof purchaseStatusQuerySchema>;\n\n try {\n query = purchaseStatusQuerySchema.parse(req.query);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err, { purchases: [] });\n return;\n }\n throw err;\n }\n\n const { userId, productId } = query;\n\n try {\n let purchases = await purchaseStore.getPurchasesByUserId(userId);\n\n if (productId !== undefined) {\n purchases = purchases.filter((p) => p.productId === productId);\n }\n\n const response: PurchaseStatusResponse = { purchases };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/purchase/status] Store error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error', { purchases: [] });\n }\n });\n\n return router;\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type {\n Entitlement,\n EntitlementResponse,\n EntitlementStatus,\n EntitlementsConfig,\n EntitlementsResponse,\n OneSubServerConfig,\n PurchaseInfo,\n SubscriptionInfo,\n} from '@onesub/shared';\nimport { ROUTES, SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE, PURCHASE_TYPE } from '@onesub/shared';\nimport type { PurchaseStore, SubscriptionStore } from '../store.js';\nimport { log } from '../logger.js';\nimport { sendError } from '../errors.js';\n\n/**\n * Evaluate a single entitlement for a user.\n *\n * A user is entitled when EITHER condition holds for any productId in\n * `entitlement.productIds`:\n * 1. an active subscription (status === active|grace_period AND\n * expiresAt > now) for that productId exists\n * 2. a non-consumable purchase for that productId exists\n *\n * Consumables are intentionally excluded — they grant a one-time resource,\n * not an ongoing right.\n *\n * Subscription is preferred over purchase when both match (subs carry an\n * expiry; non-consumables are forever) so the source field skews toward the\n * more \"interesting\" signal for ops/analytics.\n */\nexport async function evaluateEntitlement(\n userId: string,\n entitlement: Entitlement,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Promise<EntitlementStatus> {\n const productIdSet = new Set(entitlement.productIds);\n const now = Date.now();\n\n // 1. Check subscriptions first (richer signal — has expiry).\n const subs = await store.getAllByUserId(userId);\n for (const sub of subs) {\n if (!productIdSet.has(sub.productId)) continue;\n const statusAllows =\n sub.status === SUBSCRIPTION_STATUS.ACTIVE ||\n sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD;\n if (!statusAllows) continue;\n if (new Date(sub.expiresAt).getTime() <= now) continue;\n return {\n active: true,\n source: 'subscription',\n productId: sub.productId,\n expiresAt: sub.expiresAt,\n };\n }\n\n // 2. Check non-consumable purchases.\n const purchases = await purchaseStore.getPurchasesByUserId(userId);\n for (const p of purchases) {\n if (p.type !== PURCHASE_TYPE.NON_CONSUMABLE) continue;\n if (!productIdSet.has(p.productId)) continue;\n return {\n active: true,\n source: 'purchase',\n productId: p.productId,\n };\n }\n\n return { active: false, source: null };\n}\n\nconst userIdSchema = z.string().min(1).max(256);\nconst entitlementIdSchema = z.string().min(1).max(128);\n\nexport function createEntitlementRouter(\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Router | null {\n // Only mount when entitlements are configured. Without this guard, the\n // routes would always 503 — better to make the absence loud (404 from\n // express) so misconfiguration is obvious during dev.\n if (!config.entitlements || Object.keys(config.entitlements).length === 0) {\n return null;\n }\n const entitlements: EntitlementsConfig = config.entitlements;\n\n const router = Router();\n\n /**\n * GET /onesub/entitlement?userId=&id=premium\n * Single entitlement check.\n */\n router.get(ROUTES.ENTITLEMENT, async (req: Request, res: Response) => {\n let userId: string;\n let id: string;\n try {\n userId = userIdSchema.parse(req.query['userId']);\n id = entitlementIdSchema.parse(req.query['id']);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId and id are required');\n return;\n }\n\n const entitlement = entitlements[id];\n if (!entitlement) {\n sendError(res, 404, ONESUB_ERROR_CODE.ENTITLEMENT_NOT_FOUND, `Unknown entitlement: ${id}`);\n return;\n }\n\n try {\n const status = await evaluateEntitlement(userId, entitlement, store, purchaseStore);\n const response: EntitlementResponse = { id, ...status };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/entitlement] evaluation error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n /**\n * GET /onesub/entitlements?userId=\n * Evaluate every configured entitlement in one round-trip — useful on\n * app launch / login when the host wants the full entitlement map.\n */\n router.get(ROUTES.ENTITLEMENTS, async (req: Request, res: Response) => {\n let userId: string;\n try {\n userId = userIdSchema.parse(req.query['userId']);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId is required');\n return;\n }\n\n try {\n const entries = await Promise.all(\n Object.entries(entitlements).map(async ([id, entitlement]) => {\n const status = await evaluateEntitlement(userId, entitlement, store, purchaseStore);\n return [id, status] as const;\n }),\n );\n const response: EntitlementsResponse = {\n entitlements: Object.fromEntries(entries),\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/entitlements] evaluation error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error', { entitlements: {} });\n }\n });\n\n return router;\n}\n\n// Keep the unused-type imports honest — these are exported as documentation\n// for callers writing their own evaluators.\nexport type { Entitlement, EntitlementStatus, PurchaseInfo, SubscriptionInfo };\n","import { timingSafeEqual } from 'node:crypto';\n\n/**\n * Constant-time comparison for shared secrets (admin secret, offer secret).\n *\n * A plain `!==` short-circuits on the first differing byte, letting an\n * attacker recover the secret byte-by-byte from response timing.\n * `crypto.timingSafeEqual` requires equal-length buffers, so a length\n * mismatch returns `false` immediately — this leaks only the secret's\n * LENGTH (not its content), which is an accepted trade-off.\n */\nexport function secretsEqual(provided: string, expected: string): boolean {\n const providedBuf = Buffer.from(provided, 'utf8');\n const expectedBuf = Buffer.from(expected, 'utf8');\n if (providedBuf.length !== expectedBuf.length) return false;\n return timingSafeEqual(providedBuf, expectedBuf);\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type {\n CustomerProfileResponse,\n EntitlementStatus,\n OneSubServerConfig,\n PurchaseInfo,\n ListSubscriptionsResponse,\n Platform,\n SubscriptionStatus,\n} from '@onesub/shared';\nimport { PURCHASE_TYPE, ONESUB_ERROR_CODE, ROUTES, SUBSCRIPTION_STATUS } from '@onesub/shared';\nimport type { PurchaseStore, SubscriptionStore } from '../store.js';\nimport { evaluateEntitlement } from './entitlements.js';\nimport { sendError, sendZodError } from '../errors.js';\nimport type { WebhookQueue } from '../webhook-queue.js';\nimport { fetchAppleSubscriptionStatus } from '../providers/apple.js';\nimport { secretsEqual } from './secret-compare.js';\n\nconst ADMIN_SECRET_HEADER = 'x-admin-secret';\n\n/**\n * Admin routes for testing / operational tasks. Require `X-Admin-Secret`\n * header matching config.adminSecret. If config.adminSecret is not set, the\n * entire admin router is not mounted (returns 404 from the parent router).\n *\n * Endpoints:\n * DELETE /onesub/purchase/admin/:userId/:productId\n * → reset a non-consumable so the user can test the purchase flow again\n *\n * POST /onesub/purchase/admin/grant\n * → manually insert a purchase record (skips store verification)\n * body: { userId, productId, platform, type?, transactionId? }\n *\n * POST /onesub/purchase/admin/transfer\n * → reassign a transactionId to a new userId (device migration)\n *\n * GET /onesub/admin/subscriptions?userId=&status=&productId=&platform=&limit=&offset=\n * → filtered/paginated subscription list (used by dashboard + scripts)\n *\n * GET /onesub/admin/subscriptions/:transactionId\n * → single subscription record by originalTransactionId (dashboard detail page)\n *\n * GET /onesub/admin/customers/:userId\n * → bundle of every record onesub knows for one user (subs + purchases +\n * entitlements when configured). Backs the dashboard customer detail page.\n */\nexport function createAdminRouter(\n config: OneSubServerConfig,\n purchaseStore: PurchaseStore,\n store: SubscriptionStore,\n webhookQueue?: WebhookQueue,\n): Router | null {\n if (!config.adminSecret) return null;\n\n const router = Router();\n const adminSecret = config.adminSecret;\n\n // Auth middleware — applied to both admin scopes. Without this guard, a\n // misconfigured mount on the parent root would expose admin endpoints\n // alongside host-app routes (e.g. /health).\n const adminAuth = (req: Request, res: Response, next: () => void) => {\n const provided = req.headers[ADMIN_SECRET_HEADER];\n if (typeof provided !== 'string' || !secretsEqual(provided, adminSecret)) {\n sendError(res, 401, ONESUB_ERROR_CODE.INVALID_ADMIN_SECRET, 'INVALID_ADMIN_SECRET');\n return;\n }\n next();\n };\n router.use('/onesub/purchase/admin', adminAuth);\n router.use('/onesub/admin', adminAuth);\n\n // DELETE /onesub/purchase/admin/:userId/:productId\n // Express 5 types route params as `string | string[]` — narrow via zod.\n const resetParamsSchema = z.object({\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256),\n });\n router.delete('/onesub/purchase/admin/:userId/:productId', async (req: Request, res: Response) => {\n let params;\n try {\n params = resetParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId and productId required');\n return;\n }\n const deleted = await purchaseStore.deletePurchases(params.userId, params.productId);\n res.json({ ok: true, deleted });\n });\n\n // POST /onesub/purchase/admin/transfer — reassign transactionId to a new userId\n // (legitimate device/account migration)\n const transferSchema = z.object({\n transactionId: z.string().min(1).max(256),\n newUserId: z.string().min(1).max(256),\n });\n router.post('/onesub/purchase/admin/transfer', async (req: Request, res: Response) => {\n let body;\n try {\n body = transferSchema.parse(req.body);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err);\n return;\n }\n throw err;\n }\n const existing = await purchaseStore.getPurchaseByTransactionId(body.transactionId);\n if (!existing) {\n sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'TRANSACTION_NOT_FOUND');\n return;\n }\n // Move only this transaction. deletePurchases(userId, productId) would\n // also destroy sibling consumable rows for the same product.\n const moved = await purchaseStore.reassignPurchase(body.transactionId, body.newUserId);\n if (!moved) {\n // Row vanished between the lookup above and the reassign (e.g. a\n // concurrent refund webhook deleted it) — don't report a false success.\n sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'TRANSACTION_NOT_FOUND');\n return;\n }\n const migrated: PurchaseInfo = { ...existing, userId: body.newUserId };\n res.json({ ok: true, purchase: migrated });\n });\n\n // POST /onesub/purchase/admin/grant\n const grantSchema = z.object({\n userId: z.string().min(1).max(256),\n productId: z.string().min(1).max(256),\n platform: z.enum(['apple', 'google']),\n type: z.enum([PURCHASE_TYPE.CONSUMABLE, PURCHASE_TYPE.NON_CONSUMABLE]).default(PURCHASE_TYPE.NON_CONSUMABLE),\n transactionId: z.string().min(1).max(256).optional(),\n });\n\n router.post('/onesub/purchase/admin/grant', async (req: Request, res: Response) => {\n let body;\n try {\n body = grantSchema.parse(req.body);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err);\n return;\n }\n throw err;\n }\n\n const transactionId = body.transactionId ?? `admin_grant_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;\n const purchase: PurchaseInfo = {\n transactionId,\n userId: body.userId,\n productId: body.productId,\n platform: body.platform,\n type: body.type,\n quantity: 1,\n purchasedAt: new Date().toISOString(),\n };\n await purchaseStore.savePurchase(purchase);\n res.json({ ok: true, purchase });\n });\n\n // GET /onesub/admin/subscriptions?userId=&status=&productId=&platform=&limit=&offset=\n // Filtered + paginated subscription list. Backs the dashboard's\n // subscriptions page and ad-hoc operational scripts.\n const listQuerySchema = z.object({\n userId: z.string().min(1).max(256).optional(),\n status: z.enum([\n SUBSCRIPTION_STATUS.ACTIVE,\n SUBSCRIPTION_STATUS.GRACE_PERIOD,\n SUBSCRIPTION_STATUS.ON_HOLD,\n SUBSCRIPTION_STATUS.PAUSED,\n SUBSCRIPTION_STATUS.EXPIRED,\n SUBSCRIPTION_STATUS.CANCELED,\n SUBSCRIPTION_STATUS.NONE,\n ] as [SubscriptionStatus, ...SubscriptionStatus[]]).optional(),\n productId: z.string().min(1).max(256).optional(),\n platform: z.enum(['apple', 'google'] as [Platform, ...Platform[]]).optional(),\n // Cap at 200 server-side so a runaway dashboard query can't tip the DB.\n limit: z.coerce.number().int().positive().max(200).optional(),\n offset: z.coerce.number().int().nonnegative().optional(),\n });\n\n router.get(ROUTES.ADMIN_SUBSCRIPTIONS, async (req: Request, res: Response) => {\n let query: z.infer<typeof listQuerySchema>;\n try {\n query = listQuerySchema.parse(req.query);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err);\n return;\n }\n throw err;\n }\n\n try {\n const result = await store.listFiltered(query);\n const response: ListSubscriptionsResponse = {\n items: result.items,\n total: result.total,\n limit: result.limit,\n offset: result.offset,\n };\n res.status(200).json(response);\n } catch (err) {\n // Bubble the message up but not the stack — admin clients log status code\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'list error');\n }\n });\n\n // GET /onesub/admin/subscriptions/:transactionId — single record by\n // originalTransactionId. Backs the dashboard's subscription detail page.\n const detailParamsSchema = z.object({\n transactionId: z.string().min(1).max(256),\n });\n router.get('/onesub/admin/subscriptions/:transactionId', async (req: Request, res: Response) => {\n let params;\n try {\n params = detailParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'transactionId required');\n return;\n }\n try {\n const sub = await store.getByTransactionId(params.transactionId);\n if (!sub) {\n sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'TRANSACTION_NOT_FOUND');\n return;\n }\n res.status(200).json(sub);\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'detail error');\n }\n });\n\n // GET /onesub/admin/customers/:userId — full per-user profile. Combines the\n // store calls a CS dashboard makes for \"show me everything about this user\"\n // into a single round-trip.\n //\n // Always returns 200 (even for unknown userIds) — empty arrays + no\n // entitlements means \"we have no record of this user\", which is a valid\n // signal for the dashboard to render rather than an error condition.\n const customerParamsSchema = z.object({\n userId: z.string().min(1).max(256),\n });\n router.get('/onesub/admin/customers/:userId', async (req: Request, res: Response) => {\n let params;\n try {\n params = customerParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId required');\n return;\n }\n try {\n const [subscriptions, purchases] = await Promise.all([\n store.getAllByUserId(params.userId),\n purchaseStore.getPurchasesByUserId(params.userId),\n ]);\n\n // Evaluate entitlements only when configured. Hosts that don't use the\n // entitlement abstraction get the field omitted (dashboard hides panel).\n let entitlements: Record<string, EntitlementStatus> | undefined;\n if (config.entitlements && Object.keys(config.entitlements).length > 0) {\n entitlements = {};\n for (const [id, def] of Object.entries(config.entitlements)) {\n entitlements[id] = await evaluateEntitlement(params.userId, def, store, purchaseStore);\n }\n }\n\n const response: CustomerProfileResponse = {\n userId: params.userId,\n subscriptions,\n purchases,\n ...(entitlements ? { entitlements } : {}),\n };\n res.status(200).json(response);\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'customer error');\n }\n });\n\n // POST /onesub/admin/sync-apple/:originalTransactionId\n // Fetch the current subscription state directly from Apple's Status API and\n // upsert into the local store. Useful for reconciliation / missed-webhook recovery.\n const syncAppleParamsSchema = z.object({\n originalTransactionId: z.string().min(1).max(256),\n });\n router.post(ROUTES.ADMIN_SYNC_APPLE, async (req: Request, res: Response) => {\n let params;\n try {\n params = syncAppleParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'originalTransactionId required');\n return;\n }\n\n if (!config.apple?.issuerId || !config.apple?.keyId || !config.apple?.privateKey) {\n sendError(res, 400, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, 'Apple API credentials not configured');\n return;\n }\n\n try {\n const { originalTransactionId } = params;\n // Allow callers to force sandbox mode; otherwise infer from the stored record.\n const existingForEnv = await store.getByTransactionId(originalTransactionId);\n const isSandbox =\n req.query['sandbox'] === 'true' ||\n (existingForEnv as (typeof existingForEnv & { environment?: string }) | null)?.environment === 'Sandbox';\n const fresh = await fetchAppleSubscriptionStatus(originalTransactionId, config.apple, { sandbox: isSandbox });\n if (!fresh) {\n sendError(res, 404, ONESUB_ERROR_CODE.TRANSACTION_NOT_FOUND, 'Apple Status API returned no record');\n return;\n }\n\n const existing = existingForEnv;\n fresh.userId = existing?.userId ?? originalTransactionId;\n await store.save(fresh);\n res.status(200).json({ ok: true, subscription: fresh });\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, (err as Error).message ?? 'sync error');\n }\n });\n\n // GET /onesub/admin/webhook-deadletters\n // POST /onesub/admin/webhook-replay/:id\n // Only mounted when the configured queue supports dead-letter inspection\n // (the default in-process queue does not — Apple/Google retries are the\n // durability layer for that mode).\n if (webhookQueue?.listDeadLetters) {\n router.get('/onesub/admin/webhook-deadletters', async (_req: Request, res: Response) => {\n try {\n const items = await webhookQueue.listDeadLetters!();\n res.status(200).json({ items });\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'list error');\n }\n });\n }\n\n if (webhookQueue?.replayDeadLetter) {\n const replayParamsSchema = z.object({ id: z.string().min(1).max(256) });\n router.post('/onesub/admin/webhook-replay/:id', async (req: Request, res: Response) => {\n let params;\n try {\n params = replayParamsSchema.parse(req.params);\n } catch {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'id required');\n return;\n }\n try {\n await webhookQueue.replayDeadLetter!(params.id);\n res.status(200).json({ ok: true });\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, (err as Error).message ?? 'replay error');\n }\n });\n }\n\n return router;\n}\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type {\n MetricsActiveResponse,\n MetricsBucket,\n MetricsCountResponse,\n OneSubServerConfig,\n PurchaseInfo,\n SubscriptionInfo,\n} from '@onesub/shared';\nimport {\n ROUTES,\n SUBSCRIPTION_STATUS,\n PURCHASE_TYPE,\n ONESUB_ERROR_CODE,\n} from '@onesub/shared';\nimport type { PurchaseStore, SubscriptionStore } from '../store.js';\nimport { log } from '../logger.js';\nimport { sendError } from '../errors.js';\nimport { secretsEqual } from './secret-compare.js';\n\nconst ADMIN_SECRET_HEADER = 'x-admin-secret';\n\n/**\n * Read-only aggregate metrics endpoints — gated behind `config.adminSecret`.\n * Returns count-based metrics only (active count, started count, expired count).\n *\n * Revenue metrics (MRR, ARR, LTV) require per-product price configuration the\n * server doesn't currently track; deferred to a follow-up release that adds\n * `config.products: { 'pro_monthly': { price: 9.99, currency: 'USD' } }`.\n *\n * Aggregation strategy: pulls every record via `store.listAll()` and reduces\n * in memory. Fine up to ~100k records; large deployments should optimise the\n * Postgres path with SQL aggregates (separate PR).\n */\nexport function createMetricsRouter(\n config: OneSubServerConfig,\n store: SubscriptionStore,\n purchaseStore: PurchaseStore,\n): Router | null {\n if (!config.adminSecret) return null;\n\n const router = Router();\n const adminSecret = config.adminSecret;\n\n // Auth middleware — only protects /onesub/metrics/* (siblings unaffected\n // even when this router is mounted on the parent root).\n router.use('/onesub/metrics', (req, res, next) => {\n const provided = req.headers[ADMIN_SECRET_HEADER];\n if (typeof provided !== 'string' || !secretsEqual(provided, adminSecret)) {\n sendError(res, 401, ONESUB_ERROR_CODE.INVALID_ADMIN_SECRET, 'INVALID_ADMIN_SECRET');\n return;\n }\n next();\n });\n\n // ── helpers ──────────────────────────────────────────────────────────────\n\n const isActiveSub = (sub: SubscriptionInfo, now: number): boolean => {\n const statusAllows =\n sub.status === SUBSCRIPTION_STATUS.ACTIVE ||\n sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD;\n return statusAllows && new Date(sub.expiresAt).getTime() > now;\n };\n\n const bump = (map: Record<string, number>, key: string) => {\n map[key] = (map[key] ?? 0) + 1;\n };\n\n // ── GET /onesub/metrics/active ───────────────────────────────────────────\n\n router.get(ROUTES.METRICS_ACTIVE, async (_req: Request, res: Response) => {\n try {\n const [subs, purchases] = await Promise.all([\n store.listAll(),\n purchaseStore.listAll(),\n ]);\n const now = Date.now();\n\n const byProduct: Record<string, number> = {};\n const byProductPurchases: Record<string, number> = {};\n const byPlatform: Record<string, number> = {};\n let activeSubscriptions = 0;\n let gracePeriodSubscriptions = 0;\n let nonConsumablePurchases = 0;\n\n for (const sub of subs) {\n if (!isActiveSub(sub, now)) continue;\n activeSubscriptions++;\n if (sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD) {\n gracePeriodSubscriptions++;\n }\n bump(byProduct, sub.productId);\n bump(byPlatform, sub.platform);\n }\n\n for (const p of purchases) {\n if (p.type !== PURCHASE_TYPE.NON_CONSUMABLE) continue;\n nonConsumablePurchases++;\n // byProductPurchases is purchase-only; byProduct (subs-only) stays clean\n // so dashboards can render distinct \"subscription mix\" vs \"lifetime mix\"\n // panels without untangling them client-side.\n bump(byProductPurchases, p.productId);\n bump(byPlatform, p.platform);\n }\n\n const response: MetricsActiveResponse = {\n total: activeSubscriptions + nonConsumablePurchases,\n activeSubscriptions,\n gracePeriodSubscriptions,\n nonConsumablePurchases,\n byProduct,\n byProductPurchases,\n byPlatform,\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/metrics/active] error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n // ── GET /onesub/metrics/started?from=&to=&groupBy= ───────────────────────\n\n const rangeSchema = z.object({\n from: z.string().min(1),\n to: z.string().min(1),\n groupBy: z.enum(['none', 'day']).optional(),\n });\n\n type Range = { fromMs: number; toMs: number; groupBy: 'none' | 'day' };\n\n // groupBy=day zero-fills one bucket object per calendar day, so an\n // unbounded range (e.g. from=0001-01-01) would allocate millions of\n // buckets from a single request. Cap the span at a year of daily buckets;\n // ranges without groupBy stay uncapped (they only produce totals).\n const MAX_DAY_BUCKET_SPAN_DAYS = 366;\n const MAX_DAY_BUCKET_SPAN_MS = MAX_DAY_BUCKET_SPAN_DAYS * 86_400_000;\n\n function parseRange(req: Request): Range | { error: string } {\n const parsed = rangeSchema.safeParse(req.query);\n if (!parsed.success) return { error: 'from and to are required (ISO 8601)' };\n const fromMs = new Date(parsed.data.from).getTime();\n const toMs = new Date(parsed.data.to).getTime();\n if (Number.isNaN(fromMs) || Number.isNaN(toMs)) {\n return { error: 'from / to must be ISO 8601 timestamps' };\n }\n if (fromMs > toMs) return { error: 'from must be ≤ to' };\n const groupBy = parsed.data.groupBy ?? 'none';\n if (groupBy === 'day' && toMs - fromMs > MAX_DAY_BUCKET_SPAN_MS) {\n return {\n error: `groupBy=day supports a range of at most ${MAX_DAY_BUCKET_SPAN_DAYS} days — narrow the from/to window or omit groupBy`,\n };\n }\n return { fromMs, toMs, groupBy };\n }\n\n // UTC `YYYY-MM-DD` for a given epoch-ms. Used to assign each record to a\n // calendar-day bucket; UTC keeps the result deterministic regardless of\n // server timezone (Postgres `updated_at` is UTC; SubscriptionInfo dates are\n // ISO with explicit zone offsets).\n function utcDateKey(ms: number): string {\n const d = new Date(ms);\n const yyyy = d.getUTCFullYear();\n const mm = String(d.getUTCMonth() + 1).padStart(2, '0');\n const dd = String(d.getUTCDate()).padStart(2, '0');\n return `${yyyy}-${mm}-${dd}`;\n }\n\n // Zero-fill a daily series across [fromMs, toMs] (inclusive of both\n // boundary days). The route handler then increments the counts.\n function emptyDailyBuckets(fromMs: number, toMs: number): MetricsBucket[] {\n const out: MetricsBucket[] = [];\n // Snap `from` to UTC midnight so iteration steps land on calendar boundaries.\n const start = new Date(fromMs);\n start.setUTCHours(0, 0, 0, 0);\n let cur = start.getTime();\n while (cur <= toMs) {\n out.push({ date: utcDateKey(cur), count: 0 });\n cur += 86_400_000;\n }\n return out;\n }\n\n router.get(ROUTES.METRICS_STARTED, async (req: Request, res: Response) => {\n const range = parseRange(req);\n if ('error' in range) {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, range.error);\n return;\n }\n try {\n const subs = await store.listAll();\n const byProduct: Record<string, number> = {};\n const byPlatform: Record<string, number> = {};\n let total = 0;\n\n // Build a date→index map only when bucketing is requested — keeps the\n // non-bucketed path identical to the previous implementation.\n const buckets =\n range.groupBy === 'day' ? emptyDailyBuckets(range.fromMs, range.toMs) : null;\n const bucketIndex =\n buckets ? new Map(buckets.map((b, i) => [b.date, i])) : null;\n\n for (const sub of subs) {\n const purchasedMs = new Date(sub.purchasedAt).getTime();\n if (purchasedMs < range.fromMs || purchasedMs > range.toMs) continue;\n total++;\n bump(byProduct, sub.productId);\n bump(byPlatform, sub.platform);\n if (buckets && bucketIndex) {\n const idx = bucketIndex.get(utcDateKey(purchasedMs));\n if (idx !== undefined) buckets[idx]!.count++;\n }\n }\n\n const response: MetricsCountResponse = {\n from: req.query['from'] as string,\n to: req.query['to'] as string,\n total,\n byProduct,\n byPlatform,\n ...(buckets ? { buckets } : {}),\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/metrics/started] error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n // ── GET /onesub/metrics/expired?from=&to= ────────────────────────────────\n\n router.get(ROUTES.METRICS_EXPIRED, async (req: Request, res: Response) => {\n const range = parseRange(req);\n if ('error' in range) {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, range.error);\n return;\n }\n try {\n const subs = await store.listAll();\n const byProduct: Record<string, number> = {};\n const byPlatform: Record<string, number> = {};\n let total = 0;\n\n const buckets =\n range.groupBy === 'day' ? emptyDailyBuckets(range.fromMs, range.toMs) : null;\n const bucketIndex =\n buckets ? new Map(buckets.map((b, i) => [b.date, i])) : null;\n\n for (const sub of subs) {\n // Counted only if currently expired or canceled — a record that's\n // still active doesn't qualify even if its expiresAt happened to fall\n // inside the window (e.g. mid-period billing renewal).\n if (\n sub.status !== SUBSCRIPTION_STATUS.EXPIRED &&\n sub.status !== SUBSCRIPTION_STATUS.CANCELED\n ) continue;\n const expiredMs = new Date(sub.expiresAt).getTime();\n if (expiredMs < range.fromMs || expiredMs > range.toMs) continue;\n total++;\n bump(byProduct, sub.productId);\n bump(byPlatform, sub.platform);\n if (buckets && bucketIndex) {\n const idx = bucketIndex.get(utcDateKey(expiredMs));\n if (idx !== undefined) buckets[idx]!.count++;\n }\n }\n\n const response: MetricsCountResponse = {\n from: req.query['from'] as string,\n to: req.query['to'] as string,\n total,\n byProduct,\n byPlatform,\n ...(buckets ? { buckets } : {}),\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/metrics/expired] error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n // ── GET /onesub/metrics/purchases/started?from=&to=&groupBy= ─────────────\n // Counts non-consumable purchases (lifetime products) by purchasedAt within\n // the window. Backs the dashboard's Purchases timeseries chart so hosts that\n // sell only lifetime products (e.g. coffee's Welcome Pass) get a meaningful\n // growth signal even when they have no subscription data.\n //\n // Consumables are excluded — they grant a one-time resource (coins, lives),\n // not an ongoing entitlement, and would dominate the count noisily.\n router.get(ROUTES.METRICS_PURCHASES_STARTED, async (req: Request, res: Response) => {\n const range = parseRange(req);\n if ('error' in range) {\n sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, range.error);\n return;\n }\n try {\n const purchases = await purchaseStore.listAll();\n const byProduct: Record<string, number> = {};\n const byPlatform: Record<string, number> = {};\n let total = 0;\n\n const buckets =\n range.groupBy === 'day' ? emptyDailyBuckets(range.fromMs, range.toMs) : null;\n const bucketIndex =\n buckets ? new Map(buckets.map((b, i) => [b.date, i])) : null;\n\n for (const p of purchases) {\n if (p.type !== PURCHASE_TYPE.NON_CONSUMABLE) continue;\n const purchasedMs = new Date(p.purchasedAt).getTime();\n if (purchasedMs < range.fromMs || purchasedMs > range.toMs) continue;\n total++;\n bump(byProduct, p.productId);\n bump(byPlatform, p.platform);\n if (buckets && bucketIndex) {\n const idx = bucketIndex.get(utcDateKey(purchasedMs));\n if (idx !== undefined) buckets[idx]!.count++;\n }\n }\n\n const response: MetricsCountResponse = {\n from: req.query['from'] as string,\n to: req.query['to'] as string,\n total,\n byProduct,\n byPlatform,\n ...(buckets ? { buckets } : {}),\n };\n res.status(200).json(response);\n } catch (err) {\n log.error('[onesub/metrics/purchases/started] error:', err);\n sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');\n }\n });\n\n return router;\n}\n\nexport type { PurchaseInfo, SubscriptionInfo };\n","import { Router } from 'express';\nimport type { Request, Response } from 'express';\nimport { z } from 'zod';\nimport type { OneSubServerConfig } from '@onesub/shared';\nimport { ONESUB_ERROR_CODE } from '@onesub/shared';\nimport { signApplePromotionalOffer } from '../providers/apple.js';\nimport { sendError, sendZodError } from '../errors.js';\nimport { secretsEqual } from './secret-compare.js';\n\nconst OFFER_SECRET_HEADER = 'x-onesub-offer-secret';\n\nconst offerBodySchema = z.object({\n productId: z.string().min(1).max(256),\n offerId: z.string().min(1).max(256),\n applicationUsername: z.string().min(1).max(256),\n});\n\n/**\n * POST /onesub/apple/offer-signature\n *\n * Sign an Apple Promotional Offer payload server-side.\n * Requires config.apple.offerKeyId and config.apple.offerPrivateKey.\n *\n * Authentication: if config.adminSecret is set, the request must carry\n * `X-Onesub-Offer-Secret: <adminSecret>` (same value). Hosts that mount\n * onesub without adminSecret are responsible for securing this endpoint\n * themselves (e.g. behind their own auth middleware).\n *\n * Body: { productId, offerId, applicationUsername }\n * Response: { keyId, nonce, timestamp, signature }\n *\n * The client passes these four values to StoreKit's\n * Product.SubscriptionOffer.Signature to redeem the offer.\n */\nexport function createAppleOfferRouter(config: OneSubServerConfig): Router | null {\n const apple = config.apple as (OneSubServerConfig['apple'] & { offerKeyId?: string; offerPrivateKey?: string }) | undefined;\n if (!apple?.offerKeyId || !apple?.offerPrivateKey) return null;\n\n const router = Router();\n\n router.post('/onesub/apple/offer-signature', async (req: Request, res: Response) => {\n if (config.adminSecret) {\n const provided = req.headers[OFFER_SECRET_HEADER];\n if (typeof provided !== 'string' || !secretsEqual(provided, config.adminSecret)) {\n sendError(res, 401, ONESUB_ERROR_CODE.UNAUTHORIZED, 'Unauthorized');\n return;\n }\n }\n let body: z.infer<typeof offerBodySchema>;\n try {\n body = offerBodySchema.parse(req.body);\n } catch (err) {\n if (err instanceof z.ZodError) {\n sendZodError(res, err);\n return;\n }\n throw err;\n }\n\n if (!apple.bundleId) {\n sendError(res, 400, ONESUB_ERROR_CODE.APPLE_CONFIG_MISSING, 'config.apple.bundleId is required for offer signing');\n return;\n }\n\n try {\n const result = await signApplePromotionalOffer(\n {\n bundleId: apple.bundleId,\n productId: body.productId,\n offerId: body.offerId,\n applicationUsername: body.applicationUsername,\n },\n apple,\n );\n res.status(200).json(result);\n } catch (err) {\n sendError(res, 500, ONESUB_ERROR_CODE.INTERNAL_ERROR, (err as Error).message ?? 'Offer signing failed');\n }\n });\n\n return router;\n}\n","/**\n * Embedded Postgres DDL mirrored from `packages/server/sql/schema.sql`.\n *\n * The `.sql` file is the single human-facing source of truth (mounted into\n * docker-entrypoint-initdb.d, runnable via `psql -f`, readable by DBAs).\n * These constants exist so that `initSchema()` can run without a filesystem\n * lookup after the package is bundled/installed.\n *\n * A test in `__tests__/schema.test.ts` asserts that the two stay in sync —\n * if you edit one, update the other (or the test will fail).\n */\n\nexport const SUBSCRIPTIONS_SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS onesub_subscriptions (\n original_transaction_id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL,\n product_id TEXT NOT NULL,\n platform TEXT NOT NULL,\n status TEXT NOT NULL,\n expires_at TIMESTAMPTZ NOT NULL,\n purchased_at TIMESTAMPTZ NOT NULL,\n will_renew BOOLEAN NOT NULL,\n linked_purchase_token TEXT,\n auto_resume_time TIMESTAMPTZ,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_user_id\n ON onesub_subscriptions (user_id, updated_at DESC);\n\n-- Filter-helper indexes for /onesub/admin/subscriptions. Each filter column\n-- is paired with updated_at DESC so the planner can serve \"latest matching\"\n-- without sorting the full table once row count grows.\nCREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_status_updated\n ON onesub_subscriptions (status, updated_at DESC);\nCREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_platform_updated\n ON onesub_subscriptions (platform, updated_at DESC);\nCREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_product\n ON onesub_subscriptions (product_id, updated_at DESC);\n\n-- Backfill columns for installs that already created the table from an older\n-- schema. Safe to re-run.\nALTER TABLE onesub_subscriptions ADD COLUMN IF NOT EXISTS linked_purchase_token TEXT;\nALTER TABLE onesub_subscriptions ADD COLUMN IF NOT EXISTS auto_resume_time TIMESTAMPTZ;\n`.trim();\n\nexport const PURCHASES_SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS onesub_purchases (\n transaction_id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL,\n product_id TEXT NOT NULL,\n platform TEXT NOT NULL,\n type TEXT NOT NULL,\n quantity INTEGER NOT NULL DEFAULT 1,\n purchased_at TIMESTAMPTZ NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE INDEX IF NOT EXISTS idx_onesub_purchases_user_id\n ON onesub_purchases (user_id, purchased_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_onesub_purchases_user_product\n ON onesub_purchases (user_id, product_id);\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_onesub_purchases_non_consumable\n ON onesub_purchases (user_id, product_id)\n WHERE type = 'non_consumable';\n`.trim();\n","import type { SubscriptionInfo, PurchaseInfo } from '@onesub/shared';\nimport type {\n SubscriptionStore,\n PurchaseStore,\n ListFilteredOptions,\n ListFilteredResult,\n} from '../store.js';\nimport { SUBSCRIPTIONS_SCHEMA_SQL, PURCHASES_SCHEMA_SQL } from './schema.js';\nimport { log } from '../logger.js';\n\n/**\n * Shared pool construction for both Postgres stores.\n *\n * `label` names the owning store in the missing-dependency error and in the\n * pool 'error' log so operators can tell which store hit the problem.\n */\nasync function createPgPool(connectionString: string, label: string): Promise<import('pg').Pool> {\n // Dynamic import so that `pg` is an optional peer dependency —\n // consumers who only use InMemorySubscriptionStore pay no cost.\n const pg = await import('pg').catch(() => {\n throw new Error(`[onesub] ${label} requires the \\`pg\\` package. Run: npm install pg`);\n });\n const Pool = pg.default?.Pool ?? (pg as unknown as { Pool: typeof import('pg').Pool }).Pool;\n const pool = new Pool({ connectionString, max: 10 });\n // node-postgres emits 'error' on the Pool when an idle client's backend\n // connection dies (e.g. DB failover / restart). Without a listener that's\n // an unhandled EventEmitter 'error' — it crashes the process. Log it and\n // let the pool replace the client.\n pool.on('error', (err) => {\n log.error(`[onesub] ${label} pool error (idle client):`, err);\n });\n return pool;\n}\n\n/**\n * PostgreSQL-backed subscription store.\n *\n * Uses raw `pg` (node-postgres) to avoid Prisma / ORM dependencies.\n * The `pg` package must be installed separately:\n *\n * npm install pg\n * npm install -D @types/pg\n *\n * Usage:\n * const store = new PostgresSubscriptionStore(process.env.DATABASE_URL!);\n * await store.initSchema();\n * app.use(createOneSubMiddleware({ ...config, store }));\n */\nexport class PostgresSubscriptionStore implements SubscriptionStore {\n // Lazy-loaded to avoid requiring `pg` unless this class is actually instantiated.\n private poolPromise: Promise<import('pg').Pool> | null = null;\n\n constructor(private readonly connectionString: string) {}\n\n private getPool(): Promise<import('pg').Pool> {\n if (!this.poolPromise) {\n this.poolPromise = createPgPool(this.connectionString, 'PostgresSubscriptionStore');\n }\n return this.poolPromise;\n }\n\n /**\n * Creates the `onesub_subscriptions` table and indexes if they do not\n * already exist. Call this once during application startup.\n */\n async initSchema(): Promise<void> {\n const pool = await this.getPool();\n // DDL mirrored in packages/server/sql/schema.sql — kept in sync by\n // schema parity test. Edit both (or only the .sql file and regenerate).\n await pool.query(SUBSCRIPTIONS_SCHEMA_SQL);\n }\n\n /**\n * Upserts the given subscription.\n * If a row with the same `original_transaction_id` already exists it is\n * updated in place; otherwise a new row is inserted.\n */\n async save(sub: SubscriptionInfo): Promise<void> {\n const pool = await this.getPool();\n await pool.query(\n `INSERT INTO onesub_subscriptions\n (original_transaction_id, user_id, product_id, platform, status,\n expires_at, purchased_at, will_renew, linked_purchase_token,\n auto_resume_time, updated_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW())\n ON CONFLICT (original_transaction_id) DO UPDATE SET\n user_id = EXCLUDED.user_id,\n product_id = EXCLUDED.product_id,\n platform = EXCLUDED.platform,\n status = EXCLUDED.status,\n expires_at = EXCLUDED.expires_at,\n purchased_at = EXCLUDED.purchased_at,\n will_renew = EXCLUDED.will_renew,\n linked_purchase_token = EXCLUDED.linked_purchase_token,\n auto_resume_time = EXCLUDED.auto_resume_time,\n updated_at = NOW()`,\n [\n sub.originalTransactionId,\n sub.userId,\n sub.productId,\n sub.platform,\n sub.status,\n sub.expiresAt,\n sub.purchasedAt,\n sub.willRenew,\n sub.linkedPurchaseToken ?? null,\n sub.autoResumeTime ?? null,\n ]\n );\n }\n\n /**\n * Returns the most recently updated subscription for the given user, or\n * `null` if no record exists.\n *\n * \"Most recent\" is determined by `updated_at DESC` so that the latest\n * status change is always returned when a user has multiple subscriptions.\n */\n async getByUserId(userId: string): Promise<SubscriptionInfo | null> {\n const pool = await this.getPool();\n const result = await pool.query<DbRow>(\n `SELECT *\n FROM onesub_subscriptions\n WHERE user_id = $1\n ORDER BY updated_at DESC\n LIMIT 1`,\n [userId]\n );\n return result.rows.length > 0 ? rowToSubscriptionInfo(result.rows[0]) : null;\n }\n\n /**\n * Returns every subscription record for the user (across all productIds),\n * ordered most-recent-first. Used by entitlement evaluation, which needs to\n * see all of a user's active subscriptions to decide whether any of them\n * grants the requested entitlement.\n */\n async getAllByUserId(userId: string): Promise<SubscriptionInfo[]> {\n const pool = await this.getPool();\n const result = await pool.query<DbRow>(\n `SELECT *\n FROM onesub_subscriptions\n WHERE user_id = $1\n ORDER BY updated_at DESC`,\n [userId]\n );\n return result.rows.map(rowToSubscriptionInfo);\n }\n\n /**\n * Returns the subscription identified by `originalTransactionId` (the\n * table primary key), or `null` if it does not exist.\n */\n async getByTransactionId(txId: string): Promise<SubscriptionInfo | null> {\n const pool = await this.getPool();\n const result = await pool.query<DbRow>(\n `SELECT *\n FROM onesub_subscriptions\n WHERE original_transaction_id = $1`,\n [txId]\n );\n return result.rows.length > 0 ? rowToSubscriptionInfo(result.rows[0]) : null;\n }\n\n async listAll(): Promise<SubscriptionInfo[]> {\n const pool = await this.getPool();\n const result = await pool.query<DbRow>(`SELECT * FROM onesub_subscriptions`);\n return result.rows.map(rowToSubscriptionInfo);\n }\n\n async listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult> {\n const pool = await this.getPool();\n const limit = opts.limit ?? 50;\n const offset = opts.offset ?? 0;\n\n // Build the WHERE clause incrementally. Each non-undefined filter adds an\n // AND condition with a parameterised value (no SQL injection surface).\n const conditions: string[] = [];\n const params: unknown[] = [];\n if (opts.userId) {\n params.push(opts.userId);\n conditions.push(`user_id = $${params.length}`);\n }\n if (opts.status) {\n params.push(opts.status);\n conditions.push(`status = $${params.length}`);\n }\n if (opts.productId) {\n params.push(opts.productId);\n conditions.push(`product_id = $${params.length}`);\n }\n if (opts.platform) {\n params.push(opts.platform);\n conditions.push(`platform = $${params.length}`);\n }\n const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';\n\n // Run the count + page in parallel — both queries hit the same WHERE clause\n // so they share the index plan, and we save a round-trip.\n const [countResult, pageResult] = await Promise.all([\n pool.query<{ total: string }>(\n `SELECT COUNT(*)::text AS total FROM onesub_subscriptions ${where}`,\n params,\n ),\n pool.query<DbRow>(\n `SELECT * FROM onesub_subscriptions ${where}\n ORDER BY updated_at DESC\n LIMIT $${params.length + 1} OFFSET $${params.length + 2}`,\n [...params, limit, offset],\n ),\n ]);\n\n return {\n items: pageResult.rows.map(rowToSubscriptionInfo),\n total: parseInt(countResult.rows[0]?.total ?? '0', 10),\n limit,\n offset,\n };\n }\n\n /** Gracefully close the underlying connection pool. */\n async close(): Promise<void> {\n if (this.poolPromise) {\n const pool = await this.poolPromise;\n await pool.end();\n this.poolPromise = null;\n }\n }\n}\n\n/**\n * PostgreSQL-backed purchase store for consumables and non-consumables.\n *\n * Uses raw `pg` (node-postgres). The `pg` package must be installed separately:\n *\n * npm install pg\n * npm install -D @types/pg\n *\n * Usage:\n * const store = new PostgresPurchaseStore(process.env.DATABASE_URL!);\n * await store.initSchema();\n * app.use(createOneSubMiddleware({ ...config, purchaseStore: store }));\n */\nexport class PostgresPurchaseStore implements PurchaseStore {\n private poolPromise: Promise<import('pg').Pool> | null = null;\n\n constructor(private readonly connectionString: string) {}\n\n private getPool(): Promise<import('pg').Pool> {\n if (!this.poolPromise) {\n this.poolPromise = createPgPool(this.connectionString, 'PostgresPurchaseStore');\n }\n return this.poolPromise;\n }\n\n /**\n * Creates the `onesub_purchases` table and indexes if they do not already exist.\n * Call this once during application startup.\n */\n async initSchema(): Promise<void> {\n const pool = await this.getPool();\n // DDL mirrored in packages/server/sql/schema.sql — kept in sync by\n // schema parity test.\n await pool.query(PURCHASES_SCHEMA_SQL);\n }\n\n async savePurchase(purchase: PurchaseInfo): Promise<void> {\n const pool = await this.getPool();\n // transactionId is PRIMARY KEY. If the same transactionId already belongs\n // to a different userId we refuse the save — the second call is either a\n // fraudulent receipt-reuse attempt or a legitimate device migration that\n // should go through an explicit admin transfer instead. Returning silently\n // for a different owner (a blanket ON CONFLICT DO NOTHING) would leave the\n // server reporting valid:true while the DB has no row for the current\n // user, which lets the caller keep re-\"buying\" forever.\n //\n // INSERT ... ON CONFLICT (transaction_id) DO NOTHING claims the row\n // atomically: under two concurrent saves of the same transactionId only\n // one INSERT wins, and the loser gets zero rows back instead of a raw\n // PK-violation error (which used to surface as a 500).\n const inserted = await pool.query(\n `INSERT INTO onesub_purchases\n (transaction_id, user_id, product_id, platform, type, quantity, purchased_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (transaction_id) DO NOTHING\n RETURNING transaction_id`,\n [\n purchase.transactionId,\n purchase.userId,\n purchase.productId,\n purchase.platform,\n purchase.type,\n purchase.quantity,\n purchase.purchasedAt,\n ],\n );\n if (inserted.rows.length > 0) return; // fresh row inserted — we own it\n\n // Row already existed — enforce ownership.\n const existing = await pool.query<{ user_id: string }>(\n `SELECT user_id FROM onesub_purchases WHERE transaction_id = $1`,\n [purchase.transactionId],\n );\n const owner = existing.rows[0]?.user_id;\n if (owner !== undefined && owner !== purchase.userId) {\n const err = new Error('TRANSACTION_BELONGS_TO_OTHER_USER') as Error & { code?: string };\n err.code = 'TRANSACTION_BELONGS_TO_OTHER_USER';\n throw err;\n }\n // same user — idempotent no-op (owner === undefined means the row was\n // deleted between the INSERT and the SELECT; treat as no-op as well)\n }\n\n async getPurchasesByUserId(userId: string): Promise<PurchaseInfo[]> {\n const pool = await this.getPool();\n const result = await pool.query<PurchaseDbRow>(\n `SELECT *\n FROM onesub_purchases\n WHERE user_id = $1\n ORDER BY purchased_at DESC`,\n [userId]\n );\n return result.rows.map(rowToPurchaseInfo);\n }\n\n async getPurchaseByTransactionId(txId: string): Promise<PurchaseInfo | null> {\n const pool = await this.getPool();\n const result = await pool.query<PurchaseDbRow>(\n `SELECT *\n FROM onesub_purchases\n WHERE transaction_id = $1`,\n [txId]\n );\n return result.rows.length > 0 ? rowToPurchaseInfo(result.rows[0]) : null;\n }\n\n async hasPurchased(userId: string, productId: string): Promise<boolean> {\n const pool = await this.getPool();\n const result = await pool.query<{ exists: boolean }>(\n `SELECT EXISTS(\n SELECT 1 FROM onesub_purchases\n WHERE user_id = $1 AND product_id = $2\n ) AS exists`,\n [userId, productId]\n );\n return result.rows[0]?.exists === true;\n }\n\n async reassignPurchase(transactionId: string, newUserId: string): Promise<boolean> {\n const pool = await this.getPool();\n const result = await pool.query(\n `UPDATE onesub_purchases SET user_id = $2 WHERE transaction_id = $1`,\n [transactionId, newUserId],\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n async deletePurchases(userId: string, productId: string): Promise<number> {\n const pool = await this.getPool();\n const result = await pool.query(\n `DELETE FROM onesub_purchases WHERE user_id = $1 AND product_id = $2`,\n [userId, productId]\n );\n return result.rowCount ?? 0;\n }\n\n async deletePurchaseByTransactionId(transactionId: string): Promise<boolean> {\n const pool = await this.getPool();\n const result = await pool.query(\n `DELETE FROM onesub_purchases WHERE transaction_id = $1`,\n [transactionId]\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n async listAll(): Promise<PurchaseInfo[]> {\n const pool = await this.getPool();\n const result = await pool.query<PurchaseDbRow>(`SELECT * FROM onesub_purchases`);\n return result.rows.map(rowToPurchaseInfo);\n }\n\n /** Gracefully close the underlying connection pool. */\n async close(): Promise<void> {\n if (this.poolPromise) {\n const pool = await this.poolPromise;\n await pool.end();\n this.poolPromise = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface DbRow {\n original_transaction_id: string;\n user_id: string;\n product_id: string;\n platform: string;\n status: string;\n expires_at: Date;\n purchased_at: Date;\n will_renew: boolean;\n linked_purchase_token: string | null;\n auto_resume_time: Date | null;\n}\n\nfunction rowToSubscriptionInfo(row: DbRow): SubscriptionInfo {\n return {\n originalTransactionId: row.original_transaction_id,\n userId: row.user_id,\n productId: row.product_id,\n platform: row.platform as SubscriptionInfo['platform'],\n status: row.status as SubscriptionInfo['status'],\n expiresAt: row.expires_at.toISOString(),\n purchasedAt: row.purchased_at.toISOString(),\n willRenew: row.will_renew,\n linkedPurchaseToken: row.linked_purchase_token ?? undefined,\n autoResumeTime: row.auto_resume_time?.toISOString(),\n };\n}\n\ninterface PurchaseDbRow {\n transaction_id: string;\n user_id: string;\n product_id: string;\n platform: string;\n type: string;\n quantity: number;\n purchased_at: Date;\n}\n\nfunction rowToPurchaseInfo(row: PurchaseDbRow): PurchaseInfo {\n return {\n transactionId: row.transaction_id,\n userId: row.user_id,\n productId: row.product_id,\n platform: row.platform as PurchaseInfo['platform'],\n type: row.type as PurchaseInfo['type'],\n quantity: row.quantity,\n purchasedAt: row.purchased_at.toISOString(),\n };\n}\n","import type { SubscriptionInfo, PurchaseInfo } from '@onesub/shared';\nimport type {\n SubscriptionStore,\n PurchaseStore,\n ListFilteredOptions,\n ListFilteredResult,\n} from '../store.js';\nimport type { CacheAdapter } from '../cache.js';\nimport type { WebhookEventStore } from '../webhook-events.js';\n\ntype IORedis = import('ioredis').Redis;\n\n/**\n * Redis-backed subscription / purchase / cache stores.\n *\n * Uses the `ioredis` package — kept as an optional peer dependency so callers\n * who only need InMemory or Postgres pay no install cost.\n *\n * npm install ioredis\n *\n * Usage:\n * import Redis from 'ioredis';\n * import {\n * RedisSubscriptionStore,\n * RedisPurchaseStore,\n * RedisCacheAdapter,\n * } from '@onesub/server';\n *\n * const redis = new Redis(process.env.REDIS_URL!);\n * const store = new RedisSubscriptionStore(redis);\n * const purchaseStore = new RedisPurchaseStore(redis);\n * const cache = new RedisCacheAdapter(redis);\n *\n * app.use(createOneSubMiddleware({ ...config, store, purchaseStore, cache }));\n *\n * Key layout:\n * onesub:sub:tx:<originalTransactionId> → JSON SubscriptionInfo\n * onesub:sub:owner:<originalTransactionId> → userId (cheap prev-owner lookup in save(); written in the same MULTI as the record)\n * onesub:sub:user:<userId> → SortedSet of originalTransactionIds, scored by updatedAt (ms)\n * onesub:sub:all:sorted → SortedSet of originalTransactionIds, scored by save time (for listAll/listFiltered)\n * onesub:purchase:tx:<transactionId> → JSON PurchaseInfo\n * onesub:purchase:user:<userId> → SortedSet of transactionIds, scored by purchasedAt (ms)\n * onesub:purchase:user_product:<u>:<p> → Set of transactionIds (for non-consumable hasPurchased)\n * onesub:purchase:all → Set of transactionIds\n * onesub:cache:<key> → string with TTL (RedisCacheAdapter)\n * onesub:webhook:event:<provider>:<id> → \"1\" with TTL (RedisWebhookEventStore)\n */\n\nconst SUB_TX_PREFIX = 'onesub:sub:tx:';\nconst SUB_OWNER_PREFIX = 'onesub:sub:owner:';\nconst SUB_USER_PREFIX = 'onesub:sub:user:';\n// Global sorted set (score = save timestamp ms) — enables ordered listAll and\n// O(log n + limit) fast-path pagination when no secondary filters are applied.\nconst SUB_ALL_SORTED = 'onesub:sub:all:sorted';\n\nconst PUR_TX_PREFIX = 'onesub:purchase:tx:';\nconst PUR_USER_PREFIX = 'onesub:purchase:user:';\nconst PUR_USER_PRODUCT_PREFIX = 'onesub:purchase:user_product:';\nconst PUR_ALL = 'onesub:purchase:all';\n\nexport class RedisSubscriptionStore implements SubscriptionStore {\n constructor(private readonly redis: IORedis) {}\n\n async save(sub: SubscriptionInfo): Promise<void> {\n const score = Date.now();\n const txKey = SUB_TX_PREFIX + sub.originalTransactionId;\n const ownerKey = SUB_OWNER_PREFIX + sub.originalTransactionId;\n const userKey = SUB_USER_PREFIX + sub.userId;\n\n // If this transaction was previously bound to a different userId (the\n // validate route rebinds ownership on re-validation), drop it from the\n // old user's index — otherwise both userIds would resolve the same live\n // subscription forever. The owner side-key keeps that check to a small\n // string GET on the hot path instead of a GET + JSON.parse of the full\n // record; records written before the side-key existed fall back to the\n // full record once and are backfilled by the MULTI below.\n let prevUserId: string | null = await this.redis.get(ownerKey);\n if (prevUserId === null) {\n const prevRaw = await this.redis.get(txKey);\n prevUserId = prevRaw ? (JSON.parse(prevRaw) as SubscriptionInfo).userId : null;\n }\n\n const pipeline = this.redis.multi();\n pipeline.set(txKey, JSON.stringify(sub));\n pipeline.set(ownerKey, sub.userId);\n if (prevUserId !== null && prevUserId !== sub.userId) {\n pipeline.zrem(SUB_USER_PREFIX + prevUserId, sub.originalTransactionId);\n }\n pipeline.zadd(userKey, score, sub.originalTransactionId);\n pipeline.zadd(SUB_ALL_SORTED, score, sub.originalTransactionId);\n await pipeline.exec();\n }\n\n async getByUserId(userId: string): Promise<SubscriptionInfo | null> {\n const userKey = SUB_USER_PREFIX + userId;\n // ZREVRANGE 0 0 = most-recent (highest score)\n const ids = await this.redis.zrevrange(userKey, 0, 0);\n if (ids.length === 0) return null;\n const raw = await this.redis.get(SUB_TX_PREFIX + ids[0]);\n return raw ? (JSON.parse(raw) as SubscriptionInfo) : null;\n }\n\n async getAllByUserId(userId: string): Promise<SubscriptionInfo[]> {\n const userKey = SUB_USER_PREFIX + userId;\n const ids = await this.redis.zrevrange(userKey, 0, -1);\n if (ids.length === 0) return [];\n const raws = await this.redis.mget(...ids.map((id) => SUB_TX_PREFIX + id));\n return raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as SubscriptionInfo);\n }\n\n async getByTransactionId(txId: string): Promise<SubscriptionInfo | null> {\n const raw = await this.redis.get(SUB_TX_PREFIX + txId);\n return raw ? (JSON.parse(raw) as SubscriptionInfo) : null;\n }\n\n async listAll(): Promise<SubscriptionInfo[]> {\n // ZREVRANGE on the sorted set gives newest-first order (vs random SMEMBERS).\n const ids = await this.redis.zrevrange(SUB_ALL_SORTED, 0, -1);\n if (ids.length === 0) return [];\n const raws = await this.redis.mget(...ids.map((id) => SUB_TX_PREFIX + id));\n return raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as SubscriptionInfo);\n }\n\n async listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult> {\n const limit = opts.limit ?? 50;\n const offset = opts.offset ?? 0;\n const hasSecondaryFilters = !!(opts.status || opts.productId || opts.platform);\n\n if (opts.userId) {\n // Per-user path — already O(log n) via per-user sorted set.\n const candidates = await this.getAllByUserId(opts.userId);\n const filtered = candidates.filter((s) => {\n if (opts.status && s.status !== opts.status) return false;\n if (opts.productId && s.productId !== opts.productId) return false;\n if (opts.platform && s.platform !== opts.platform) return false;\n return true;\n });\n return { items: filtered.slice(offset, offset + limit), total: filtered.length, limit, offset };\n }\n\n if (!hasSecondaryFilters) {\n // Fast path: pure pagination with no filter — O(log n + limit) via sorted set.\n const [ids, total] = await Promise.all([\n this.redis.zrevrange(SUB_ALL_SORTED, offset, offset + limit - 1),\n this.redis.zcard(SUB_ALL_SORTED),\n ]);\n if (ids.length === 0) return { items: [], total, limit, offset };\n const raws = await this.redis.mget(...ids.map((id) => SUB_TX_PREFIX + id));\n const items = raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as SubscriptionInfo);\n return { items, total, limit, offset };\n }\n\n // Slow path: secondary filters require full scan. Results come in\n // newest-first order. For >100k rows pair with Postgres or Redis Stack.\n const allIds = await this.redis.zrevrange(SUB_ALL_SORTED, 0, -1);\n if (allIds.length === 0) return { items: [], total: 0, limit, offset };\n const raws = await this.redis.mget(...allIds.map((id) => SUB_TX_PREFIX + id));\n const all = raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as SubscriptionInfo);\n const filtered = all.filter((s) => {\n if (opts.status && s.status !== opts.status) return false;\n if (opts.productId && s.productId !== opts.productId) return false;\n if (opts.platform && s.platform !== opts.platform) return false;\n return true;\n });\n return { items: filtered.slice(offset, offset + limit), total: filtered.length, limit, offset };\n }\n}\n\nexport class RedisPurchaseStore implements PurchaseStore {\n constructor(private readonly redis: IORedis) {}\n\n async savePurchase(purchase: PurchaseInfo): Promise<void> {\n const txKey = PUR_TX_PREFIX + purchase.transactionId;\n\n // Atomically claim the transaction key with SET NX — only the first\n // writer wins, which closes the GET-then-write race where two concurrent\n // saves with different userIds could both pass a read-side owner check\n // and both index the transaction (account-binding bypass).\n const claimed = await this.redis.set(txKey, JSON.stringify(purchase), 'NX');\n\n if (claimed !== 'OK') {\n // Key already exists — same TRANSACTION_BELONGS_TO_OTHER_USER semantics\n // as Postgres / InMemory implementations. Without this guard a stolen\n // receipt could be re-bound to a different account.\n const existing = await this.redis.get(txKey);\n const owner = existing ? (JSON.parse(existing) as PurchaseInfo).userId : null;\n if (owner !== null && owner !== purchase.userId) {\n const err = new Error('TRANSACTION_BELONGS_TO_OTHER_USER') as Error & { code?: string };\n err.code = 'TRANSACTION_BELONGS_TO_OTHER_USER';\n throw err;\n }\n // Same user: fall through to the index writes below instead of returning.\n // They are idempotent (zadd/sadd), and skipping them would make a crash\n // between the SET NX and the pipeline permanent — the tx key would exist\n // with no indexes and no retry could ever backfill them.\n }\n\n const score = Date.parse(purchase.purchasedAt) || Date.now();\n const pipeline = this.redis.multi();\n pipeline.zadd(PUR_USER_PREFIX + purchase.userId, score, purchase.transactionId);\n pipeline.sadd(PUR_USER_PRODUCT_PREFIX + purchase.userId + ':' + purchase.productId, purchase.transactionId);\n pipeline.sadd(PUR_ALL, purchase.transactionId);\n await pipeline.exec();\n }\n\n async getPurchasesByUserId(userId: string): Promise<PurchaseInfo[]> {\n const ids = await this.redis.zrevrange(PUR_USER_PREFIX + userId, 0, -1);\n if (ids.length === 0) return [];\n const raws = await this.redis.mget(...ids.map((id) => PUR_TX_PREFIX + id));\n return raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as PurchaseInfo);\n }\n\n async getPurchaseByTransactionId(txId: string): Promise<PurchaseInfo | null> {\n const raw = await this.redis.get(PUR_TX_PREFIX + txId);\n return raw ? (JSON.parse(raw) as PurchaseInfo) : null;\n }\n\n async hasPurchased(userId: string, productId: string): Promise<boolean> {\n const count = await this.redis.scard(PUR_USER_PRODUCT_PREFIX + userId + ':' + productId);\n return count > 0;\n }\n\n async reassignPurchase(transactionId: string, newUserId: string): Promise<boolean> {\n const txKey = PUR_TX_PREFIX + transactionId;\n const raw = await this.redis.get(txKey);\n if (!raw) return false;\n const existing = JSON.parse(raw) as PurchaseInfo;\n if (existing.userId === newUserId) return true;\n\n const updated: PurchaseInfo = { ...existing, userId: newUserId };\n const score = Date.parse(updated.purchasedAt) || Date.now();\n const oldUserProductKey = PUR_USER_PRODUCT_PREFIX + existing.userId + ':' + existing.productId;\n const newUserProductKey = PUR_USER_PRODUCT_PREFIX + newUserId + ':' + existing.productId;\n\n const pipeline = this.redis.multi();\n pipeline.set(txKey, JSON.stringify(updated));\n pipeline.zrem(PUR_USER_PREFIX + existing.userId, transactionId);\n pipeline.zadd(PUR_USER_PREFIX + newUserId, score, transactionId);\n pipeline.srem(oldUserProductKey, transactionId);\n pipeline.sadd(newUserProductKey, transactionId);\n await pipeline.exec();\n return true;\n }\n\n async deletePurchases(userId: string, productId: string): Promise<number> {\n const userProductKey = PUR_USER_PRODUCT_PREFIX + userId + ':' + productId;\n const ids = await this.redis.smembers(userProductKey);\n if (ids.length === 0) return 0;\n\n const pipeline = this.redis.multi();\n for (const id of ids) {\n pipeline.del(PUR_TX_PREFIX + id);\n pipeline.zrem(PUR_USER_PREFIX + userId, id);\n pipeline.srem(PUR_ALL, id);\n }\n pipeline.del(userProductKey);\n await pipeline.exec();\n return ids.length;\n }\n\n async deletePurchaseByTransactionId(transactionId: string): Promise<boolean> {\n const txKey = PUR_TX_PREFIX + transactionId;\n const raw = await this.redis.get(txKey);\n if (!raw) return false;\n const existing = JSON.parse(raw) as PurchaseInfo;\n\n const pipeline = this.redis.multi();\n pipeline.del(txKey);\n pipeline.zrem(PUR_USER_PREFIX + existing.userId, transactionId);\n pipeline.srem(PUR_USER_PRODUCT_PREFIX + existing.userId + ':' + existing.productId, transactionId);\n pipeline.srem(PUR_ALL, transactionId);\n await pipeline.exec();\n return true;\n }\n\n async listAll(): Promise<PurchaseInfo[]> {\n const ids = await this.redis.smembers(PUR_ALL);\n if (ids.length === 0) return [];\n const raws = await this.redis.mget(...ids.map((id) => PUR_TX_PREFIX + id));\n return raws.filter((r): r is string => r != null).map((r) => JSON.parse(r) as PurchaseInfo);\n }\n}\n\n/**\n * Redis-backed cache adapter — share Apple API JWT / Google OAuth tokens across cluster nodes.\n *\n * Implements `CacheAdapter` so it plugs into the same default-cache slot used\n * by the Apple JWT minter and Google OAuth token minter.\n */\nexport class RedisCacheAdapter implements CacheAdapter {\n constructor(\n private readonly redis: IORedis,\n private readonly prefix = 'onesub:cache:',\n ) {}\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const raw = await this.redis.get(this.prefix + key);\n return raw ? (JSON.parse(raw) as T) : null;\n }\n\n async set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void> {\n const fullKey = this.prefix + key;\n const payload = JSON.stringify(value);\n if (ttlSeconds && ttlSeconds > 0) {\n await this.redis.set(fullKey, payload, 'EX', ttlSeconds);\n } else {\n await this.redis.set(fullKey, payload);\n }\n }\n\n async del(key: string): Promise<void> {\n await this.redis.del(this.prefix + key);\n }\n}\n\nconst WEBHOOK_EVENT_PREFIX = 'onesub:webhook:event:';\nconst DEFAULT_WEBHOOK_TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days — covers Apple's 3-day retry window\n\n/**\n * Redis-backed webhook idempotency store.\n *\n * Uses `SET key '1' EX ttl NX` — a single atomic command that sets the key\n * only if it does not already exist. Returns 'OK' (new event) or null\n * (already seen), with no race between a GET and a subsequent SET.\n *\n * Prefer this over `CacheWebhookEventStore(new RedisCacheAdapter(...))` for\n * production deployments; the cache-based variant is non-atomic under\n * concurrent retries.\n */\nexport class RedisWebhookEventStore implements WebhookEventStore {\n constructor(\n private readonly redis: IORedis,\n private readonly ttlSeconds = DEFAULT_WEBHOOK_TTL_SECONDS,\n ) {}\n\n async markIfNew(provider: 'apple' | 'google', eventId: string): Promise<boolean> {\n const key = WEBHOOK_EVENT_PREFIX + provider + ':' + eventId;\n const result = await this.redis.set(key, '1', 'EX', this.ttlSeconds, 'NX');\n return result === 'OK';\n }\n\n async unmark(provider: 'apple' | 'google', eventId: string): Promise<void> {\n await this.redis.del(WEBHOOK_EVENT_PREFIX + provider + ':' + eventId);\n }\n}\n","import { log } from './logger.js';\n\n/**\n * Webhook processing queue.\n *\n * The default in-process queue runs the handler synchronously inside the HTTP\n * request — the same behavior you got before this interface existed. For\n * production deployments that want decoupled retries, swap in a queue-backed\n * implementation (BullMQ, SQS, etc.) so a slow DB or Apple/Google API call\n * doesn't block the webhook from acknowledging.\n *\n * Failure semantics:\n * - Handler throws → queue records the failure and (for retrying queues)\n * re-runs with backoff. After max attempts the job\n * lands in the dead-letter store.\n * - Handler returns → ack to source.\n *\n * The webhook routes ack (200) as soon as `enqueue()` resolves. For the\n * in-process queue that still means \"after the handler ran\" (enqueue runs it\n * synchronously); for BullMQ it means \"job persisted to Redis\" and processing\n * happens in the worker.\n */\nexport interface WebhookJob<T = unknown> {\n provider: 'apple' | 'google';\n /** Source-supplied event id (notificationUUID / messageId) for tracing. */\n eventId: string;\n payload: T;\n}\n\nexport type WebhookHandler<T = unknown> = (job: WebhookJob<T>) => Promise<void>;\n\nexport interface WebhookQueue {\n /**\n * Enqueue a job. Returns once the job is durably accepted (synchronous for\n * in-process; persisted-to-Redis for BullMQ).\n */\n enqueue<T>(job: WebhookJob<T>): Promise<void>;\n /** Register the worker handler. Called once during middleware setup. */\n setHandler<T>(handler: WebhookHandler<T>): void;\n /** Optional: list jobs in the dead-letter queue (for the admin replay UI). */\n listDeadLetters?(): Promise<DeadLetterRecord[]>;\n /** Optional: replay a specific dead-letter job back through the handler. */\n replayDeadLetter?(id: string): Promise<void>;\n /** Optional: graceful shutdown. */\n close?(): Promise<void>;\n}\n\nexport interface DeadLetterRecord {\n id: string;\n job: WebhookJob;\n attempts: number;\n lastError: string;\n failedAt: string;\n}\n\n/**\n * Synchronous, in-process implementation. Default — no extra infra required.\n * Handler runs inside the HTTP request, so route latency = handler latency.\n *\n * Failures are NOT retried — the original Apple/Google source retry policy\n * is the durability layer (4xx = no retry, 5xx = source retries). This\n * matches the pre-queue behavior.\n */\nexport class InProcessWebhookQueue implements WebhookQueue {\n private handler: WebhookHandler | null = null;\n\n setHandler<T>(handler: WebhookHandler<T>): void {\n this.handler = handler as WebhookHandler;\n }\n\n async enqueue<T>(job: WebhookJob<T>): Promise<void> {\n if (!this.handler) {\n throw new Error('[onesub] webhook queue has no handler registered');\n }\n await this.handler(job);\n }\n}\n\n/**\n * BullMQ-backed implementation — durable Redis queue with retries and a\n * dead-letter list. `bullmq` is an optional peer dependency; install it\n * separately:\n *\n * npm install bullmq ioredis\n *\n * Usage:\n * import { Queue, Worker } from 'bullmq';\n * import IORedis from 'ioredis';\n * import { BullMQWebhookQueue } from '@onesub/server';\n *\n * const connection = new IORedis(process.env.REDIS_URL!, { maxRetriesPerRequest: null });\n * const queue = new BullMQWebhookQueue({ connection });\n *\n * app.use(createOneSubMiddleware({ ...config, webhookQueue: queue }));\n *\n * Note: the worker is started inside `setHandler`. The webhook route\n * `enqueue`s and 200s immediately, so source latency is dominated by the\n * Redis round-trip, not the receipt-validation pipeline.\n */\n/**\n * `bullmq` is an optional peer — we don't depend on its types at compile\n * time. The minimal-shape interfaces below keep TS strict-mode happy\n * without forcing every onesub user to install bullmq just to type-check.\n */\ninterface BullMQJob {\n id: string | number | undefined;\n data: unknown;\n attemptsMade: number;\n failedReason?: string;\n finishedOn?: number;\n retry(): Promise<unknown>;\n}\ninterface BullMQQueue {\n add(name: string, data: unknown, opts?: unknown): Promise<unknown>;\n getJob(id: string): Promise<BullMQJob | undefined>;\n getFailed(): Promise<BullMQJob[]>;\n close(): Promise<void>;\n /** Queue extends EventEmitter — optional in the shape so test stubs stay minimal. */\n on?(event: string, listener: (...args: unknown[]) => void): unknown;\n}\ninterface BullMQWorker {\n close(): Promise<void>;\n /** Worker extends EventEmitter — optional in the shape so test stubs stay minimal. */\n on?(event: string, listener: (...args: unknown[]) => void): unknown;\n}\ninterface BullMQModule {\n Queue: new (name: string, opts: { connection: unknown }) => BullMQQueue;\n Worker: new (\n name: string,\n fn: (job: { data: unknown }) => Promise<void>,\n opts: { connection: unknown; concurrency?: number },\n ) => BullMQWorker;\n}\n\nexport interface BullMQWebhookQueueOptions {\n /** ioredis connection (or compatible options). Required. */\n connection: unknown;\n /** Queue name. Defaults to 'onesub-webhooks'. */\n queueName?: string;\n /** Max retry attempts before sending the job to the dead-letter list. */\n maxAttempts?: number;\n /** Backoff (ms) between attempts. Exponential: backoffMs * 2^(attempt-1). */\n backoffMs?: number;\n /** Worker concurrency. Defaults to 4. */\n concurrency?: number;\n}\n\nexport class BullMQWebhookQueue implements WebhookQueue {\n private queueName: string;\n private maxAttempts: number;\n private backoffMs: number;\n private concurrency: number;\n private connection: unknown;\n\n // Lazy-loaded so `bullmq` doesn't have to be installed unless this class\n // is instantiated.\n private queuePromise: Promise<BullMQQueue> | null = null;\n private workerPromise: Promise<BullMQWorker> | null = null;\n private handler: WebhookHandler | null = null;\n /**\n * Captured worker-startup failure (e.g. `bullmq` not installed). `setHandler`\n * is synchronous, so it can't reject — the error is surfaced on the next\n * `enqueue()` instead of becoming an unhandled rejection that kills the\n * process.\n */\n private workerStartupError: Error | null = null;\n\n constructor(opts: BullMQWebhookQueueOptions) {\n this.connection = opts.connection;\n this.queueName = opts.queueName ?? 'onesub-webhooks';\n this.maxAttempts = opts.maxAttempts ?? 5;\n this.backoffMs = opts.backoffMs ?? 1000;\n this.concurrency = opts.concurrency ?? 4;\n }\n\n private async getBullMQ(): Promise<BullMQModule> {\n return import('bullmq' as string).catch(() => {\n throw new Error('[onesub] BullMQWebhookQueue requires the `bullmq` package. Run: npm install bullmq');\n }) as Promise<BullMQModule>;\n }\n\n private getQueue(): Promise<BullMQQueue> {\n if (!this.queuePromise) {\n this.queuePromise = (async () => {\n const { Queue } = await this.getBullMQ();\n const queue = new Queue(this.queueName, { connection: this.connection });\n // Without an 'error' listener, a Redis hiccup on the EventEmitter\n // becomes an uncaught 'error' event and crashes the process.\n queue.on?.('error', (err) => {\n log.error('[onesub] BullMQ queue error:', err);\n });\n return queue;\n })();\n }\n return this.queuePromise;\n }\n\n setHandler<T>(handler: WebhookHandler<T>): void {\n this.handler = handler as WebhookHandler;\n if (!this.workerPromise) {\n this.workerPromise = (async () => {\n const { Worker } = await this.getBullMQ();\n const worker = new Worker(\n this.queueName,\n async (job) => {\n if (!this.handler) throw new Error('[onesub] handler not set');\n await this.handler(job.data as WebhookJob);\n },\n {\n connection: this.connection,\n concurrency: this.concurrency,\n },\n );\n // Same rationale as the queue's 'error' listener above. Job failures\n // are NOT this event — they go through the retry/dead-letter flow.\n worker.on?.('error', (err) => {\n log.error('[onesub] BullMQ worker error:', err);\n });\n return worker;\n })();\n // The worker starts in the background — swallow the rejection here\n // (otherwise: unhandled rejection → process crash when bullmq is\n // missing) and surface it on the next enqueue instead.\n this.workerPromise.catch((err) => {\n this.workerStartupError = err instanceof Error ? err : new Error(String(err));\n log.error('[onesub] BullMQ worker failed to start:', err);\n });\n }\n }\n\n async enqueue<T>(job: WebhookJob<T>): Promise<void> {\n if (this.workerStartupError) throw this.workerStartupError;\n const queue = await this.getQueue();\n await queue.add('webhook', job, {\n attempts: this.maxAttempts,\n backoff: { type: 'exponential', delay: this.backoffMs },\n removeOnFail: false,\n removeOnComplete: { age: 24 * 60 * 60, count: 1000 },\n jobId: `${job.provider}:${job.eventId}`,\n });\n }\n\n async listDeadLetters(): Promise<DeadLetterRecord[]> {\n const queue = await this.getQueue();\n const failed = await queue.getFailed();\n return failed.map((j) => ({\n id: String(j.id),\n job: j.data as WebhookJob,\n attempts: j.attemptsMade,\n lastError: j.failedReason ?? 'unknown',\n failedAt: new Date(j.finishedOn ?? Date.now()).toISOString(),\n }));\n }\n\n async replayDeadLetter(id: string): Promise<void> {\n const queue = await this.getQueue();\n const job = await queue.getJob(id);\n if (!job) throw new Error(`[onesub] dead-letter job ${id} not found`);\n await job.retry();\n }\n\n async close(): Promise<void> {\n if (this.workerPromise) {\n // A rejected workerPromise (startup failure) has nothing to close.\n const worker = await this.workerPromise.catch(() => null);\n if (worker) await worker.close();\n }\n if (this.queuePromise) {\n const queue = await this.queuePromise.catch(() => null);\n if (queue) await queue.close();\n }\n }\n}\n","/**\n * OpenAPI 3.1 spec for the onesub HTTP surface.\n *\n * Hand-maintained — kept in sync with the routes by a parity test (see\n * __tests__/openapi.test.ts) that mounts every router via\n * `createOneSubMiddleware`, walks the express router stack, and asserts every\n * mounted path+method is described here (and vice versa — no stale spec\n * entries). Generated clients live downstream:\n *\n * - swagger-typescript-api → typed fetch client\n * - openapi-generator-cli → Kotlin/Swift/etc. clients\n *\n * Why hand-maintained instead of zod-to-openapi: the routes use zod for\n * input validation but the request/response shapes the public docs need are\n * a thin slice of those internal types. Hand-writing keeps the doc readable\n * without coupling client output to internal validation strictness changes.\n */\n\nimport { ONESUB_ERROR_CODE, ROUTES } from '@onesub/shared';\n\nexport interface OpenAPIDoc {\n openapi: '3.1.0';\n info: {\n title: string;\n version: string;\n description: string;\n };\n servers: Array<{ url: string; description?: string }>;\n paths: Record<string, unknown>;\n components: {\n schemas: Record<string, unknown>;\n securitySchemes: Record<string, unknown>;\n };\n}\n\n// ── shared fragments ─────────────────────────────────────────────────────────\n// Every onesub 4xx/5xx goes through `sendError` (errors.ts) and carries\n// `{ error, errorCode }`; some routes append shape-compat fields such as\n// `valid: false` / `subscription: null`.\n\nconst ERROR_CONTENT = {\n 'application/json': { schema: { $ref: '#/components/schemas/ErrorResponse' } },\n};\n\n/** Error-response entry: description + the canonical `{ error, errorCode }` body. */\nconst err = (description: string) => ({ description, content: ERROR_CONTENT });\n\n/** `X-Admin-Secret` header — required on every admin + metrics route. */\nconst ADMIN_SECRET_PARAM = {\n in: 'header',\n name: 'X-Admin-Secret',\n required: true,\n schema: { type: 'string' },\n};\n\n/** Shared `from`/`to`/`groupBy` query params for windowed metrics endpoints. */\nconst METRICS_RANGE_PARAMS = [\n { in: 'query', name: 'from', required: true, schema: { type: 'string', format: 'date-time' }, description: 'Window start (ISO 8601).' },\n { in: 'query', name: 'to', required: true, schema: { type: 'string', format: 'date-time' }, description: 'Window end (ISO 8601).' },\n { in: 'query', name: 'groupBy', schema: { type: 'string', enum: ['none', 'day'] }, description: \"'day' adds a zero-filled daily `buckets` series to the response.\" },\n];\n\nexport const ONESUB_OPENAPI: OpenAPIDoc = {\n openapi: '3.1.0',\n info: {\n title: 'onesub HTTP API',\n version: '1.0.0',\n description:\n 'Receipt validation, subscription status, one-time purchases, entitlements, webhooks, metrics, and admin endpoints exposed by `createOneSubMiddleware`.',\n },\n servers: [\n { url: 'http://localhost:4100', description: 'Local dev (examples/server)' },\n ],\n paths: {\n // ── public: subscriptions ────────────────────────────────────────────────\n [ROUTES.VALIDATE]: {\n post: {\n summary: 'Validate an Apple/Google receipt and persist subscription state.',\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { $ref: '#/components/schemas/ValidateRequest' } } },\n },\n responses: {\n 200: { description: 'Validation succeeded; subscription state persisted.', content: { 'application/json': { schema: { $ref: '#/components/schemas/ValidateResponse' } } } },\n 400: err('Bad request — missing fields, malformed receipt, package mismatch.'),\n 409: err('Receipt is account-bound to a different userId (TRANSACTION_BELONGS_TO_OTHER_USER).'),\n 422: err('Receipt validation failed (RECEIPT_VALIDATION_FAILED).'),\n },\n },\n },\n [ROUTES.STATUS]: {\n get: {\n summary: 'Fetch the most recent subscription state for a user.',\n parameters: [{ in: 'query', name: 'userId', required: true, schema: { type: 'string' } }],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/StatusResponse' } } } },\n 400: err('Missing userId or userId too long (INVALID_INPUT / USER_ID_TOO_LONG).'),\n },\n },\n },\n // ── public: one-time purchases ───────────────────────────────────────────\n [ROUTES.VALIDATE_PURCHASE]: {\n post: {\n summary: 'Validate a consumable / non-consumable receipt and record the purchase.',\n description:\n 'Non-consumables are idempotent: an already-owned product returns the stored purchase with `action: \"restored\"`. Consumable receipts can only be redeemed once.',\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { $ref: '#/components/schemas/ValidatePurchaseRequest' } } },\n },\n responses: {\n 200: { description: 'Purchase validated — freshly recorded (`action: \"new\"`) or restored.', content: { 'application/json': { schema: { $ref: '#/components/schemas/ValidatePurchaseResponse' } } } },\n 400: err('Bad request — missing/invalid fields (INVALID_INPUT).'),\n 409: err('Receipt is account-bound or already redeemed by another user (TRANSACTION_BELONGS_TO_OTHER_USER).'),\n 422: err('Receipt validation failed (RECEIPT_VALIDATION_FAILED).'),\n },\n },\n },\n [ROUTES.PURCHASE_STATUS]: {\n get: {\n summary: 'List all recorded purchases for a user, optionally filtered by product.',\n parameters: [\n { in: 'query', name: 'userId', required: true, schema: { type: 'string' } },\n { in: 'query', name: 'productId', schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/PurchaseStatusResponse' } } } },\n 400: err('Missing/invalid userId (INVALID_INPUT).'),\n },\n },\n },\n // ── webhooks ─────────────────────────────────────────────────────────────\n [ROUTES.WEBHOOK_APPLE]: {\n post: {\n summary: 'Apple App Store Server Notification V2 receiver.',\n description:\n 'Apple POSTs `{ signedPayload }`. JWS-verified, idempotent when `webhookEventStore` is configured.',\n responses: {\n 200: { description: 'Acknowledged.' },\n 400: err('Missing or invalid signedPayload.'),\n },\n },\n },\n [ROUTES.WEBHOOK_GOOGLE]: {\n post: {\n summary: 'Google Play Real-Time Developer Notification (Pub/Sub push) receiver.',\n responses: {\n 200: { description: 'Acknowledged.' },\n 400: err('Missing message.data or package mismatch.'),\n 401: err('pushAudience configured and JWT verification failed.'),\n },\n },\n },\n // ── entitlements (mounted when config.entitlements is set) ──────────────\n [ROUTES.ENTITLEMENT]: {\n get: {\n summary: 'Evaluate a single configured entitlement for a user.',\n parameters: [\n { in: 'query', name: 'userId', required: true, schema: { type: 'string' } },\n { in: 'query', name: 'id', required: true, schema: { type: 'string' }, description: 'Entitlement id from `config.entitlements`.' },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/EntitlementResponse' } } } },\n 400: err('userId and id are required (INVALID_INPUT).'),\n 404: err('Unknown entitlement id (ENTITLEMENT_NOT_FOUND).'),\n },\n },\n },\n [ROUTES.ENTITLEMENTS]: {\n get: {\n summary: 'Evaluate every configured entitlement for a user.',\n parameters: [{ in: 'query', name: 'userId', required: true, schema: { type: 'string' } }],\n responses: {\n 200: { description: 'Map of entitlementId → EntitlementStatus.', content: { 'application/json': { schema: { $ref: '#/components/schemas/EntitlementsResponse' } } } },\n 400: err('userId is required (INVALID_INPUT).'),\n },\n },\n },\n // ── admin (mounted when config.adminSecret is set) ───────────────────────\n [ROUTES.ADMIN_SUBSCRIPTIONS]: {\n get: {\n summary: 'Filtered, paginated subscription list (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'query', name: 'userId', schema: { type: 'string' } },\n { in: 'query', name: 'status', schema: { type: 'string', enum: ['active', 'grace_period', 'on_hold', 'paused', 'expired', 'canceled', 'none'] } },\n { in: 'query', name: 'productId', schema: { type: 'string' } },\n { in: 'query', name: 'platform', schema: { type: 'string', enum: ['apple', 'google'] } },\n { in: 'query', name: 'limit', schema: { type: 'integer', minimum: 1, maximum: 200 } },\n { in: 'query', name: 'offset', schema: { type: 'integer', minimum: 0 } },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/ListSubscriptionsResponse' } } } },\n 400: err('Invalid filter/pagination params (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/admin/subscriptions/{transactionId}': {\n get: {\n summary: 'Single subscription record by originalTransactionId (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'transactionId', required: true, schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/SubscriptionInfo' } } } },\n 400: err('transactionId required (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n 404: err('No record for this transactionId (TRANSACTION_NOT_FOUND).'),\n },\n },\n },\n '/onesub/admin/customers/{userId}': {\n get: {\n summary: 'Customer profile bundle: subscriptions + purchases + entitlements (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'userId', required: true, schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK — empty arrays mean \"no record of this user\" (never 404).', content: { 'application/json': { schema: { $ref: '#/components/schemas/CustomerProfileResponse' } } } },\n 400: err('userId required (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/admin/sync-apple/{originalTransactionId}': {\n post: {\n summary: 'Reconcile one subscription from the Apple Status API into the local store (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'originalTransactionId', required: true, schema: { type: 'string' } },\n { in: 'query', name: 'sandbox', schema: { type: 'string', enum: ['true'] }, description: 'Force the sandbox Status API host (otherwise inferred from the stored record).' },\n ],\n responses: {\n 200: { description: 'Fresh state fetched and upserted.', content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' }, subscription: { $ref: '#/components/schemas/SubscriptionInfo' } } } } } },\n 400: err('originalTransactionId missing or Apple API credentials not configured (INVALID_INPUT / APPLE_CONFIG_MISSING).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n 404: err('Apple Status API returned no record (TRANSACTION_NOT_FOUND).'),\n },\n },\n },\n '/onesub/purchase/admin/{userId}/{productId}': {\n delete: {\n summary: 'Reset a purchase so the user can test the flow again (admin).',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'userId', required: true, schema: { type: 'string' } },\n { in: 'path', name: 'productId', required: true, schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' }, deleted: { type: 'integer', description: 'Number of purchase rows removed.' } } } } } },\n 400: err('userId and productId required (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/purchase/admin/transfer': {\n post: {\n summary: 'Reassign a transactionId to a new userId — device/account migration (admin).',\n parameters: [ADMIN_SECRET_PARAM],\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { type: 'object', required: ['transactionId', 'newUserId'], properties: { transactionId: { type: 'string' }, newUserId: { type: 'string' } } } } },\n },\n responses: {\n 200: { description: 'Transferred.', content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' }, purchase: { $ref: '#/components/schemas/PurchaseInfo' } } } } } },\n 400: err('Missing/invalid body fields (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n 404: err('No purchase with this transactionId (TRANSACTION_NOT_FOUND).'),\n },\n },\n },\n '/onesub/purchase/admin/grant': {\n post: {\n summary: 'Manually insert a purchase record, skipping store verification (admin).',\n parameters: [ADMIN_SECRET_PARAM],\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { $ref: '#/components/schemas/GrantPurchaseRequest' } } },\n },\n responses: {\n 200: { description: 'Granted.', content: { 'application/json': { schema: { type: 'object', properties: { ok: { type: 'boolean' }, purchase: { $ref: '#/components/schemas/PurchaseInfo' } } } } } },\n 400: err('Missing/invalid body fields (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/admin/webhook-deadletters': {\n get: {\n summary: 'List failed webhook jobs (when a queue with DLQ support is configured).',\n parameters: [ADMIN_SECRET_PARAM],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { type: 'object', properties: { items: { type: 'array', items: { type: 'object', description: 'DeadLetterRecord: { id, job, attemptsMade, lastError, failedAt }.' } } } } } } },\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n '/onesub/admin/webhook-replay/{id}': {\n post: {\n summary: 'Replay a dead-letter job through the webhook handler.',\n parameters: [\n ADMIN_SECRET_PARAM,\n { in: 'path', name: 'id', required: true, schema: { type: 'string' } },\n ],\n responses: {\n 200: { description: 'OK' },\n 400: err('id required (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n // ── metrics (mounted when config.adminSecret is set) ─────────────────────\n [ROUTES.METRICS_ACTIVE]: {\n get: {\n summary: 'Snapshot of currently-entitled users (active subs + non-consumable owners).',\n parameters: [ADMIN_SECRET_PARAM],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/MetricsActiveResponse' } } } },\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n [ROUTES.METRICS_STARTED]: {\n get: {\n summary: 'Subscriptions started (purchasedAt) within a window.',\n parameters: [ADMIN_SECRET_PARAM, ...METRICS_RANGE_PARAMS],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/MetricsCountResponse' } } } },\n 400: err('from/to missing, non-ISO, or from > to (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n [ROUTES.METRICS_EXPIRED]: {\n get: {\n summary: 'Subscriptions expired/canceled (expiresAt) within a window.',\n parameters: [ADMIN_SECRET_PARAM, ...METRICS_RANGE_PARAMS],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/MetricsCountResponse' } } } },\n 400: err('from/to missing, non-ISO, or from > to (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n [ROUTES.METRICS_PURCHASES_STARTED]: {\n get: {\n summary: 'Non-consumable purchases started (purchasedAt) within a window.',\n parameters: [ADMIN_SECRET_PARAM, ...METRICS_RANGE_PARAMS],\n responses: {\n 200: { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/MetricsCountResponse' } } } },\n 400: err('from/to missing, non-ISO, or from > to (INVALID_INPUT).'),\n 401: err('Invalid admin secret (INVALID_ADMIN_SECRET).'),\n },\n },\n },\n // ── Apple Promotional Offer signing (mounted when offerKeyId + offerPrivateKey set) ──\n [ROUTES.APPLE_OFFER_SIGNATURE]: {\n post: {\n summary: 'Sign an Apple Promotional Offer payload server-side (ES256).',\n description:\n 'The client passes the returned `{ keyId, nonce, timestamp, signature }` to StoreKit to redeem the offer. When `config.adminSecret` is set, the request must carry `X-Onesub-Offer-Secret` with the same value.',\n parameters: [\n { in: 'header', name: 'X-Onesub-Offer-Secret', required: false, schema: { type: 'string' }, description: 'Required (must equal config.adminSecret) when adminSecret is configured.' },\n ],\n requestBody: {\n required: true,\n content: { 'application/json': { schema: { $ref: '#/components/schemas/OfferSignatureRequest' } } },\n },\n responses: {\n 200: { description: 'Signed.', content: { 'application/json': { schema: { $ref: '#/components/schemas/OfferSignatureResponse' } } } },\n 400: err('Missing/invalid body fields or apple.bundleId not configured (INVALID_INPUT / APPLE_CONFIG_MISSING).'),\n 401: err('Offer secret missing or wrong (UNAUTHORIZED).'),\n },\n },\n },\n },\n components: {\n schemas: {\n ErrorResponse: {\n type: 'object',\n required: ['error', 'errorCode'],\n description:\n 'Canonical error body — every onesub 4xx/5xx carries it. Some routes append shape-compat fields (e.g. `valid: false`, `subscription: null`, `purchases: []`).',\n properties: {\n error: { type: 'string', description: 'Human-readable message.' },\n errorCode: { type: 'string', enum: Object.values(ONESUB_ERROR_CODE), description: 'Machine-readable canonical code.' },\n },\n },\n ValidateRequest: {\n type: 'object',\n required: ['userId', 'platform', 'productId', 'receipt'],\n properties: {\n userId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n productId: { type: 'string' },\n receipt: { type: 'string', description: 'Apple JWS or Google purchaseToken' },\n purchaseType: { type: 'string', enum: ['subscription', 'consumable', 'non_consumable'] },\n },\n },\n ValidateResponse: {\n type: 'object',\n properties: {\n valid: { type: 'boolean' },\n subscription: { $ref: '#/components/schemas/SubscriptionInfo' },\n purchase: { $ref: '#/components/schemas/PurchaseInfo' },\n },\n },\n ValidatePurchaseRequest: {\n type: 'object',\n required: ['userId', 'platform', 'productId', 'receipt', 'type'],\n properties: {\n userId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n productId: { type: 'string' },\n receipt: { type: 'string', description: 'Apple JWS or Google purchaseToken' },\n type: { type: 'string', enum: ['consumable', 'non_consumable'] },\n },\n },\n ValidatePurchaseResponse: {\n type: 'object',\n properties: {\n valid: { type: 'boolean' },\n purchase: { $ref: '#/components/schemas/PurchaseInfo' },\n action: { type: 'string', enum: ['new', 'restored'], description: 'Present on valid:true — \"restored\" means the transactionId was already recorded (idempotent retry or reassignment).' },\n },\n },\n PurchaseStatusResponse: {\n type: 'object',\n properties: {\n purchases: { type: 'array', items: { $ref: '#/components/schemas/PurchaseInfo' } },\n },\n },\n StatusResponse: {\n type: 'object',\n properties: {\n active: { type: 'boolean' },\n subscription: { $ref: '#/components/schemas/SubscriptionInfo' },\n },\n },\n SubscriptionInfo: {\n type: 'object',\n properties: {\n originalTransactionId: { type: 'string' },\n userId: { type: 'string' },\n productId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n status: { type: 'string', enum: ['active', 'grace_period', 'on_hold', 'paused', 'expired', 'canceled', 'none'] },\n expiresAt: { type: 'string', format: 'date-time' },\n purchasedAt: { type: 'string', format: 'date-time' },\n willRenew: { type: 'boolean' },\n },\n },\n PurchaseInfo: {\n type: 'object',\n properties: {\n transactionId: { type: 'string' },\n userId: { type: 'string' },\n productId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n type: { type: 'string', enum: ['consumable', 'non_consumable'] },\n quantity: { type: 'integer' },\n purchasedAt: { type: 'string', format: 'date-time' },\n },\n },\n EntitlementStatus: {\n type: 'object',\n required: ['active', 'source'],\n properties: {\n active: { type: 'boolean' },\n source: { type: ['string', 'null'], enum: ['subscription', 'purchase', null], description: 'Where the entitlement came from when active; null when not active.' },\n productId: { type: 'string', description: 'Matched productId (only when active).' },\n expiresAt: { type: 'string', format: 'date-time', description: 'Only when source === \"subscription\".' },\n },\n },\n EntitlementResponse: {\n allOf: [\n { $ref: '#/components/schemas/EntitlementStatus' },\n { type: 'object', required: ['id'], properties: { id: { type: 'string', description: 'The entitlement id queried, echoed back.' } } },\n ],\n },\n EntitlementsResponse: {\n type: 'object',\n properties: {\n entitlements: { type: 'object', additionalProperties: { $ref: '#/components/schemas/EntitlementStatus' } },\n },\n },\n ListSubscriptionsResponse: {\n type: 'object',\n properties: {\n items: { type: 'array', items: { $ref: '#/components/schemas/SubscriptionInfo' } },\n total: { type: 'integer', description: 'Total matches before limit/offset.' },\n limit: { type: 'integer' },\n offset: { type: 'integer' },\n },\n },\n CustomerProfileResponse: {\n type: 'object',\n properties: {\n userId: { type: 'string' },\n subscriptions: { type: 'array', items: { $ref: '#/components/schemas/SubscriptionInfo' } },\n purchases: { type: 'array', items: { $ref: '#/components/schemas/PurchaseInfo' } },\n entitlements: { type: 'object', additionalProperties: { $ref: '#/components/schemas/EntitlementStatus' }, description: 'Omitted when the server has no entitlements config.' },\n },\n },\n GrantPurchaseRequest: {\n type: 'object',\n required: ['userId', 'productId', 'platform'],\n properties: {\n userId: { type: 'string' },\n productId: { type: 'string' },\n platform: { type: 'string', enum: ['apple', 'google'] },\n type: { type: 'string', enum: ['consumable', 'non_consumable'], default: 'non_consumable' },\n transactionId: { type: 'string', description: 'Auto-generated `admin_grant_*` id when omitted.' },\n },\n },\n MetricsActiveResponse: {\n type: 'object',\n properties: {\n total: { type: 'integer', description: 'Active subs + grace_period subs + non-consumable owners.' },\n activeSubscriptions: { type: 'integer' },\n gracePeriodSubscriptions: { type: 'integer' },\n nonConsumablePurchases: { type: 'integer' },\n byProduct: { type: 'object', additionalProperties: { type: 'integer' }, description: 'Subscription product distribution (subs only).' },\n byProductPurchases: { type: 'object', additionalProperties: { type: 'integer' }, description: 'Non-consumable purchase distribution.' },\n byPlatform: { type: 'object', additionalProperties: { type: 'integer' } },\n },\n },\n MetricsBucket: {\n type: 'object',\n properties: {\n date: { type: 'string', description: 'UTC calendar day, YYYY-MM-DD.' },\n count: { type: 'integer' },\n },\n },\n MetricsCountResponse: {\n type: 'object',\n properties: {\n from: { type: 'string', format: 'date-time' },\n to: { type: 'string', format: 'date-time' },\n total: { type: 'integer' },\n byProduct: { type: 'object', additionalProperties: { type: 'integer' } },\n byPlatform: { type: 'object', additionalProperties: { type: 'integer' } },\n buckets: { type: 'array', items: { $ref: '#/components/schemas/MetricsBucket' }, description: 'Only present with ?groupBy=day — zero-filled, sorted ascending.' },\n },\n },\n OfferSignatureRequest: {\n type: 'object',\n required: ['productId', 'offerId', 'applicationUsername'],\n properties: {\n productId: { type: 'string' },\n offerId: { type: 'string', description: 'Promotional offer id defined in App Store Connect.' },\n applicationUsername: { type: 'string', description: 'Unique per-request UUID (the nonce seed).' },\n },\n },\n OfferSignatureResponse: {\n type: 'object',\n required: ['keyId', 'nonce', 'timestamp', 'signature'],\n properties: {\n keyId: { type: 'string' },\n nonce: { type: 'string' },\n timestamp: { type: 'integer', description: 'Milliseconds since epoch.' },\n signature: { type: 'string', description: 'Base64 ES256 signature for StoreKit.' },\n },\n },\n },\n securitySchemes: {\n AdminSecret: {\n type: 'apiKey',\n in: 'header',\n name: 'X-Admin-Secret',\n },\n },\n },\n};\n\n/**\n * Mount a route that serves the OpenAPI document. Useful for hosts that want\n * to expose `/openapi.json` to API explorers / SDK generators.\n *\n * app.use('/openapi.json', openapiHandler());\n */\nexport function openapiHandler() {\n return (_req: import('express').Request, res: import('express').Response) => {\n res.json(ONESUB_OPENAPI);\n };\n}\n","/**\n * Optional OpenTelemetry tracing.\n *\n * `@opentelemetry/api` is an optional peer dependency — when it's installed\n * onesub wraps its hot paths in spans. When it isn't, every span helper is\n * a zero-cost no-op (no dynamic import, no Promise overhead).\n *\n * Why a custom helper instead of @opentelemetry/instrumentation-express:\n * the express instrumentation traces every request, but onesub's interesting\n * spans are specific operations (Apple JWT mint, Google OAuth refresh,\n * receipt validation, webhook dispatch). Hand-spanned hot paths give\n * actionable traces without doubling latency-tracker overhead.\n */\n\nimport { createRequire } from 'node:module';\n\ntype Tracer = {\n startActiveSpan: <T>(name: string, fn: (span: Span) => T) => T;\n};\n\ntype Span = {\n setAttribute: (key: string, value: string | number | boolean) => void;\n recordException: (err: unknown) => void;\n setStatus: (status: { code: number; message?: string }) => void;\n end: () => void;\n};\n\nconst NOOP_SPAN: Span = {\n setAttribute: () => {},\n recordException: () => {},\n setStatus: () => {},\n end: () => {},\n};\n\n/**\n * Lazy-loaded tracer. We don't reach for `@opentelemetry/api` until the\n * first `withSpan` call so process startup pays nothing when otel is absent.\n */\nlet cachedTracer: Tracer | null = null;\nlet resolved = false;\n\nfunction getTracer(): Tracer | null {\n if (resolved) return cachedTracer;\n resolved = true;\n try {\n // Resolve synchronously — otel api is CJS-friendly. In the CJS bundle the\n // native `require` exists, so use it directly; in ESM we mint one via\n // createRequire(import.meta.url). The `typeof require` guard matters for\n // the CJS output: tsup's esbuild pass lowers `import.meta` to an empty\n // object there (shims are off in tsup.config.ts), so that expression must\n // only be evaluated on the ESM path — where it is fully supported.\n const req: NodeRequire =\n typeof require === 'function' ? require : createRequire(import.meta.url);\n const otel = req('@opentelemetry/api') as { trace: { getTracer: (n: string, v: string) => Tracer } };\n cachedTracer = otel.trace.getTracer('@onesub/server', '1.0.0');\n return cachedTracer;\n } catch {\n return null;\n }\n}\n\n/**\n * Wrap an async operation in a span. When otel isn't installed the function\n * runs unmodified — no span object is allocated.\n *\n * Use for operations whose latency or failure rate is operationally\n * interesting: receipt validation, store writes, outbound API calls.\n */\nexport async function withSpan<T>(\n name: string,\n attributes: Record<string, string | number | boolean>,\n fn: (span: Span) => Promise<T>,\n): Promise<T> {\n const tracer = getTracer();\n if (!tracer) return fn(NOOP_SPAN);\n\n return tracer.startActiveSpan(name, async (span) => {\n for (const [k, v] of Object.entries(attributes)) span.setAttribute(k, v);\n try {\n const result = await fn(span);\n // status code 1 = OK, 2 = ERROR (avoid importing the enum from otel api)\n span.setStatus({ code: 1 });\n return result;\n } catch (err) {\n span.recordException(err);\n span.setStatus({ code: 2, message: (err as Error).message });\n throw err;\n } finally {\n span.end();\n }\n });\n}\n","import express, { Router } from 'express';\nimport type { OneSubServerConfig } from '@onesub/shared';\nimport { DEFAULT_PORT } from '@onesub/shared';\nimport { InMemorySubscriptionStore, InMemoryPurchaseStore } from './store.js';\nimport type { SubscriptionStore, PurchaseStore } from './store.js';\nimport { createValidateRouter } from './routes/validate.js';\nimport { createStatusRouter } from './routes/status.js';\nimport { createWebhookRouter } from './routes/webhook.js';\nimport { createPurchaseRouter } from './routes/purchase.js';\nimport { createAdminRouter } from './routes/admin.js';\nimport { createEntitlementRouter } from './routes/entitlements.js';\nimport { createMetricsRouter } from './routes/metrics.js';\nimport { createAppleOfferRouter } from './routes/apple-offer.js';\nimport { setLogger } from './logger.js';\nimport type { CacheAdapter } from './cache.js';\nimport { setDefaultCache } from './cache.js';\nimport type { WebhookEventStore } from './webhook-events.js';\nimport type { WebhookQueue } from './webhook-queue.js';\n\n/**\n * Extended config with optional pluggable stores.\n */\nexport interface OneSubMiddlewareConfig extends OneSubServerConfig {\n /**\n * Custom subscription store. Defaults to InMemorySubscriptionStore.\n * For production, provide a PostgreSQL or Redis backed implementation.\n */\n store?: SubscriptionStore;\n /**\n * Custom purchase store for consumables and non-consumables.\n * Defaults to InMemoryPurchaseStore.\n * For production, provide a PostgreSQL backed implementation.\n */\n purchaseStore?: PurchaseStore;\n /**\n * Cache backend for short-lived secrets (Apple JWT, Google OAuth token).\n * Default: in-memory, process-local. For multi-instance deployments pass a\n * `RedisCacheAdapter` so all nodes share a single mint per TTL window.\n */\n cache?: CacheAdapter;\n /**\n * Webhook idempotency store. Default: none (rely on Apple/Google retry +\n * downstream store PKs). For production strongly recommended — pass an\n * `InMemoryWebhookEventStore` (single instance) or `CacheWebhookEventStore`\n * backed by Redis (multi-instance) so duplicate notifications never apply\n * twice.\n */\n webhookEventStore?: WebhookEventStore;\n /**\n * Async webhook queue. Default: none — webhook routes process notifications\n * synchronously inside the request (route latency = handler latency;\n * Apple/Google source retries on 5xx are the durability layer).\n *\n * When set, the webhook routes keep the cheap gating inline (validation,\n * JWS/push-token verification, bundleId/packageName checks, idempotency\n * dedup) and `enqueue()` the decoded work, acking 200 as soon as the job is\n * accepted. The state-mutating processing runs in the queue handler, which\n * is registered once here at middleware creation via `setHandler()`.\n *\n * Pass a `BullMQWebhookQueue` for durable retries with backoff; jobs that\n * exhaust their attempts land in the dead-letter list, accessible via\n * `GET /onesub/admin/webhook-deadletters` and replayable via\n * `POST /onesub/admin/webhook-replay/:id` (requires `adminSecret`).\n */\n webhookQueue?: WebhookQueue;\n}\n\n/**\n * Create an Express Router with all OneSub routes mounted.\n *\n * Mount this in your existing Express app:\n * ```ts\n * import { createOneSubMiddleware } from '@onesub/server';\n * app.use(createOneSubMiddleware(config));\n * ```\n *\n * The routes registered are (all prefixed with nothing — mount at root or use\n * a prefix via `app.use('/prefix', createOneSubMiddleware(config))`):\n *\n * POST /onesub/validate\n * GET /onesub/status\n * POST /onesub/webhook/apple\n * POST /onesub/webhook/google\n */\nexport function createOneSubMiddleware(config: OneSubMiddlewareConfig): Router {\n setLogger(config.logger);\n\n // Hard guard — mockMode accepts ANY receipt as valid. Letting this run on\n // a production server would be a fraud disaster. `skipJwsVerification` has\n // a similar shape but only degrades Apple signature checking; this one is\n // strictly worse, so the check is an error, not a warning.\n if ((config.apple?.mockMode || config.google?.mockMode) && process.env['NODE_ENV'] === 'production') {\n throw new Error(\n '[onesub] apple.mockMode / google.mockMode cannot be enabled when NODE_ENV=production — these modes accept any receipt as valid.',\n );\n }\n\n const store: SubscriptionStore = config.store ?? new InMemorySubscriptionStore();\n const purchaseStore: PurchaseStore = config.purchaseStore ?? new InMemoryPurchaseStore();\n\n // Swap the global cache adapter once when middleware is created. This is\n // the cheapest way to share a Redis-backed cache between all providers\n // without threading the adapter through every internal call site.\n if (config.cache) setDefaultCache(config.cache);\n\n const router = Router();\n\n // Parse JSON bodies for all OneSub routes (50 kb cap to prevent payload flooding)\n router.use(express.json({ limit: '50kb' }));\n\n router.use(createValidateRouter(config, store));\n router.use(createStatusRouter(store));\n router.use(createWebhookRouter(config, store, purchaseStore, config.webhookEventStore, config.webhookQueue));\n router.use(createPurchaseRouter(config, purchaseStore));\n\n // Admin routes — only mounted when config.adminSecret is set\n const adminRouter = createAdminRouter(config, purchaseStore, store, config.webhookQueue);\n if (adminRouter) router.use(adminRouter);\n\n // Entitlement routes — only mounted when config.entitlements is set\n const entitlementRouter = createEntitlementRouter(config, store, purchaseStore);\n if (entitlementRouter) router.use(entitlementRouter);\n\n // Metrics routes — only mounted when config.adminSecret is set (same gate\n // as admin routes; metrics expose aggregate operational data)\n const metricsRouter = createMetricsRouter(config, store, purchaseStore);\n if (metricsRouter) router.use(metricsRouter);\n\n // Apple Promotional Offer signing — only mounted when offerKeyId + offerPrivateKey are set\n const appleOfferRouter = createAppleOfferRouter(config);\n if (appleOfferRouter) router.use(appleOfferRouter);\n\n return router;\n}\n\n/**\n * Create a standalone Express application with all OneSub routes.\n *\n * ```ts\n * import { createOneSubServer } from '@onesub/server';\n * createOneSubServer(config).listen(4100);\n * ```\n */\nexport function createOneSubServer(config: OneSubMiddlewareConfig): ReturnType<typeof express> {\n const app = express();\n\n // Health check\n app.get('/health', (_req, res) => {\n res.json({ ok: true, service: 'onesub' });\n });\n\n app.use(createOneSubMiddleware(config));\n\n return app;\n}\n\n// Named re-exports for consumers who want to bring their own store\nexport { InMemorySubscriptionStore, InMemoryPurchaseStore } from './store.js';\nexport type { SubscriptionStore, PurchaseStore } from './store.js';\nexport { PostgresSubscriptionStore, PostgresPurchaseStore } from './stores/postgres.js';\nexport {\n RedisSubscriptionStore,\n RedisPurchaseStore,\n RedisCacheAdapter,\n RedisWebhookEventStore,\n} from './stores/redis.js';\n\n// Cache adapters\nexport type { CacheAdapter } from './cache.js';\nexport { InMemoryCacheAdapter, getDefaultCache, setDefaultCache } from './cache.js';\n\n// Webhook hardening primitives\nexport type { WebhookEventStore } from './webhook-events.js';\nexport { InMemoryWebhookEventStore, CacheWebhookEventStore, unmarkWebhookEvent } from './webhook-events.js';\nexport type { WebhookQueue, WebhookJob, WebhookHandler, DeadLetterRecord } from './webhook-queue.js';\nexport { InProcessWebhookQueue, BullMQWebhookQueue } from './webhook-queue.js';\n// Post-gate webhook processors — the same functions the built-in queue\n// handler runs. Exported so hosts running a dedicated worker process (e.g.\n// a standalone BullMQ Worker) can call `queue.setHandler(...)` themselves\n// with identical processing semantics.\nexport { processAppleNotification } from './routes/webhook-apple.js';\nexport type { AppleWebhookWork } from './routes/webhook-apple.js';\nexport { processGoogleNotification } from './routes/webhook-google.js';\nexport type { GoogleWebhookWork } from './routes/webhook-google.js';\n\n// OpenAPI document — for hosts that want to expose `/openapi.json` and\n// generate clients from the spec.\nexport { ONESUB_OPENAPI, openapiHandler } from './openapi.js';\nexport type { OpenAPIDoc } from './openapi.js';\n\n// OpenTelemetry helpers — exposed so host apps can wrap their own\n// integrations in spans that inherit the same tracer config.\nexport { withSpan } from './tracing.js';\n\n// Provider functions for direct (non-HTTP) usage\nexport {\n validateAppleReceipt,\n fetchAppleSubscriptionStatus,\n fetchAppleTransactionHistory,\n signApplePromotionalOffer,\n} from './providers/apple.js';\nexport { validateGoogleReceipt } from './providers/google.js';\n\n// Entitlement evaluator — exported so hosts can evaluate entitlements\n// in-process (e.g. from non-HTTP background workers, custom routes).\nexport { evaluateEntitlement } from './routes/entitlements.js';\n\n// Logger plumbing — expose so non-middleware callers (direct provider use)\n// can still redirect logs.\nexport { setLogger, log } from './logger.js';\n\n// Allow running directly: node dist/index.js\n// Reads config from environment variables for quick local testing.\nconst isMain =\n typeof process !== 'undefined' &&\n process.argv[1] != null &&\n new URL(import.meta.url).pathname.endsWith(process.argv[1].replace(/\\\\/g, '/'));\n\nif (isMain) {\n const config: OneSubMiddlewareConfig = {\n apple: process.env['APPLE_BUNDLE_ID']\n ? {\n bundleId: process.env['APPLE_BUNDLE_ID'],\n sharedSecret: process.env['APPLE_SHARED_SECRET'],\n keyId: process.env['APPLE_KEY_ID'],\n issuerId: process.env['APPLE_ISSUER_ID'],\n privateKey: process.env['APPLE_PRIVATE_KEY'],\n }\n : undefined,\n google: process.env['GOOGLE_PACKAGE_NAME']\n ? {\n packageName: process.env['GOOGLE_PACKAGE_NAME'],\n serviceAccountKey: process.env['GOOGLE_SERVICE_ACCOUNT_KEY'],\n }\n : undefined,\n database: {\n url: process.env['DATABASE_URL'] ?? '',\n },\n webhookSecret: process.env['WEBHOOK_SECRET'],\n };\n\n const port = process.env['PORT'] ? parseInt(process.env['PORT'], 10) : DEFAULT_PORT;\n\n createOneSubServer(config).listen(port, () => {\n console.log(`[onesub] Server listening on port ${port}`);\n });\n}\n"]}