From eee0761a21baa5e9e2f3f0ff60f671bcffa4c460 Mon Sep 17 00:00:00 2001 From: "foreman-ai-billing[bot]" Date: Mon, 31 Aug 2026 16:30:59 +0000 Subject: [PATCH] fix(nextjs): mock removed Narev balance/credit-topup endpoints fetchPolarUsage, fetchStripeUsage, fetchTopUpConfig, and createCheckout called Narev endpoints (GET /v1/balance, GET/POST /v1/credit) that no longer exist, returning 404s and breaking the billing UI package and chatbot examples. Replace these four nextjs server actions with deterministic, typed mock data instead of calling getNarevClient. createCheckout now safely returns the passed-in successUrl. Public exports and TypeScript contracts are unchanged. Unrelated Narev pricing/reference integrations (createOpenAIWithBilling & friends, fetchModelPricing, createChatRouter) are untouched and still call the live Narev API. - Add packages/nextjs/src/mock-billing-data.ts with shared fixtures. - Rewrite the four colocated Vitest suites to assert on mock result shapes and that getNarevClient is not called. - Update packages/nextjs/README.md and examples/utils/chatbot-minimal/.env.example to drop the now inaccurate NAREV_API_KEY requirement for the mocked billing UI/config calls, while keeping the still-needed model pricing NAREV_API_KEY documented. - Add a patch changeset for @ai-billing/nextjs. --- .changeset/thick-plums-sneeze.md | 5 + examples/utils/chatbot-minimal/.env.example | 4 +- packages/nextjs/README.md | 4 +- packages/nextjs/src/mock-billing-data.ts | 37 ++++++++ .../nextjs/src/polar/createCheckout.test.ts | 33 +++---- packages/nextjs/src/polar/createCheckout.ts | 26 ++--- .../nextjs/src/polar/fetchPolarUsage.test.ts | 95 +++---------------- packages/nextjs/src/polar/fetchPolarUsage.ts | 33 ++----- .../nextjs/src/polar/fetchTopUpConfig.test.ts | 56 ++++------- packages/nextjs/src/polar/fetchTopUpConfig.ts | 34 +++---- .../src/stripe/fetchStripeUsage.test.ts | 75 +++------------ .../nextjs/src/stripe/fetchStripeUsage.ts | 31 ++---- 12 files changed, 143 insertions(+), 290 deletions(-) create mode 100644 .changeset/thick-plums-sneeze.md create mode 100644 packages/nextjs/src/mock-billing-data.ts diff --git a/.changeset/thick-plums-sneeze.md b/.changeset/thick-plums-sneeze.md new file mode 100644 index 00000000..2b301b8b --- /dev/null +++ b/.changeset/thick-plums-sneeze.md @@ -0,0 +1,5 @@ +--- +"@ai-billing/nextjs": patch +--- + +fix(nextjs): mock removed Narev balance/credit-topup endpoints in billing UI server actions to prevent 404s diff --git a/examples/utils/chatbot-minimal/.env.example b/examples/utils/chatbot-minimal/.env.example index 45e8655b..7ede1463 100644 --- a/examples/utils/chatbot-minimal/.env.example +++ b/examples/utils/chatbot-minimal/.env.example @@ -8,6 +8,8 @@ POLAR_ACCESS_TOKEN=*** # Optional: Polar server environment (defaults to 'sandbox') # POLAR_SERVER=sandbox -# Required: Narev API key for pricing and credit management +# Required: Narev API key for model pricing # https://narev.ai +# Note: the credit balance / top-up UI (CreditUsagePolar, CreditTopUpPolar) +# currently returns mocked data and does not require this key. NAREV_API_KEY=*** diff --git a/packages/nextjs/README.md b/packages/nextjs/README.md index 6580c4a1..6f3dd23e 100644 --- a/packages/nextjs/README.md +++ b/packages/nextjs/README.md @@ -26,7 +26,6 @@ import { CreditUsagePolar, CreditTopUpPolar } from '@ai-billing/nextjs'; | Variable | Required | |----------|----------| -| `NAREV_API_KEY` | Config fetch | | `POLAR_ACCESS_TOKEN` | Meter usage + top-up | | `POLAR_SERVER` | `sandbox` or `production` | @@ -46,7 +45,6 @@ Stripe meters report values in nano-units. The component converts them to dollar | Variable | Required | |----------|----------| -| `NAREV_API_KEY` | Config fetch | | `STRIPE_SECRET_KEY` | Meter usage | ## Server Actions @@ -57,6 +55,8 @@ Server actions are available for advanced use cases: import { fetchPolarUsage, fetchStripeUsage, createCheckout } from '@ai-billing/nextjs/server'; ``` +> **Note:** `fetchPolarUsage`, `fetchStripeUsage`, `fetchTopUpConfig`, and `createCheckout` currently return mocked data. The Narev balance/credit endpoints they used to call have been removed, so no `NAREV_API_KEY` is required for these UI billing/config calls anymore. `NAREV_API_KEY` is still required for model pricing lookups (e.g. `createOpenAIWithBilling` and friends, `fetchModelPricing`), which continue to call the live Narev API. + ## Theming Components use CSS custom properties for styling. Override them via `className`: diff --git a/packages/nextjs/src/mock-billing-data.ts b/packages/nextjs/src/mock-billing-data.ts new file mode 100644 index 00000000..8b4a11c8 --- /dev/null +++ b/packages/nextjs/src/mock-billing-data.ts @@ -0,0 +1,37 @@ +import type { CreditPackage } from '@ai-billing/types'; +import type { PolarUsageData } from './polar/types.js'; +import type { StripeUsageData } from './stripe/types.js'; + +/** + * Temporary mock fixtures for the nextjs billing UI server actions. + * + * The Narev endpoints these actions used to call (`GET /v1/balance` and + * `GET`/`POST /v1/credit`) no longer exist. Until a replacement backend is + * available, `fetchPolarUsage`, `fetchStripeUsage`, `fetchTopUpConfig`, and + * `createCheckout` return deterministic mock data derived from these + * fixtures instead of calling `getNarevClient`. + */ + +/** Mock usage data returned by {@link fetchPolarUsage}. */ +export const MOCK_POLAR_USAGE_DATA: PolarUsageData = { + consumedUnits: 42, + creditedUnits: 100, + meterName: 'Usage', + found: true, +}; + +/** Mock usage data returned by {@link fetchStripeUsage}. */ +export const MOCK_STRIPE_USAGE_DATA: StripeUsageData = { + aggregatedValue: 12.5, + found: true, +}; + +/** Mock credit packages returned by {@link fetchTopUpConfig}. */ +export const MOCK_CREDIT_PACKAGES: CreditPackage[] = [ + { id: 'pkg_small', credits: 100, priceCents: 500 }, + { id: 'pkg_medium', credits: 500, priceCents: 2000 }, + { id: 'pkg_large', credits: 1000, priceCents: 3500 }, +]; + +/** Mock tax behavior returned alongside {@link MOCK_CREDIT_PACKAGES}. */ +export const MOCK_TAX_BEHAVIOR = 'exclusive' as const; diff --git a/packages/nextjs/src/polar/createCheckout.test.ts b/packages/nextjs/src/polar/createCheckout.test.ts index cbbf920c..a38c94a6 100644 --- a/packages/nextjs/src/polar/createCheckout.test.ts +++ b/packages/nextjs/src/polar/createCheckout.test.ts @@ -9,32 +9,25 @@ import { createCheckout } from './createCheckout.js'; beforeEach(() => { vi.clearAllMocks(); - process.env.NAREV_API_KEY = 'test-key'; }); describe('createCheckout', () => { - it('returns checkout URL on success', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - createCheckout: vi.fn().mockResolvedValueOnce({ - data: { url: 'https://polar.sh/checkout/sess_abc' }, - }), - } as ReturnType); - + it('returns the passed successUrl as the checkout URL', async () => { const url = await createCheckout('pkg_1', 'user_1', 'https://myapp.com'); - expect(url).toBe('https://polar.sh/checkout/sess_abc'); + expect(url).toBe('https://myapp.com'); }); - it('throws when checkout fails', async () => { - const consoleError = vi - .spyOn(console, 'error') - .mockImplementation(() => {}); - vi.mocked(getNarevClient).mockReturnValueOnce({ - createCheckout: vi.fn().mockRejectedValueOnce(new Error('API error')), - } as ReturnType); + it('is harmless for any productId/userId combination', async () => { + const url = await createCheckout( + 'pkg_2', + 'user_2', + 'https://myapp.com/success', + ); + expect(url).toBe('https://myapp.com/success'); + }); - await expect( - createCheckout('pkg_1', 'user_1', 'https://myapp.com'), - ).rejects.toThrow('Failed to create checkout'); - consoleError.mockRestore(); + it('does not call getNarevClient', async () => { + await createCheckout('pkg_1', 'user_1', 'https://myapp.com'); + expect(getNarevClient).not.toHaveBeenCalled(); }); }); diff --git a/packages/nextjs/src/polar/createCheckout.ts b/packages/nextjs/src/polar/createCheckout.ts index b86b14a2..9fc0270d 100644 --- a/packages/nextjs/src/polar/createCheckout.ts +++ b/packages/nextjs/src/polar/createCheckout.ts @@ -1,9 +1,12 @@ 'use server'; -import { getNarevClient } from '../narev-client.js'; - /** - * Creates a checkout session via Narev and returns the URL. + * Returns a mock checkout URL for a credit package purchase. + * + * The Narev checkout endpoint (`POST /v1/credit`) this action used to call + * no longer exists. Until a replacement backend is available this safely + * returns the passed-in `successUrl` instead of making a network call, so + * the caller's redirect flow keeps working harmlessly. * @param productId - the credit package product ID * @param userId - the end-user ID * @param successUrl - URL to redirect after successful purchase @@ -12,17 +15,8 @@ export async function createCheckout( productId: string, userId: string, successUrl: string, -) { - try { - const client = getNarevClient(); - const response = await client.createCheckout({ - productId, - userId, - successUrl, - }); - return response.data.url; - } catch (error) { - console.error('Create checkout failed:', error); - throw new Error('Failed to create checkout', { cause: error }); - } +): Promise { + void productId; + void userId; + return successUrl; } diff --git a/packages/nextjs/src/polar/fetchPolarUsage.test.ts b/packages/nextjs/src/polar/fetchPolarUsage.test.ts index 3df31bb1..34cb1b88 100644 --- a/packages/nextjs/src/polar/fetchPolarUsage.test.ts +++ b/packages/nextjs/src/polar/fetchPolarUsage.test.ts @@ -6,100 +6,29 @@ vi.mock('../narev-client.js', () => ({ import { getNarevClient } from '../narev-client.js'; import { fetchPolarUsage } from './fetchPolarUsage.js'; +import { MOCK_POLAR_USAGE_DATA } from '../mock-billing-data.js'; beforeEach(() => { vi.clearAllMocks(); - process.env.NAREV_API_KEY = 'test-key'; }); describe('fetchPolarUsage', () => { - it('returns usage data from Narev balance', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - getBalance: vi.fn().mockResolvedValueOnce({ - data: { - unitsBalance: 50, - unitsConsumed: 42, - unitsCredited: 100, - unit: 'base', - currency: 'USD', - meterName: 'Tokens', - found: true, - }, - }), - } as ReturnType); - - const result = await fetchPolarUsage('user_1'); - expect(result).toEqual({ - consumedUnits: 42, - creditedUnits: 100, - meterName: 'Tokens', - found: true, - }); - }); - - it('maps null creditedUnits to 0', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - getBalance: vi.fn().mockResolvedValueOnce({ - data: { - unitsBalance: null, - unitsConsumed: 10, - unitsCredited: null, - unit: 'base', - currency: 'USD', - meterName: 'Usage', - found: true, - }, - }), - } as ReturnType); - + it('returns the mock usage data', async () => { const result = await fetchPolarUsage('user_1'); - expect(result).toEqual({ - consumedUnits: 10, - creditedUnits: 0, - meterName: 'Usage', - found: true, - }); + expect(result).toEqual(MOCK_POLAR_USAGE_DATA); }); - it('returns empty when not found', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - getBalance: vi.fn().mockResolvedValueOnce({ - data: { - unitsBalance: null, - unitsConsumed: 0, - unitsCredited: null, - unit: 'base', - currency: 'USD', - meterName: 'Usage', - found: false, - }, - }), - } as ReturnType); - + it('returns a found, non-empty, contract-valid result', async () => { const result = await fetchPolarUsage('user_1'); - expect(result).toEqual({ - consumedUnits: 0, - creditedUnits: 0, - meterName: 'Usage', - found: false, - }); + expect(result.found).toBe(true); + expect(result.consumedUnits).toBeGreaterThanOrEqual(0); + expect(result.creditedUnits).toBeGreaterThanOrEqual(0); + expect(typeof result.meterName).toBe('string'); + expect(result.meterName.length).toBeGreaterThan(0); }); - it('returns empty when Narev API throws', async () => { - const consoleError = vi - .spyOn(console, 'error') - .mockImplementation(() => {}); - vi.mocked(getNarevClient).mockReturnValueOnce({ - getBalance: vi.fn().mockRejectedValueOnce(new Error('API error')), - } as ReturnType); - - const result = await fetchPolarUsage('user_1'); - expect(result).toEqual({ - consumedUnits: 0, - creditedUnits: 0, - meterName: 'Usage', - found: false, - }); - consoleError.mockRestore(); + it('does not call getNarevClient', async () => { + await fetchPolarUsage('user_1'); + expect(getNarevClient).not.toHaveBeenCalled(); }); }); diff --git a/packages/nextjs/src/polar/fetchPolarUsage.ts b/packages/nextjs/src/polar/fetchPolarUsage.ts index c74a8b3b..8bd25c90 100644 --- a/packages/nextjs/src/polar/fetchPolarUsage.ts +++ b/packages/nextjs/src/polar/fetchPolarUsage.ts @@ -1,34 +1,17 @@ 'use server'; -import { getNarevClient } from '../narev-client.js'; +import { MOCK_POLAR_USAGE_DATA } from '../mock-billing-data.js'; import type { PolarUsageData } from './types.js'; /** - * Fetches usage data for a given user via the Narev API. + * Returns mock usage data for a given user. + * + * The Narev balance endpoint (`GET /v1/balance`) this action used to call no + * longer exists. Until a replacement backend is available this returns + * deterministic mock data instead of making a network call. * @param userId - the end-user ID */ export async function fetchPolarUsage(userId: string): Promise { - const empty = { - consumedUnits: 0, - creditedUnits: 0, - meterName: 'Usage', - found: false, - }; - - try { - const client = getNarevClient(); - const response = await client.getBalance({ userId }); - const data = response.data; - - return { - consumedUnits: data.unitsConsumed, - creditedUnits: data.unitsCredited ?? 0, - meterName: data.meterName, - found: data.found, - }; - } catch (error) { - console.error('fetchPolarUsage:', error); - } - - return empty; + void userId; + return MOCK_POLAR_USAGE_DATA; } diff --git a/packages/nextjs/src/polar/fetchTopUpConfig.test.ts b/packages/nextjs/src/polar/fetchTopUpConfig.test.ts index 64c6ddee..6514f694 100644 --- a/packages/nextjs/src/polar/fetchTopUpConfig.test.ts +++ b/packages/nextjs/src/polar/fetchTopUpConfig.test.ts @@ -6,56 +6,34 @@ vi.mock('../narev-client.js', () => ({ import { getNarevClient } from '../narev-client.js'; import { fetchTopUpConfig } from './fetchTopUpConfig.js'; +import { + MOCK_CREDIT_PACKAGES, + MOCK_TAX_BEHAVIOR, +} from '../mock-billing-data.js'; beforeEach(() => { vi.clearAllMocks(); - process.env.NAREV_API_KEY = 'test-key'; }); describe('fetchTopUpConfig', () => { - it('returns packages and tax behavior from Narev', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - getCreditConfig: vi.fn().mockResolvedValueOnce({ - data: { - packages: [ - { id: 'pkg_1', credits: 100, priceCents: 1000 }, - { id: 'pkg_2', credits: 500, priceCents: 4500 }, - ], - taxBehavior: 'inclusive', - }, - }), - } as ReturnType); - + it('returns the mock packages and tax behavior', async () => { const result = await fetchTopUpConfig(); - expect(result.packages).toEqual([ - { id: 'pkg_1', credits: 100, priceCents: 1000 }, - { id: 'pkg_2', credits: 500, priceCents: 4500 }, - ]); - expect(result.taxBehavior).toBe('inclusive'); + expect(result.packages).toEqual(MOCK_CREDIT_PACKAGES); + expect(result.taxBehavior).toBe(MOCK_TAX_BEHAVIOR); }); - it('returns empty packages when no packages available', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - getCreditConfig: vi.fn().mockResolvedValueOnce({ - data: { packages: [] }, - }), - } as ReturnType); - + it('returns a non-empty, contract-valid package list', async () => { const result = await fetchTopUpConfig(); - expect(result.packages).toEqual([]); - expect(result.taxBehavior).toBeUndefined(); + expect(result.packages.length).toBeGreaterThan(0); + for (const pkg of result.packages) { + expect(typeof pkg.id).toBe('string'); + expect(pkg.credits).toBeGreaterThan(0); + expect(pkg.priceCents).toBeGreaterThan(0); + } }); - it('returns empty config when Narev API throws', async () => { - const consoleError = vi - .spyOn(console, 'error') - .mockImplementation(() => {}); - vi.mocked(getNarevClient).mockReturnValueOnce({ - getCreditConfig: vi.fn().mockRejectedValueOnce(new Error('API down')), - } as ReturnType); - - const result = await fetchTopUpConfig(); - expect(result).toEqual({ packages: [] }); - consoleError.mockRestore(); + it('does not call getNarevClient', async () => { + await fetchTopUpConfig(); + expect(getNarevClient).not.toHaveBeenCalled(); }); }); diff --git a/packages/nextjs/src/polar/fetchTopUpConfig.ts b/packages/nextjs/src/polar/fetchTopUpConfig.ts index 487ee512..03a7e4a1 100644 --- a/packages/nextjs/src/polar/fetchTopUpConfig.ts +++ b/packages/nextjs/src/polar/fetchTopUpConfig.ts @@ -1,6 +1,9 @@ 'use server'; -import { getNarevClient } from '../narev-client.js'; +import { + MOCK_CREDIT_PACKAGES, + MOCK_TAX_BEHAVIOR, +} from '../mock-billing-data.js'; import type { CreditPackage } from './types.js'; interface TopUpConfig { @@ -8,23 +11,16 @@ interface TopUpConfig { taxBehavior?: 'inclusive' | 'exclusive' | 'location'; } -/** Fetches available top-up packages and optional tax behavior from Narev. */ +/** + * Returns mock top-up packages and tax behavior. + * + * The Narev credit config endpoint (`GET /v1/credit`) this action used to + * call no longer exists. Until a replacement backend is available this + * returns deterministic mock data instead of making a network call. + */ export async function fetchTopUpConfig(): Promise { - const config: TopUpConfig = { packages: [] }; - - try { - const client = getNarevClient(); - const response = await client.getCreditConfig(); - const data = response.data; - - config.packages = data.packages; - - if (data.taxBehavior) { - config.taxBehavior = data.taxBehavior; - } - } catch (error) { - console.error('fetchTopUpConfig: config fetch failed', error); - } - - return config; + return { + packages: MOCK_CREDIT_PACKAGES, + taxBehavior: MOCK_TAX_BEHAVIOR, + }; } diff --git a/packages/nextjs/src/stripe/fetchStripeUsage.test.ts b/packages/nextjs/src/stripe/fetchStripeUsage.test.ts index 7d3103c9..88ce9fd5 100644 --- a/packages/nextjs/src/stripe/fetchStripeUsage.test.ts +++ b/packages/nextjs/src/stripe/fetchStripeUsage.test.ts @@ -6,80 +6,31 @@ vi.mock('../narev-client.js', () => ({ import { getNarevClient } from '../narev-client.js'; import { fetchStripeUsage } from './fetchStripeUsage.js'; +import { MOCK_STRIPE_USAGE_DATA } from '../mock-billing-data.js'; beforeEach(() => { vi.clearAllMocks(); - process.env.NAREV_API_KEY = 'test-key'; }); describe('fetchStripeUsage', () => { - it('converts nanos to dollars when unit is nanos', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - getBalance: vi.fn().mockResolvedValueOnce({ - data: { - unitsBalance: null, - unitsConsumed: 1_417_500, - unitsCredited: null, - unit: 'nanos', - currency: 'USD', - meterName: 'Usage', - found: true, - }, - }), - } as ReturnType); - - const result = await fetchStripeUsage({ stripeCustomerId: 'cus_1' }); - expect(result).toEqual({ aggregatedValue: 0.0014175, found: true }); - }); - - it('does not convert when unit is base', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - getBalance: vi.fn().mockResolvedValueOnce({ - data: { - unitsBalance: 50, - unitsConsumed: 25, - unitsCredited: 100, - unit: 'base', - currency: 'USD', - meterName: 'Usage', - found: true, - }, - }), - } as ReturnType); - + it('returns the mock usage data for a userId lookup', async () => { const result = await fetchStripeUsage({ userId: 'user_1' }); - expect(result).toEqual({ aggregatedValue: 25, found: true }); + expect(result).toEqual(MOCK_STRIPE_USAGE_DATA); }); - it('returns empty when not found', async () => { - vi.mocked(getNarevClient).mockReturnValueOnce({ - getBalance: vi.fn().mockResolvedValueOnce({ - data: { - unitsBalance: null, - unitsConsumed: 0, - unitsCredited: null, - unit: 'nanos', - currency: 'USD', - meterName: 'Usage', - found: false, - }, - }), - } as ReturnType); - + it('returns the mock usage data for a stripeCustomerId lookup', async () => { const result = await fetchStripeUsage({ stripeCustomerId: 'cus_1' }); - expect(result).toEqual({ aggregatedValue: 0, found: false }); + expect(result).toEqual(MOCK_STRIPE_USAGE_DATA); }); - it('returns empty on Narev API error', async () => { - const consoleError = vi - .spyOn(console, 'error') - .mockImplementation(() => {}); - vi.mocked(getNarevClient).mockReturnValueOnce({ - getBalance: vi.fn().mockRejectedValueOnce(new Error('API down')), - } as ReturnType); + it('returns a found, non-empty, contract-valid result', async () => { + const result = await fetchStripeUsage({ userId: 'user_1' }); + expect(result.found).toBe(true); + expect(result.aggregatedValue).toBeGreaterThan(0); + }); - const result = await fetchStripeUsage({ stripeCustomerId: 'cus_1' }); - expect(result).toEqual({ aggregatedValue: 0, found: false }); - consoleError.mockRestore(); + it('does not call getNarevClient', async () => { + await fetchStripeUsage({ userId: 'user_1' }); + expect(getNarevClient).not.toHaveBeenCalled(); }); }); diff --git a/packages/nextjs/src/stripe/fetchStripeUsage.ts b/packages/nextjs/src/stripe/fetchStripeUsage.ts index 6e95f466..dde9e57b 100644 --- a/packages/nextjs/src/stripe/fetchStripeUsage.ts +++ b/packages/nextjs/src/stripe/fetchStripeUsage.ts @@ -1,35 +1,20 @@ 'use server'; import type { GetBalanceRequest } from '@ai-billing/types'; -import { getNarevClient } from '../narev-client.js'; +import { MOCK_STRIPE_USAGE_DATA } from '../mock-billing-data.js'; import type { StripeUsageData } from './types.js'; /** - * Fetches usage data for a given customer via the Narev API. + * Returns mock usage data for a given customer. + * + * The Narev balance endpoint (`GET /v1/balance`) this action used to call no + * longer exists. Until a replacement backend is available this returns + * deterministic mock data instead of making a network call. * @param request - User identifier — either `{ userId }` or `{ stripeCustomerId }`. */ export async function fetchStripeUsage( request: GetBalanceRequest, ): Promise { - const empty = { aggregatedValue: 0, found: false }; - - try { - const client = getNarevClient(); - const response = await client.getBalance(request); - const data = response.data; - - const aggregatedValue = - data.unit === 'nanos' - ? data.unitsConsumed / 1_000_000_000 - : data.unitsConsumed; - - return { - aggregatedValue, - found: data.found, - }; - } catch (error) { - console.error('fetchStripeUsage:', error); - } - - return empty; + void request; + return MOCK_STRIPE_USAGE_DATA; }