@delightstack/stripe 0.1.0 → 0.2.0

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.
@@ -16,8 +16,11 @@ export interface PlanDefinition {
16
16
  amount: number;
17
17
  /** Currency code (lowercase) @default 'usd' */
18
18
  currency?: string;
19
- /** Billing interval */
20
- interval: 'month' | 'year' | 'week' | 'day';
19
+ /**
20
+ * Billing interval. Omit for a ONE-TIME plan (single payment, `mode: 'payment'`
21
+ * checkout) — grant its effects via the `onOneTimePurchase` hook.
22
+ */
23
+ interval?: 'month' | 'year' | 'week' | 'day';
21
24
  /** Number of intervals between billings @default 1 */
22
25
  interval_count?: number;
23
26
  /**
@@ -154,6 +157,23 @@ export interface BillingConfig<E extends string = string> {
154
157
  entitlements: string[];
155
158
  event: RequestEvent;
156
159
  }) => void | Promise<void>;
160
+ /**
161
+ * Called when a one-time plan's checkout completes (`mode: 'payment'`).
162
+ * The package cannot know what a purchase grants (a credit, a timed pass,
163
+ * a permanent entitlement) — apply it here. `amount` is an integer in the
164
+ * smallest currency unit (cents).
165
+ */
166
+ onOneTimePurchase?: (ctx: {
167
+ customer_id: string;
168
+ /** Resolved from the customer's metadata; null when billing is user-scoped or unknown */
169
+ org_id: string | null;
170
+ plan_id: string;
171
+ /** Integer amount in the smallest currency unit (e.g. cents) */
172
+ amount: number;
173
+ currency: string;
174
+ checkout_session_id: string;
175
+ event: RequestEvent;
176
+ }) => void | Promise<void>;
157
177
  /** Called after a payment succeeds. `amount` is an integer in the smallest currency unit (cents). */
158
178
  onPaymentSuccess?: (ctx: {
159
179
  customer_id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"billing.config.d.ts","sourceRoot":"","sources":["../../src/server/billing.config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"billing.config.d.ts","sourceRoot":"","sources":["../../src/server/billing.config.ts"],"names":[],"mappings":"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"}
@@ -45,16 +45,17 @@ export async function syncProducts(config) {
45
45
  }
46
46
  // 2. Ensure price exists with the correct lookup_key
47
47
  const prices = await stripeCall(() => stripe.prices.list({ lookup_keys: [plan.lookup_key], limit: 1 }));
48
+ // One-time plans (no interval) create a non-recurring price
49
+ const recurring = plan.interval
50
+ ? { interval: plan.interval, interval_count: plan.interval_count ?? 1 }
51
+ : undefined;
48
52
  if (!prices.data.length) {
49
53
  // Create price with lookup_key
50
54
  await stripeCall(() => stripe.prices.create({
51
55
  product: product.id,
52
56
  unit_amount: plan.amount,
53
57
  currency: plan.currency ?? 'usd',
54
- recurring: {
55
- interval: plan.interval,
56
- interval_count: plan.interval_count ?? 1,
57
- },
58
+ ...(recurring ? { recurring } : {}),
58
59
  lookup_key: plan.lookup_key,
59
60
  transfer_lookup_key: true,
60
61
  metadata: { plan_id: plan.id },
@@ -65,16 +66,14 @@ export async function syncProducts(config) {
65
66
  // and transfer the lookup_key
66
67
  const existing_price = prices.data[0];
67
68
  if (existing_price.unit_amount !== plan.amount ||
68
- existing_price.recurring?.interval !== plan.interval ||
69
- (existing_price.recurring?.interval_count ?? 1) !== (plan.interval_count ?? 1)) {
69
+ (existing_price.recurring?.interval ?? undefined) !== plan.interval ||
70
+ (plan.interval &&
71
+ (existing_price.recurring?.interval_count ?? 1) !== (plan.interval_count ?? 1))) {
70
72
  await stripeCall(() => stripe.prices.create({
71
73
  product: product.id,
72
74
  unit_amount: plan.amount,
73
75
  currency: plan.currency ?? 'usd',
74
- recurring: {
75
- interval: plan.interval,
76
- interval_count: plan.interval_count ?? 1,
77
- },
76
+ ...(recurring ? { recurring } : {}),
78
77
  lookup_key: plan.lookup_key,
79
78
  transfer_lookup_key: true,
80
79
  metadata: { plan_id: plan.id },
@@ -1 +1 @@
1
- {"version":3,"file":"billing.routes.d.ts","sourceRoot":"","sources":["../../src/server/billing.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EACX,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,MAAM,kBAAkB,CAAC;AAa1B,MAAM,WAAW,YAAY;IAC5B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,aAAa,GAAG,SAAS,CAAC;IACnE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,YAAY,GAAG,SAAS,CAAC;CACjE;AAoLD,wBAAsB,kBAAkB,CACvC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,qBAAqB,EAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,YAAY,GACf,OAAO,CAAC,QAAQ,CAAC,CA+XnB"}
1
+ {"version":3,"file":"billing.routes.d.ts","sourceRoot":"","sources":["../../src/server/billing.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EACX,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,MAAM,kBAAkB,CAAC;AAa1B,MAAM,WAAW,YAAY;IAC5B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,aAAa,GAAG,SAAS,CAAC;IACnE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,YAAY,GAAG,SAAS,CAAC;CACjE;AA4LD,wBAAsB,kBAAkB,CACvC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,qBAAqB,EAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,YAAY,GACf,OAAO,CAAC,QAAQ,CAAC,CAwYnB"}
@@ -379,19 +379,25 @@ export async function handleBillingRoute(event, config, route_path, method, ctx)
379
379
  const price = prices.data[0];
380
380
  if (!price)
381
381
  throw DelightError.badRequest(`Price not found for plan: ${plan_id}`);
382
+ // Plans without an interval are one-time purchases (`mode: 'payment'`).
383
+ // plan_id metadata lets the webhook resolve the plan for onOneTimePurchase.
384
+ const one_time = !plan.interval;
382
385
  const session = await stripeCall(() => stripe.checkout.sessions.create({
383
- mode: 'subscription',
386
+ mode: one_time ? 'payment' : 'subscription',
384
387
  customer: customer_id,
385
388
  line_items: [{ price: price.id, quantity: 1 }],
386
389
  ui_mode: 'embedded',
387
390
  return_url,
388
- ...(plan.trial_days
389
- ? {
390
- subscription_data: {
391
- trial_period_days: plan.trial_days,
392
- },
393
- }
394
- : {}),
391
+ metadata: { plan_id: plan.id },
392
+ ...(one_time
393
+ ? { invoice_creation: { enabled: true } }
394
+ : plan.trial_days
395
+ ? {
396
+ subscription_data: {
397
+ trial_period_days: plan.trial_days,
398
+ },
399
+ }
400
+ : {}),
395
401
  }));
396
402
  return jsonResponse({ client_secret: session.client_secret });
397
403
  }
@@ -1 +1 @@
1
- {"version":3,"file":"billing.webhook.d.ts","sourceRoot":"","sources":["../../src/server/billing.webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EACX,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,MAAM,kBAAkB,CAAC;AAM1B,MAAM,WAAW,cAAc;IAC9B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,aAAa,GAAG,SAAS,CAAC;IACnE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,YAAY,GAAG,SAAS,CAAC;CACjE;AASD,6DAA6D;AAC7D,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAuED,wBAAsB,aAAa,CAClC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,qBAAqB,EAC7B,GAAG,EAAE,cAAc,GACjB,OAAO,CAAC,QAAQ,CAAC,CAwKnB"}
1
+ {"version":3,"file":"billing.webhook.d.ts","sourceRoot":"","sources":["../../src/server/billing.webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EACX,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,MAAM,kBAAkB,CAAC;AAM1B,MAAM,WAAW,cAAc;IAC9B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,aAAa,GAAG,SAAS,CAAC;IACnE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,YAAY,GAAG,SAAS,CAAC;CACjE;AASD,6DAA6D;AAC7D,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAuED,wBAAsB,aAAa,CAClC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,qBAAqB,EAC7B,GAAG,EAAE,cAAc,GACjB,OAAO,CAAC,QAAQ,CAAC,CA2LnB"}
@@ -203,6 +203,26 @@ export async function handleWebhook(event, config, ctx) {
203
203
  ws: org_id ? ctx.getWebsocket?.(event) : undefined,
204
204
  });
205
205
  }
206
+ else if (session.mode === 'payment' && session.customer) {
207
+ // One-time plan purchase — the checkout route stamped plan_id metadata.
208
+ // The package can't know what the purchase grants; the app applies it
209
+ // in the onOneTimePurchase hook (credit, timed pass, entitlement, ...).
210
+ const plan_id = session.metadata?.plan_id;
211
+ const plan = plan_id ? config.plans?.find((p) => p.id === plan_id) : undefined;
212
+ if (plan && config.hooks?.onOneTimePurchase) {
213
+ const customer_id = extractCustomerId(session.customer);
214
+ const org_id = await resolveOrgIdFromCustomer(stripe, customer_id);
215
+ await config.hooks.onOneTimePurchase({
216
+ customer_id,
217
+ org_id: org_id ?? null,
218
+ plan_id: plan.id,
219
+ amount: session.amount_total ?? plan.amount,
220
+ currency: session.currency ?? plan.currency ?? 'usd',
221
+ checkout_session_id: session.id,
222
+ event,
223
+ });
224
+ }
225
+ }
206
226
  break;
207
227
  }
208
228
  default:
@@ -19,7 +19,8 @@ export interface PlanInfo {
19
19
  description?: string;
20
20
  amount: number;
21
21
  currency: string;
22
- interval: 'month' | 'year' | 'week' | 'day';
22
+ /** Absent for one-time plans */
23
+ interval?: 'month' | 'year' | 'week' | 'day';
23
24
  interval_count: number;
24
25
  entitlements: string[];
25
26
  trial_days?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"billing.type.d.ts","sourceRoot":"","sources":["../../src/types/billing.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,0FAA0F;AAC1F,MAAM,WAAW,iBAAiB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mCAAmC;AACnC,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,uDAAuD;AACvD,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC"}
1
+ {"version":3,"file":"billing.type.d.ts","sourceRoot":"","sources":["../../src/types/billing.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,0FAA0F;AAC1F,MAAM,WAAW,iBAAiB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mCAAmC;AACnC,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,uDAAuD;AACvD,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delightstack/stripe",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Stripe billing for Cloudflare Workers and SvelteKit — products, metered usage, webhooks, and subscription guards.",