@revealui/services 0.3.2 → 0.3.4
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.
- package/dist/stripe/payment-intent.d.ts.map +1 -1
- package/dist/stripe/payment-intent.js +6 -1
- package/dist/stripe/payment-intent.js.map +1 -1
- package/dist/supabase/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/LICENSE.commercial +0 -111
- package/dist/api/create-checkout-session/index.d.ts +0 -2
- package/dist/api/create-checkout-session/index.d.ts.map +0 -1
- package/dist/api/create-checkout-session/index.js +0 -61
- package/dist/api/create-checkout-session/index.js.map +0 -1
- package/dist/api/create-portal-link/index.d.ts +0 -2
- package/dist/api/create-portal-link/index.d.ts.map +0 -1
- package/dist/api/create-portal-link/index.js +0 -37
- package/dist/api/create-portal-link/index.js.map +0 -1
- package/dist/api/handlers/customer-handlers.d.ts +0 -27
- package/dist/api/handlers/customer-handlers.d.ts.map +0 -1
- package/dist/api/handlers/customer-handlers.js +0 -82
- package/dist/api/handlers/customer-handlers.js.map +0 -1
- package/dist/api/handlers/index.d.ts +0 -18
- package/dist/api/handlers/index.d.ts.map +0 -1
- package/dist/api/handlers/index.js +0 -18
- package/dist/api/handlers/index.js.map +0 -1
- package/dist/api/handlers/invoice-handlers.d.ts +0 -9
- package/dist/api/handlers/invoice-handlers.d.ts.map +0 -1
- package/dist/api/handlers/invoice-handlers.js +0 -52
- package/dist/api/handlers/invoice-handlers.js.map +0 -1
- package/dist/api/handlers/payment-handlers.d.ts +0 -13
- package/dist/api/handlers/payment-handlers.d.ts.map +0 -1
- package/dist/api/handlers/payment-handlers.js +0 -133
- package/dist/api/handlers/payment-handlers.js.map +0 -1
- package/dist/api/handlers/payment-intent.d.ts +0 -21
- package/dist/api/handlers/payment-intent.d.ts.map +0 -1
- package/dist/api/handlers/payment-intent.js +0 -87
- package/dist/api/handlers/payment-intent.js.map +0 -1
- package/dist/api/handlers/product-handlers.d.ts +0 -11
- package/dist/api/handlers/product-handlers.d.ts.map +0 -1
- package/dist/api/handlers/product-handlers.js +0 -43
- package/dist/api/handlers/product-handlers.js.map +0 -1
- package/dist/api/handlers/subscription-handlers.d.ts +0 -17
- package/dist/api/handlers/subscription-handlers.d.ts.map +0 -1
- package/dist/api/handlers/subscription-handlers.js +0 -119
- package/dist/api/handlers/subscription-handlers.js.map +0 -1
- package/dist/api/index.d.ts +0 -7
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js +0 -7
- package/dist/api/index.js.map +0 -1
- package/dist/api/types/stripe.d.ts +0 -42
- package/dist/api/types/stripe.d.ts.map +0 -1
- package/dist/api/types/stripe.js +0 -72
- package/dist/api/types/stripe.js.map +0 -1
- package/dist/api/update-price/index.d.ts +0 -42
- package/dist/api/update-price/index.d.ts.map +0 -1
- package/dist/api/update-price/index.js +0 -78
- package/dist/api/update-price/index.js.map +0 -1
- package/dist/api/update-product/index.d.ts +0 -44
- package/dist/api/update-product/index.d.ts.map +0 -1
- package/dist/api/update-product/index.js +0 -85
- package/dist/api/update-product/index.js.map +0 -1
- package/dist/api/utils.d.ts +0 -34
- package/dist/api/utils.d.ts.map +0 -1
- package/dist/api/utils.js +0 -66
- package/dist/api/utils.js.map +0 -1
- package/dist/api/webhooks/index.d.ts +0 -12
- package/dist/api/webhooks/index.d.ts.map +0 -1
- package/dist/api/webhooks/index.js +0 -280
- package/dist/api/webhooks/index.js.map +0 -1
- package/dist/stripe/billing.d.ts +0 -121
- package/dist/stripe/billing.d.ts.map +0 -1
- package/dist/stripe/billing.js +0 -157
- package/dist/stripe/billing.js.map +0 -1
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated This Supabase-based webhook handler is legacy code.
|
|
3
|
-
*
|
|
4
|
-
* The production webhook handler is in apps/api/src/routes/webhooks.ts,
|
|
5
|
-
* which uses NeonDB (Drizzle ORM) directly and handles all subscription
|
|
6
|
-
* lifecycle events including disputes, refunds, and payment recovery.
|
|
7
|
-
*
|
|
8
|
-
* This file is retained for backward compatibility with any external
|
|
9
|
-
* consumers of @revealui/services, but is NOT used by the API app.
|
|
10
|
-
*/
|
|
11
|
-
import { generateLicenseKey } from '@revealui/core/license';
|
|
12
|
-
import { logger } from '@revealui/core/utils/logger';
|
|
13
|
-
import { getClient } from '@revealui/db/client';
|
|
14
|
-
import { processedWebhookEvents } from '@revealui/db/schema';
|
|
15
|
-
import { eq } from 'drizzle-orm';
|
|
16
|
-
import { protectedStripe } from '../../stripe/stripeClient.js';
|
|
17
|
-
import { createServerClientFromRequest } from '../../supabase/index.js';
|
|
18
|
-
import { handleInvoicePaymentFailed, handleInvoicePaymentSucceeded, handlePaymentMethodAttached, manageSubscriptionStatusChange, upsertPriceRecord, upsertProductRecord, } from '../utils.js';
|
|
19
|
-
const relevantEvents = new Set([
|
|
20
|
-
'product.created',
|
|
21
|
-
'product.updated',
|
|
22
|
-
'price.created',
|
|
23
|
-
'price.updated',
|
|
24
|
-
'checkout.session.completed',
|
|
25
|
-
'customer.subscription.created',
|
|
26
|
-
'customer.subscription.updated',
|
|
27
|
-
'customer.subscription.deleted',
|
|
28
|
-
'invoice.payment_succeeded',
|
|
29
|
-
'invoice.payment_failed',
|
|
30
|
-
'payment_method.attached',
|
|
31
|
-
]);
|
|
32
|
-
/** Maximum webhook payload size (10 MB). Stripe payloads are tiny; this guards against DoS. */
|
|
33
|
-
const MAX_BODY_BYTES = 10 * 1024 * 1024;
|
|
34
|
-
/**
|
|
35
|
-
* Resolve the webhook secret from environment variables.
|
|
36
|
-
* Throws at startup/request time if no secret is configured, preventing
|
|
37
|
-
* unsigned webhook acceptance.
|
|
38
|
-
*/
|
|
39
|
-
function getWebhookSecret() {
|
|
40
|
-
const secret = (typeof import.meta.env.STRIPE_WEBHOOK_SECRET_LIVE === 'string'
|
|
41
|
-
? import.meta.env.STRIPE_WEBHOOK_SECRET_LIVE
|
|
42
|
-
: undefined) ??
|
|
43
|
-
(typeof import.meta.env.STRIPE_WEBHOOK_SECRET === 'string'
|
|
44
|
-
? import.meta.env.STRIPE_WEBHOOK_SECRET
|
|
45
|
-
: undefined);
|
|
46
|
-
if (!secret) {
|
|
47
|
-
throw new Error('STRIPE_WEBHOOK_SECRET or STRIPE_WEBHOOK_SECRET_LIVE must be configured. ' +
|
|
48
|
-
'Refusing to process webhooks without signature verification.');
|
|
49
|
-
}
|
|
50
|
-
return secret;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Database-backed idempotency check. Prevents duplicate webhook processing
|
|
54
|
-
* across Vercel cold starts, multi-region deployments, and server restarts.
|
|
55
|
-
*/
|
|
56
|
-
async function isEventAlreadyProcessed(eventId) {
|
|
57
|
-
const db = getClient();
|
|
58
|
-
const [existing] = await db
|
|
59
|
-
.select({ id: processedWebhookEvents.id })
|
|
60
|
-
.from(processedWebhookEvents)
|
|
61
|
-
.where(eq(processedWebhookEvents.id, eventId))
|
|
62
|
-
.limit(1);
|
|
63
|
-
return !!existing;
|
|
64
|
-
}
|
|
65
|
-
async function markEventProcessed(eventId, eventType) {
|
|
66
|
-
const db = getClient();
|
|
67
|
-
await db.insert(processedWebhookEvents).values({ id: eventId, eventType }).onConflictDoNothing();
|
|
68
|
-
}
|
|
69
|
-
export async function POST(request) {
|
|
70
|
-
const supabase = createServerClientFromRequest(request);
|
|
71
|
-
if (!supabase) {
|
|
72
|
-
return new Response('Supabase client not available', { status: 500 });
|
|
73
|
-
}
|
|
74
|
-
let webhookSecret;
|
|
75
|
-
try {
|
|
76
|
-
webhookSecret = getWebhookSecret();
|
|
77
|
-
}
|
|
78
|
-
catch (err) {
|
|
79
|
-
const msg = err instanceof Error ? err.message : 'Unknown error';
|
|
80
|
-
logger.error('Webhook secret not configured', { error: msg });
|
|
81
|
-
return new Response('Webhook endpoint not configured', { status: 500 });
|
|
82
|
-
}
|
|
83
|
-
// Reject oversized payloads before reading into memory (DoS protection).
|
|
84
|
-
// Content-Length is advisory; we still enforce after reading.
|
|
85
|
-
const contentLength = request.headers.get('content-length');
|
|
86
|
-
if (contentLength !== null && Number(contentLength) > MAX_BODY_BYTES) {
|
|
87
|
-
return new Response('Payload too large', { status: 413 });
|
|
88
|
-
}
|
|
89
|
-
const body = await request.text();
|
|
90
|
-
if (body.length > MAX_BODY_BYTES) {
|
|
91
|
-
return new Response('Payload too large', { status: 413 });
|
|
92
|
-
}
|
|
93
|
-
const sig = request.headers.get('Stripe-Signature');
|
|
94
|
-
if (!sig) {
|
|
95
|
-
return new Response('Missing Stripe-Signature header', { status: 400 });
|
|
96
|
-
}
|
|
97
|
-
let event;
|
|
98
|
-
try {
|
|
99
|
-
event = protectedStripe.webhooks.constructEvent(body, sig, webhookSecret);
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
103
|
-
logger.error('Webhook signature verification failed', {
|
|
104
|
-
error: errorMessage,
|
|
105
|
-
});
|
|
106
|
-
return new Response(`Webhook Error: ${errorMessage}`, { status: 400 });
|
|
107
|
-
}
|
|
108
|
-
// Idempotency check: skip already-processed events
|
|
109
|
-
if (await isEventAlreadyProcessed(event.id)) {
|
|
110
|
-
logger.debug('Skipping duplicate webhook event', { eventId: event.id });
|
|
111
|
-
return new Response(JSON.stringify({ received: true, duplicate: true }), { status: 200 });
|
|
112
|
-
}
|
|
113
|
-
if (relevantEvents.has(event.type)) {
|
|
114
|
-
try {
|
|
115
|
-
// Mark event as processing BEFORE handling — prevents duplicate processing
|
|
116
|
-
// on Stripe retries even if the handler throws below.
|
|
117
|
-
await markEventProcessed(event.id, event.type);
|
|
118
|
-
switch (event.type) {
|
|
119
|
-
case 'product.created':
|
|
120
|
-
case 'product.updated': {
|
|
121
|
-
const product = event.data.object;
|
|
122
|
-
if (product.object === 'product') {
|
|
123
|
-
await upsertProductRecord(supabase, product);
|
|
124
|
-
}
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
case 'price.created':
|
|
128
|
-
case 'price.updated': {
|
|
129
|
-
const price = event.data.object;
|
|
130
|
-
if (price.object === 'price') {
|
|
131
|
-
await upsertPriceRecord(supabase, price);
|
|
132
|
-
}
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
case 'customer.subscription.created':
|
|
136
|
-
case 'customer.subscription.updated':
|
|
137
|
-
case 'customer.subscription.deleted': {
|
|
138
|
-
const subscription = event.data.object;
|
|
139
|
-
if (subscription.object === 'subscription') {
|
|
140
|
-
const customerId = typeof subscription.customer === 'string'
|
|
141
|
-
? subscription.customer
|
|
142
|
-
: subscription.customer?.id;
|
|
143
|
-
if (!customerId) {
|
|
144
|
-
throw new Error('Subscription missing customer');
|
|
145
|
-
}
|
|
146
|
-
await manageSubscriptionStatusChange(subscription.id, customerId, event.type === 'customer.subscription.created', supabase);
|
|
147
|
-
// Flag license for revocation on subscription cancellation
|
|
148
|
-
if (event.type === 'customer.subscription.deleted') {
|
|
149
|
-
logger.info('Subscription deleted — license should be revoked', {
|
|
150
|
-
customerId,
|
|
151
|
-
subscriptionId: subscription.id,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
case 'checkout.session.completed': {
|
|
158
|
-
const checkoutSession = event.data.object;
|
|
159
|
-
if (checkoutSession.object === 'checkout.session') {
|
|
160
|
-
const session = checkoutSession;
|
|
161
|
-
if (session.mode === 'subscription' && session.subscription) {
|
|
162
|
-
const subscriptionId = typeof session.subscription === 'string'
|
|
163
|
-
? session.subscription
|
|
164
|
-
: session.subscription.id;
|
|
165
|
-
const customerId = typeof session.customer === 'string' ? session.customer : session.customer?.id;
|
|
166
|
-
if (subscriptionId && customerId) {
|
|
167
|
-
await manageSubscriptionStatusChange(subscriptionId, customerId, true, supabase);
|
|
168
|
-
// Retrieve the subscription to:
|
|
169
|
-
// (a) check if a license key was already generated (idempotency across retries)
|
|
170
|
-
// (b) get the actual price ID for authoritative tier resolution
|
|
171
|
-
const subscription = await protectedStripe.subscriptions.retrieve(subscriptionId);
|
|
172
|
-
if (subscription.metadata?.license_key) {
|
|
173
|
-
// Already generated — Stripe retried the webhook. Safe to skip.
|
|
174
|
-
logger.info('License key already present, skipping generation (idempotent)', {
|
|
175
|
-
customerId,
|
|
176
|
-
subscriptionId,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
const priceId = subscription.items.data[0]?.price?.id ?? null;
|
|
181
|
-
const tier = resolveTierFromMetadata(session.metadata, priceId);
|
|
182
|
-
const privateKey = process.env.REVEALUI_LICENSE_PRIVATE_KEY;
|
|
183
|
-
if (!privateKey) {
|
|
184
|
-
// This is a configuration error, not a customer error.
|
|
185
|
-
// The subscription is active but no license can be issued.
|
|
186
|
-
logger.error('CRITICAL: REVEALUI_LICENSE_PRIVATE_KEY not set — license not generated. ' +
|
|
187
|
-
'Manual intervention required.', { customerId, subscriptionId, tier });
|
|
188
|
-
}
|
|
189
|
-
else if (tier !== 'free') {
|
|
190
|
-
try {
|
|
191
|
-
const licenseKey = await generateLicenseKey({ tier, customerId }, privateKey);
|
|
192
|
-
await protectedStripe.subscriptions.update(subscriptionId, {
|
|
193
|
-
metadata: { license_key: licenseKey, license_tier: tier },
|
|
194
|
-
});
|
|
195
|
-
logger.info('License key generated for checkout', { tier, customerId });
|
|
196
|
-
}
|
|
197
|
-
catch (licenseErr) {
|
|
198
|
-
logger.error('CRITICAL: License generation failed after successful checkout. ' +
|
|
199
|
-
'Manual intervention required.', {
|
|
200
|
-
error: licenseErr instanceof Error ? licenseErr.message : 'Unknown error',
|
|
201
|
-
customerId,
|
|
202
|
-
subscriptionId,
|
|
203
|
-
tier,
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
logger.info('Subscription session completed');
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
case 'invoice.payment_succeeded': {
|
|
215
|
-
const invoiceEvent = event;
|
|
216
|
-
await handleInvoicePaymentSucceeded(invoiceEvent, supabase);
|
|
217
|
-
break;
|
|
218
|
-
}
|
|
219
|
-
case 'invoice.payment_failed': {
|
|
220
|
-
const invoiceEvent = event;
|
|
221
|
-
await handleInvoicePaymentFailed(invoiceEvent, supabase);
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
224
|
-
case 'payment_method.attached': {
|
|
225
|
-
const pmEvent = event;
|
|
226
|
-
await handlePaymentMethodAttached(pmEvent, supabase);
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
default:
|
|
230
|
-
throw new Error('Unhandled relevant event!');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
catch (error) {
|
|
234
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
235
|
-
logger.error('Webhook handler error', { error: errorMessage });
|
|
236
|
-
return new Response(`Webhook error: "Webhook handler failed. View logs." Error: ${errorMessage}`, {
|
|
237
|
-
status: 400,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return new Response(JSON.stringify({ received: true }), { status: 200 });
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Resolve license tier from the subscription's actual price ID and checkout metadata.
|
|
245
|
-
*
|
|
246
|
-
* Price ID takes precedence — it cannot be spoofed because it comes from
|
|
247
|
-
* Stripe's own subscription record, not from client-supplied data.
|
|
248
|
-
* Metadata is a fallback for products that predate the env-var tier mapping.
|
|
249
|
-
*/
|
|
250
|
-
function resolveTierFromMetadata(metadata, priceId) {
|
|
251
|
-
// Primary: validate against known price IDs from environment
|
|
252
|
-
if (priceId) {
|
|
253
|
-
const proPriceId = process.env.STRIPE_PRO_PRICE_ID;
|
|
254
|
-
const maxPriceId = process.env.STRIPE_MAX_PRICE_ID;
|
|
255
|
-
const enterprisePriceId = process.env.STRIPE_ENTERPRISE_PRICE_ID;
|
|
256
|
-
if (proPriceId && priceId === proPriceId)
|
|
257
|
-
return 'pro';
|
|
258
|
-
if (maxPriceId && priceId === maxPriceId)
|
|
259
|
-
return 'max';
|
|
260
|
-
if (enterprisePriceId && priceId === enterprisePriceId)
|
|
261
|
-
return 'enterprise';
|
|
262
|
-
}
|
|
263
|
-
// Fallback: metadata set server-side during checkout session creation (not customer-supplied)
|
|
264
|
-
const tier = metadata?.tier;
|
|
265
|
-
if (tier === 'enterprise')
|
|
266
|
-
return 'enterprise';
|
|
267
|
-
if (tier === 'max')
|
|
268
|
-
return 'max';
|
|
269
|
-
if (tier === 'pro')
|
|
270
|
-
return 'pro';
|
|
271
|
-
// No tier could be resolved — this is a configuration error.
|
|
272
|
-
// Throw so Stripe retries (500) and the team can investigate.
|
|
273
|
-
logger.error('Cannot resolve license tier — unknown price ID and no metadata', {
|
|
274
|
-
priceId,
|
|
275
|
-
metadata,
|
|
276
|
-
});
|
|
277
|
-
throw new Error(`Cannot resolve license tier: price ID "${priceId}" does not match any configured tier. ` +
|
|
278
|
-
'Check STRIPE_PRO_PRICE_ID, STRIPE_MAX_PRICE_ID, and STRIPE_ENTERPRISE_PRICE_ID env vars.');
|
|
279
|
-
}
|
|
280
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/api/webhooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,kBAAkB,EAAoB,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EACL,0BAA0B,EAC1B,6BAA6B,EAC7B,2BAA2B,EAC3B,8BAA8B,EAC9B,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,iBAAiB;IACjB,iBAAiB;IACjB,eAAe;IACf,eAAe;IACf,4BAA4B;IAC5B,+BAA+B;IAC/B,+BAA+B;IAC/B,+BAA+B;IAC/B,2BAA2B;IAC3B,wBAAwB;IACxB,yBAAyB;CAC1B,CAAC,CAAC;AAEH,+FAA+F;AAC/F,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAExC;;;;GAIG;AACH,SAAS,gBAAgB;IACvB,MAAM,MAAM,GACV,CAAC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,0BAA0B,KAAK,QAAQ;QAC7D,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,0BAA0B;QAC5C,CAAC,CAAC,SAAS,CAAC;QACd,CAAC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,qBAAqB,KAAK,QAAQ;YACxD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,qBAAqB;YACvC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEjB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,0EAA0E;YACxE,8DAA8D,CACjE,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,uBAAuB,CAAC,OAAe;IACpD,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IACvB,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE;SACxB,MAAM,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,EAAE,EAAE,CAAC;SACzC,IAAI,CAAC,sBAAsB,CAAC;SAC5B,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;SAC7C,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,CAAC,CAAC,QAAQ,CAAC;AACpB,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,OAAe,EAAE,SAAiB;IAClE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IACvB,MAAM,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,mBAAmB,EAAE,CAAC;AACnG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAgB;IACzC,MAAM,QAAQ,GAAG,6BAA6B,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,IAAI,QAAQ,CAAC,+BAA+B,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,aAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACjE,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9D,OAAO,IAAI,QAAQ,CAAC,iCAAiC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,yEAAyE;IACzE,8DAA8D;IAC9D,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC5D,IAAI,aAAa,KAAK,IAAI,IAAI,MAAM,CAAC,aAAa,CAAC,GAAG,cAAc,EAAE,CAAC;QACrE,OAAO,IAAI,QAAQ,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;IAClC,IAAI,IAAI,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QACjC,OAAO,IAAI,QAAQ,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAEpD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,QAAQ,CAAC,iCAAiC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,KAAmB,CAAC;IACxB,IAAI,CAAC;QACH,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC;IAC5E,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,YAAY,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,uCAAuC,EAAE;YACpD,KAAK,EAAE,YAAY;SACpB,CAAC,CAAC;QACH,OAAO,IAAI,QAAQ,CAAC,kBAAkB,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,mDAAmD;IACnD,IAAI,MAAM,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACxE,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,2EAA2E;YAC3E,sDAAsD;YACtD,MAAM,kBAAkB,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAE/C,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;gBACnB,KAAK,iBAAiB,CAAC;gBACvB,KAAK,iBAAiB,CAAC,CAAC,CAAC;oBACvB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;oBAClC,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;wBACjC,MAAM,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oBAC/C,CAAC;oBACD,MAAM;gBACR,CAAC;gBACD,KAAK,eAAe,CAAC;gBACrB,KAAK,eAAe,CAAC,CAAC,CAAC;oBACrB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;oBAChC,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;wBAC7B,MAAM,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;oBAC3C,CAAC;oBACD,MAAM;gBACR,CAAC;gBACD,KAAK,+BAA+B,CAAC;gBACrC,KAAK,+BAA+B,CAAC;gBACrC,KAAK,+BAA+B,CAAC,CAAC,CAAC;oBACrC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;oBACvC,IAAI,YAAY,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;wBAC3C,MAAM,UAAU,GACd,OAAO,YAAY,CAAC,QAAQ,KAAK,QAAQ;4BACvC,CAAC,CAAC,YAAY,CAAC,QAAQ;4BACvB,CAAC,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;wBAChC,IAAI,CAAC,UAAU,EAAE,CAAC;4BAChB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;wBACnD,CAAC;wBAED,MAAM,8BAA8B,CAClC,YAAY,CAAC,EAAE,EACf,UAAU,EACV,KAAK,CAAC,IAAI,KAAK,+BAA+B,EAC9C,QAAQ,CACT,CAAC;wBAEF,2DAA2D;wBAC3D,IAAI,KAAK,CAAC,IAAI,KAAK,+BAA+B,EAAE,CAAC;4BACnD,MAAM,CAAC,IAAI,CAAC,kDAAkD,EAAE;gCAC9D,UAAU;gCACV,cAAc,EAAE,YAAY,CAAC,EAAE;6BAChC,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;oBACD,MAAM;gBACR,CAAC;gBACD,KAAK,4BAA4B,CAAC,CAAC,CAAC;oBAClC,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;oBAC1C,IAAI,eAAe,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;wBAClD,MAAM,OAAO,GAAG,eAAe,CAAC;wBAChC,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;4BAC5D,MAAM,cAAc,GAClB,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ;gCACtC,CAAC,CAAC,OAAO,CAAC,YAAY;gCACtB,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;4BAC9B,MAAM,UAAU,GACd,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;4BACjF,IAAI,cAAc,IAAI,UAAU,EAAE,CAAC;gCACjC,MAAM,8BAA8B,CAAC,cAAc,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;gCAEjF,gCAAgC;gCAChC,kFAAkF;gCAClF,kEAAkE;gCAClE,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;gCAElF,IAAI,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC;oCACvC,gEAAgE;oCAChE,MAAM,CAAC,IAAI,CAAC,+DAA+D,EAAE;wCAC3E,UAAU;wCACV,cAAc;qCACf,CAAC,CAAC;gCACL,CAAC;qCAAM,CAAC;oCACN,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,IAAI,CAAC;oCAC9D,MAAM,IAAI,GAAG,uBAAuB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oCAChE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;oCAE5D,IAAI,CAAC,UAAU,EAAE,CAAC;wCAChB,uDAAuD;wCACvD,2DAA2D;wCAC3D,MAAM,CAAC,KAAK,CACV,0EAA0E;4CACxE,+BAA+B,EACjC,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,CACrC,CAAC;oCACJ,CAAC;yCAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;wCAC3B,IAAI,CAAC;4CACH,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,UAAU,CAAC,CAAC;4CAC9E,MAAM,eAAe,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE;gDACzD,QAAQ,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE;6CAC1D,CAAC,CAAC;4CACH,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;wCAC1E,CAAC;wCAAC,OAAO,UAAU,EAAE,CAAC;4CACpB,MAAM,CAAC,KAAK,CACV,iEAAiE;gDAC/D,+BAA+B,EACjC;gDACE,KAAK,EAAE,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;gDACzE,UAAU;gDACV,cAAc;gDACd,IAAI;6CACL,CACF,CAAC;wCACJ,CAAC;oCACH,CAAC;gCACH,CAAC;gCAED,MAAM,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;4BAChD,CAAC;wBACH,CAAC;oBACH,CAAC;oBACD,MAAM;gBACR,CAAC;gBACD,KAAK,2BAA2B,CAAC,CAAC,CAAC;oBACjC,MAAM,YAAY,GAAG,KAGpB,CAAC;oBACF,MAAM,6BAA6B,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;oBAC5D,MAAM;gBACR,CAAC;gBACD,KAAK,wBAAwB,CAAC,CAAC,CAAC;oBAC9B,MAAM,YAAY,GAAG,KAGpB,CAAC;oBACF,MAAM,0BAA0B,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;oBACzD,MAAM;gBACR,CAAC;gBACD,KAAK,yBAAyB,CAAC,CAAC,CAAC;oBAC/B,MAAM,OAAO,GAAG,KAGf,CAAC;oBACF,MAAM,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;oBACrD,MAAM;gBACR,CAAC;gBACD;oBACE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YAC9E,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;YAE/D,OAAO,IAAI,QAAQ,CACjB,8DAA8D,YAAY,EAAE,EAC5E;gBACE,MAAM,EAAE,GAAG;aACZ,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;;GAMG;AACH,SAAS,uBAAuB,CAC9B,QAAmD,EACnD,OAAuB;IAEvB,6DAA6D;IAC7D,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QACnD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QACnD,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;QACjE,IAAI,UAAU,IAAI,OAAO,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;QACvD,IAAI,UAAU,IAAI,OAAO,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;QACvD,IAAI,iBAAiB,IAAI,OAAO,KAAK,iBAAiB;YAAE,OAAO,YAAY,CAAC;IAC9E,CAAC;IACD,8FAA8F;IAC9F,MAAM,IAAI,GAAG,QAAQ,EAAE,IAAI,CAAC;IAC5B,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,YAAY,CAAC;IAC/C,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAEjC,6DAA6D;IAC7D,8DAA8D;IAC9D,MAAM,CAAC,KAAK,CAAC,gEAAgE,EAAE;QAC7E,OAAO;QACP,QAAQ;KACT,CAAC,CAAC;IACH,MAAM,IAAI,KAAK,CACb,0CAA0C,OAAO,wCAAwC;QACvF,0FAA0F,CAC7F,CAAC;AACJ,CAAC"}
|
package/dist/stripe/billing.d.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stripe Billing Meter Utilities
|
|
3
|
-
*
|
|
4
|
-
* Reusable helpers for reporting usage to Stripe Billing Meters.
|
|
5
|
-
* Replaces the deprecated `subscriptionItems.createUsageRecord()` API with
|
|
6
|
-
* `billing.meterEvents.create()`.
|
|
7
|
-
*
|
|
8
|
-
* ## Prerequisites (Stripe Dashboard)
|
|
9
|
-
*
|
|
10
|
-
* Before this module can send meter events, the account owner must:
|
|
11
|
-
*
|
|
12
|
-
* 1. Create a **Billing Meter** in Stripe Dashboard -> Billing -> Meters.
|
|
13
|
-
* Set the `event_name` to a descriptive slug (e.g. `agent_task_overage`).
|
|
14
|
-
* Configure `customer_mapping.event_payload_key` = `stripe_customer_id`
|
|
15
|
-
* and `value_settings.event_payload_key` = `value`.
|
|
16
|
-
*
|
|
17
|
-
* 2. Create a **metered Price** linked to the Meter. Attach the Price to
|
|
18
|
-
* subscription line items for customers who should be billed for overages.
|
|
19
|
-
*
|
|
20
|
-
* 3. Set the `STRIPE_AGENT_METER_EVENT_NAME` environment variable to the
|
|
21
|
-
* `event_name` configured in step 1. If the variable is missing, all
|
|
22
|
-
* reporting functions return early without error (graceful degradation).
|
|
23
|
-
*/
|
|
24
|
-
import type Stripe from 'stripe';
|
|
25
|
-
/** Configuration for the Stripe Billing Meter reporter. */
|
|
26
|
-
export interface MeterReporterConfig {
|
|
27
|
-
/**
|
|
28
|
-
* The Stripe meter event name (must match the Meter created in the Stripe Dashboard).
|
|
29
|
-
* When `undefined`, all reporting is silently skipped (owner setup not complete).
|
|
30
|
-
*/
|
|
31
|
-
meterEventName: string | undefined;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Override the default meter reporter configuration.
|
|
35
|
-
* Useful for tests or multi-tenant setups with different meter names.
|
|
36
|
-
*/
|
|
37
|
-
export declare function configureMeterReporter(overrides: Partial<MeterReporterConfig>): void;
|
|
38
|
-
/**
|
|
39
|
-
* Reset the meter reporter configuration to lazy env-based defaults.
|
|
40
|
-
* Primarily for testing.
|
|
41
|
-
*/
|
|
42
|
-
export declare function resetMeterReporterConfig(): void;
|
|
43
|
-
/**
|
|
44
|
-
* Computes a Stripe meter event timestamp for the last second of a billing cycle.
|
|
45
|
-
*
|
|
46
|
-
* Stripe Billing Meters require event timestamps to fall within the billing period
|
|
47
|
-
* they are associated with. Since overage is reported for the *previous* calendar
|
|
48
|
-
* month, the timestamp must be within that month -- not the current month when the
|
|
49
|
-
* cron runs.
|
|
50
|
-
*
|
|
51
|
-
* Takes the cycle start (1st of the previous month at 00:00 UTC), adds ~30 days
|
|
52
|
-
* (one calendar-month approximation), then subtracts 1 second to land on the last
|
|
53
|
-
* second of that cycle. Stripe only checks that the timestamp falls within the
|
|
54
|
-
* subscription's billing interval.
|
|
55
|
-
*
|
|
56
|
-
* @param cycleStart - The first day of the billing cycle (UTC midnight)
|
|
57
|
-
* @returns Unix timestamp (seconds) for the last second of the approximate cycle
|
|
58
|
-
*/
|
|
59
|
-
export declare function getMeterEventTimestamp(cycleStart: Date): number;
|
|
60
|
-
/** Result of a single meter event report attempt. */
|
|
61
|
-
export interface MeterEventResult {
|
|
62
|
-
/** Whether the event was successfully sent to Stripe. */
|
|
63
|
-
success: boolean;
|
|
64
|
-
/** Error message if the event failed (undefined on success). */
|
|
65
|
-
error?: string;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Report a single meter event to a Stripe Billing Meter.
|
|
69
|
-
*
|
|
70
|
-
* Uses `stripe.billing.meterEvents.create()` (the replacement for the
|
|
71
|
-
* deprecated `subscriptionItems.createUsageRecord()` API).
|
|
72
|
-
*
|
|
73
|
-
* @param stripe - A Stripe client instance (raw or protected)
|
|
74
|
-
* @param params - The meter event parameters
|
|
75
|
-
* @returns Result indicating success or failure
|
|
76
|
-
*/
|
|
77
|
-
export declare function reportMeterEvent(stripe: Pick<Stripe, 'billing'>, params: {
|
|
78
|
-
/** Stripe customer ID to attribute the usage to. */
|
|
79
|
-
stripeCustomerId: string;
|
|
80
|
-
/** Usage value (quantity). Will be stringified for the Stripe payload. */
|
|
81
|
-
value: number;
|
|
82
|
-
/**
|
|
83
|
-
* Optional Unix timestamp (seconds) for the event.
|
|
84
|
-
* Defaults to current time if not provided.
|
|
85
|
-
* Use `getMeterEventTimestamp()` for retroactive billing cycle events.
|
|
86
|
-
*/
|
|
87
|
-
timestamp?: number;
|
|
88
|
-
/**
|
|
89
|
-
* Optional idempotency identifier for the event.
|
|
90
|
-
* Stripe enforces uniqueness within a rolling 24-hour period.
|
|
91
|
-
*/
|
|
92
|
-
identifier?: string;
|
|
93
|
-
}): Promise<MeterEventResult>;
|
|
94
|
-
/** Summary returned by `reportMeterEventBatch`. */
|
|
95
|
-
export interface MeterEventBatchResult {
|
|
96
|
-
/** Number of events successfully sent. */
|
|
97
|
-
reported: number;
|
|
98
|
-
/** Number of events that were skipped or failed. */
|
|
99
|
-
skipped: number;
|
|
100
|
-
}
|
|
101
|
-
/** A single overage row to be reported. */
|
|
102
|
-
export interface OverageRow {
|
|
103
|
-
/** Stripe customer ID (null/undefined = skip). */
|
|
104
|
-
stripeCustomerId: string | null | undefined;
|
|
105
|
-
/** Overage quantity to report. */
|
|
106
|
-
overage: number;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Report a batch of overage meter events to Stripe.
|
|
110
|
-
*
|
|
111
|
-
* Iterates over `rows`, skipping any without a `stripeCustomerId`, and
|
|
112
|
-
* calls `reportMeterEvent` for each valid row. Failures are counted as
|
|
113
|
-
* skipped (non-throwing).
|
|
114
|
-
*
|
|
115
|
-
* @param stripe - A Stripe client instance (raw or protected)
|
|
116
|
-
* @param rows - Overage rows to report
|
|
117
|
-
* @param cycleStart - The billing cycle start date (used for timestamp attribution)
|
|
118
|
-
* @returns Batch summary with reported/skipped counts
|
|
119
|
-
*/
|
|
120
|
-
export declare function reportMeterEventBatch(stripe: Pick<Stripe, 'billing'>, rows: ReadonlyArray<OverageRow>, cycleStart: Date): Promise<MeterEventBatchResult>;
|
|
121
|
-
//# sourceMappingURL=billing.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../src/stripe/billing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAQjC,2DAA2D;AAC3D,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAgBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAEpF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,IAAI,GAAG,MAAM,CAG/D;AAMD,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,EAC/B,MAAM,EAAE;IACN,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,gBAAgB,CAAC,CAsC3B;AAMD,mDAAmD;AACnD,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC5C,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,EAC/B,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,EAC/B,UAAU,EAAE,IAAI,GACf,OAAO,CAAC,qBAAqB,CAAC,CA6BhC"}
|
package/dist/stripe/billing.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stripe Billing Meter Utilities
|
|
3
|
-
*
|
|
4
|
-
* Reusable helpers for reporting usage to Stripe Billing Meters.
|
|
5
|
-
* Replaces the deprecated `subscriptionItems.createUsageRecord()` API with
|
|
6
|
-
* `billing.meterEvents.create()`.
|
|
7
|
-
*
|
|
8
|
-
* ## Prerequisites (Stripe Dashboard)
|
|
9
|
-
*
|
|
10
|
-
* Before this module can send meter events, the account owner must:
|
|
11
|
-
*
|
|
12
|
-
* 1. Create a **Billing Meter** in Stripe Dashboard -> Billing -> Meters.
|
|
13
|
-
* Set the `event_name` to a descriptive slug (e.g. `agent_task_overage`).
|
|
14
|
-
* Configure `customer_mapping.event_payload_key` = `stripe_customer_id`
|
|
15
|
-
* and `value_settings.event_payload_key` = `value`.
|
|
16
|
-
*
|
|
17
|
-
* 2. Create a **metered Price** linked to the Meter. Attach the Price to
|
|
18
|
-
* subscription line items for customers who should be billed for overages.
|
|
19
|
-
*
|
|
20
|
-
* 3. Set the `STRIPE_AGENT_METER_EVENT_NAME` environment variable to the
|
|
21
|
-
* `event_name` configured in step 1. If the variable is missing, all
|
|
22
|
-
* reporting functions return early without error (graceful degradation).
|
|
23
|
-
*/
|
|
24
|
-
import { createLogger } from '@revealui/core/observability/logger';
|
|
25
|
-
const logger = createLogger({ service: 'StripeBilling' });
|
|
26
|
-
/**
|
|
27
|
-
* Explicit override (set via `configureMeterReporter`).
|
|
28
|
-
* When `null`, the meter event name is resolved lazily from
|
|
29
|
-
* `process.env.STRIPE_AGENT_METER_EVENT_NAME` on every call so that tests
|
|
30
|
-
* manipulating the env var between cases work without resetting module state.
|
|
31
|
-
*/
|
|
32
|
-
let overrideConfig = null;
|
|
33
|
-
/** Resolve the effective configuration (explicit override or env-based default). */
|
|
34
|
-
function resolveConfig() {
|
|
35
|
-
if (overrideConfig)
|
|
36
|
-
return overrideConfig;
|
|
37
|
-
return { meterEventName: process.env.STRIPE_AGENT_METER_EVENT_NAME };
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Override the default meter reporter configuration.
|
|
41
|
-
* Useful for tests or multi-tenant setups with different meter names.
|
|
42
|
-
*/
|
|
43
|
-
export function configureMeterReporter(overrides) {
|
|
44
|
-
overrideConfig = { meterEventName: process.env.STRIPE_AGENT_METER_EVENT_NAME, ...overrides };
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Reset the meter reporter configuration to lazy env-based defaults.
|
|
48
|
-
* Primarily for testing.
|
|
49
|
-
*/
|
|
50
|
-
export function resetMeterReporterConfig() {
|
|
51
|
-
overrideConfig = null;
|
|
52
|
-
}
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// Timestamp helper
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
/**
|
|
57
|
-
* Computes a Stripe meter event timestamp for the last second of a billing cycle.
|
|
58
|
-
*
|
|
59
|
-
* Stripe Billing Meters require event timestamps to fall within the billing period
|
|
60
|
-
* they are associated with. Since overage is reported for the *previous* calendar
|
|
61
|
-
* month, the timestamp must be within that month -- not the current month when the
|
|
62
|
-
* cron runs.
|
|
63
|
-
*
|
|
64
|
-
* Takes the cycle start (1st of the previous month at 00:00 UTC), adds ~30 days
|
|
65
|
-
* (one calendar-month approximation), then subtracts 1 second to land on the last
|
|
66
|
-
* second of that cycle. Stripe only checks that the timestamp falls within the
|
|
67
|
-
* subscription's billing interval.
|
|
68
|
-
*
|
|
69
|
-
* @param cycleStart - The first day of the billing cycle (UTC midnight)
|
|
70
|
-
* @returns Unix timestamp (seconds) for the last second of the approximate cycle
|
|
71
|
-
*/
|
|
72
|
-
export function getMeterEventTimestamp(cycleStart) {
|
|
73
|
-
const SECONDS_IN_30_DAYS = 30 * 24 * 60 * 60;
|
|
74
|
-
return Math.floor(cycleStart.getTime() / 1000 + SECONDS_IN_30_DAYS - 1);
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Report a single meter event to a Stripe Billing Meter.
|
|
78
|
-
*
|
|
79
|
-
* Uses `stripe.billing.meterEvents.create()` (the replacement for the
|
|
80
|
-
* deprecated `subscriptionItems.createUsageRecord()` API).
|
|
81
|
-
*
|
|
82
|
-
* @param stripe - A Stripe client instance (raw or protected)
|
|
83
|
-
* @param params - The meter event parameters
|
|
84
|
-
* @returns Result indicating success or failure
|
|
85
|
-
*/
|
|
86
|
-
export async function reportMeterEvent(stripe, params) {
|
|
87
|
-
const eventName = resolveConfig().meterEventName;
|
|
88
|
-
if (!eventName) {
|
|
89
|
-
logger.debug('Meter event skipped: STRIPE_AGENT_METER_EVENT_NAME not configured');
|
|
90
|
-
return { success: false, error: 'meter_not_configured' };
|
|
91
|
-
}
|
|
92
|
-
const meterParams = {
|
|
93
|
-
event_name: eventName,
|
|
94
|
-
payload: {
|
|
95
|
-
stripe_customer_id: params.stripeCustomerId,
|
|
96
|
-
value: String(params.value),
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
if (params.timestamp !== undefined) {
|
|
100
|
-
meterParams.timestamp = params.timestamp;
|
|
101
|
-
}
|
|
102
|
-
if (params.identifier !== undefined) {
|
|
103
|
-
meterParams.identifier = params.identifier;
|
|
104
|
-
}
|
|
105
|
-
try {
|
|
106
|
-
await stripe.billing.meterEvents.create(meterParams);
|
|
107
|
-
return { success: true };
|
|
108
|
-
}
|
|
109
|
-
catch (err) {
|
|
110
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
111
|
-
logger.error('Failed to create Stripe meter event', err instanceof Error ? err : undefined, {
|
|
112
|
-
event_name: eventName,
|
|
113
|
-
stripe_customer_id: params.stripeCustomerId,
|
|
114
|
-
value: params.value,
|
|
115
|
-
});
|
|
116
|
-
return { success: false, error: message };
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Report a batch of overage meter events to Stripe.
|
|
121
|
-
*
|
|
122
|
-
* Iterates over `rows`, skipping any without a `stripeCustomerId`, and
|
|
123
|
-
* calls `reportMeterEvent` for each valid row. Failures are counted as
|
|
124
|
-
* skipped (non-throwing).
|
|
125
|
-
*
|
|
126
|
-
* @param stripe - A Stripe client instance (raw or protected)
|
|
127
|
-
* @param rows - Overage rows to report
|
|
128
|
-
* @param cycleStart - The billing cycle start date (used for timestamp attribution)
|
|
129
|
-
* @returns Batch summary with reported/skipped counts
|
|
130
|
-
*/
|
|
131
|
-
export async function reportMeterEventBatch(stripe, rows, cycleStart) {
|
|
132
|
-
if (!resolveConfig().meterEventName) {
|
|
133
|
-
return { reported: 0, skipped: 0 };
|
|
134
|
-
}
|
|
135
|
-
const timestamp = getMeterEventTimestamp(cycleStart);
|
|
136
|
-
let reported = 0;
|
|
137
|
-
let skipped = 0;
|
|
138
|
-
for (const row of rows) {
|
|
139
|
-
if (!row.stripeCustomerId) {
|
|
140
|
-
skipped++;
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const result = await reportMeterEvent(stripe, {
|
|
144
|
-
stripeCustomerId: row.stripeCustomerId,
|
|
145
|
-
value: row.overage,
|
|
146
|
-
timestamp,
|
|
147
|
-
});
|
|
148
|
-
if (result.success) {
|
|
149
|
-
reported++;
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
skipped++;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return { reported, skipped };
|
|
156
|
-
}
|
|
157
|
-
//# sourceMappingURL=billing.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/stripe/billing.ts"],"names":[],"mappings":"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"}
|