@nextsparkjs/core 0.1.0-beta.100 → 0.1.0-beta.102

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.
Files changed (77) hide show
  1. package/dist/components/auth/forms/LoginForm.d.ts.map +1 -1
  2. package/dist/components/auth/forms/LoginForm.js +15 -10
  3. package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
  4. package/dist/components/auth/forms/SignupForm.js +27 -24
  5. package/dist/components/auth/pages/AuthErrorPage.d.ts +2 -0
  6. package/dist/components/auth/pages/AuthErrorPage.d.ts.map +1 -0
  7. package/dist/components/auth/pages/AuthErrorPage.js +44 -0
  8. package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
  9. package/dist/lib/api/entity/generic-handler.js +13 -3
  10. package/dist/lib/auth/registration-guard-plugin.d.ts +14 -0
  11. package/dist/lib/auth/registration-guard-plugin.d.ts.map +1 -0
  12. package/dist/lib/auth/registration-guard-plugin.js +37 -0
  13. package/dist/lib/auth/registration-helpers.d.ts +65 -0
  14. package/dist/lib/auth/registration-helpers.d.ts.map +1 -0
  15. package/dist/lib/auth/registration-helpers.js +51 -0
  16. package/dist/lib/auth.d.ts.map +1 -1
  17. package/dist/lib/auth.js +54 -1
  18. package/dist/lib/billing/config-types.d.ts +5 -0
  19. package/dist/lib/billing/config-types.d.ts.map +1 -1
  20. package/dist/lib/billing/gateways/factory.d.ts +25 -0
  21. package/dist/lib/billing/gateways/factory.d.ts.map +1 -0
  22. package/dist/lib/billing/gateways/factory.js +34 -0
  23. package/dist/lib/billing/gateways/interface.d.ts +20 -0
  24. package/dist/lib/billing/gateways/interface.d.ts.map +1 -0
  25. package/dist/lib/billing/gateways/polar.d.ts +47 -0
  26. package/dist/lib/billing/gateways/polar.d.ts.map +1 -0
  27. package/dist/lib/billing/gateways/polar.js +150 -0
  28. package/dist/lib/billing/gateways/stripe.d.ts +40 -65
  29. package/dist/lib/billing/gateways/stripe.d.ts.map +1 -1
  30. package/dist/lib/billing/gateways/stripe.js +134 -62
  31. package/dist/lib/billing/gateways/types.d.ts +52 -0
  32. package/dist/lib/billing/gateways/types.d.ts.map +1 -0
  33. package/dist/lib/billing/gateways/types.js +0 -0
  34. package/dist/lib/billing/types.d.ts +1 -1
  35. package/dist/lib/billing/types.d.ts.map +1 -1
  36. package/dist/lib/config/app.config.d.ts.map +1 -1
  37. package/dist/lib/config/app.config.js +24 -0
  38. package/dist/lib/config/config-sync.d.ts +15 -0
  39. package/dist/lib/config/config-sync.d.ts.map +1 -1
  40. package/dist/lib/config/config-sync.js +15 -0
  41. package/dist/lib/config/types.d.ts +67 -0
  42. package/dist/lib/config/types.d.ts.map +1 -1
  43. package/dist/lib/email/factory.d.ts.map +1 -1
  44. package/dist/lib/email/factory.js +0 -3
  45. package/dist/lib/media/utils.d.ts.map +1 -1
  46. package/dist/lib/services/plan.service.d.ts +6 -3
  47. package/dist/lib/services/plan.service.d.ts.map +1 -1
  48. package/dist/lib/services/plan.service.js +13 -4
  49. package/dist/lib/services/subscription.service.js +4 -4
  50. package/dist/lib/services/team.service.d.ts.map +1 -1
  51. package/dist/lib/services/team.service.js +1 -0
  52. package/dist/messages/en/auth.json +11 -0
  53. package/dist/messages/en/index.d.ts +11 -0
  54. package/dist/messages/en/index.d.ts.map +1 -1
  55. package/dist/messages/es/auth.json +11 -0
  56. package/dist/messages/es/index.d.ts +11 -0
  57. package/dist/messages/es/index.d.ts.map +1 -1
  58. package/dist/migrations/090_sample_data.sql +1 -1
  59. package/dist/styles/classes.json +1 -1
  60. package/dist/templates/app/(auth)/auth-error/page.tsx +26 -0
  61. package/dist/templates/app/(auth)/signup/page.tsx +24 -2
  62. package/dist/templates/app/api/v1/billing/cancel/route.ts +5 -8
  63. package/dist/templates/app/api/v1/billing/checkout/route.ts +3 -3
  64. package/dist/templates/app/api/v1/billing/portal/route.ts +2 -2
  65. package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +410 -0
  66. package/dist/templates/contents/themes/starter/config/app.config.ts +21 -0
  67. package/migrations/090_sample_data.sql +1 -1
  68. package/package.json +39 -2
  69. package/scripts/build/registry/generators/billing-registry.mjs +6 -3
  70. package/templates/app/(auth)/auth-error/page.tsx +26 -0
  71. package/templates/app/(auth)/signup/page.tsx +24 -2
  72. package/templates/app/api/v1/billing/cancel/route.ts +5 -8
  73. package/templates/app/api/v1/billing/checkout/route.ts +3 -3
  74. package/templates/app/api/v1/billing/portal/route.ts +2 -2
  75. package/templates/app/api/v1/billing/webhooks/polar/route.ts +410 -0
  76. package/templates/contents/themes/starter/config/app.config.ts +21 -0
  77. /package/dist/lib/billing/gateways/{stripe.d.js → interface.js} +0 -0
@@ -0,0 +1,34 @@
1
+ import { BILLING_REGISTRY } from "@nextsparkjs/registries/billing-registry";
2
+ let gatewayInstance = null;
3
+ function getBillingGateway() {
4
+ if (!gatewayInstance) {
5
+ const provider = BILLING_REGISTRY.provider;
6
+ switch (provider) {
7
+ case "stripe": {
8
+ const { StripeGateway } = require("./stripe");
9
+ gatewayInstance = new StripeGateway();
10
+ break;
11
+ }
12
+ case "polar": {
13
+ const { PolarGateway } = require("./polar");
14
+ gatewayInstance = new PolarGateway();
15
+ break;
16
+ }
17
+ // Future providers:
18
+ // case 'paddle': { ... }
19
+ // case 'lemonsqueezy': { ... }
20
+ default:
21
+ throw new Error(
22
+ `Unsupported billing provider: "${provider}". Supported providers: stripe, polar. Check your billing.config.ts provider setting.`
23
+ );
24
+ }
25
+ }
26
+ return gatewayInstance;
27
+ }
28
+ function resetBillingGateway() {
29
+ gatewayInstance = null;
30
+ }
31
+ export {
32
+ getBillingGateway,
33
+ resetBillingGateway
34
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Billing Gateway Interface
3
+ *
4
+ * Defines the contract that all payment provider implementations must satisfy.
5
+ * Consumers interact with this interface via the factory (getBillingGateway()),
6
+ * making provider switching a configuration change rather than a code change.
7
+ */
8
+ import type { CheckoutSessionResult, PortalSessionResult, SubscriptionResult, CustomerResult, WebhookEventResult, CreateCheckoutParams, CreatePortalParams, CreateCustomerParams, UpdateSubscriptionParams } from './types';
9
+ export interface BillingGateway {
10
+ createCheckoutSession(params: CreateCheckoutParams): Promise<CheckoutSessionResult>;
11
+ createPortalSession(params: CreatePortalParams): Promise<PortalSessionResult>;
12
+ getCustomer(customerId: string): Promise<CustomerResult>;
13
+ createCustomer(params: CreateCustomerParams): Promise<CustomerResult>;
14
+ updateSubscriptionPlan(params: UpdateSubscriptionParams): Promise<SubscriptionResult>;
15
+ cancelSubscriptionAtPeriodEnd(subscriptionId: string): Promise<SubscriptionResult>;
16
+ cancelSubscriptionImmediately(subscriptionId: string): Promise<SubscriptionResult>;
17
+ reactivateSubscription(subscriptionId: string): Promise<SubscriptionResult>;
18
+ verifyWebhookSignature(payload: string | Buffer, signatureOrHeaders: string | Record<string, string>): WebhookEventResult;
19
+ }
20
+ //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../../src/lib/billing/gateways/interface.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACzB,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,cAAc;IAE7B,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAA;IACnF,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAG7E,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IACxD,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IAGrE,sBAAsB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACrF,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAClF,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAClF,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAG3E,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,kBAAkB,CAAA;CAC1H"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Polar.sh Payment Gateway Implementation
3
+ *
4
+ * Implements BillingGateway interface for Polar.sh.
5
+ * Wraps Polar SDK types into provider-agnostic result types.
6
+ *
7
+ * Key differences from Stripe:
8
+ * - Checkout uses `products: [productId]` (Polar product IDs, not Stripe price IDs)
9
+ * - Cancel = "revoke" in Polar terminology (immediate)
10
+ * - Customer portal via "customer sessions" (not a hosted portal page)
11
+ * - Webhook verification requires ALL headers, not just a signature string
12
+ * - Uses `validateEvent` from @polar-sh/sdk/webhooks
13
+ *
14
+ * NOTE: In billing.config.ts, Polar's `providerPriceIds` should contain Polar **product IDs**
15
+ * (not price IDs). Polar associates prices with products, so the product ID is used for checkout
16
+ * and subscription plan changes.
17
+ *
18
+ * NOTE: Polar also offers @polar-sh/better-auth for direct Better Auth integration.
19
+ * That is an ALTERNATIVE approach to this gateway. If you want the simpler plugin-based
20
+ * approach (auto-create customer on signup, client-side checkout helpers), use the
21
+ * Better Auth plugin instead of this gateway. The two approaches are mutually exclusive
22
+ * for the same project - pick one.
23
+ */
24
+ import { Polar } from '@polar-sh/sdk';
25
+ import type { BillingGateway } from './interface';
26
+ import type { CheckoutSessionResult, PortalSessionResult, SubscriptionResult, CustomerResult, WebhookEventResult, CreateCheckoutParams, CreatePortalParams, CreateCustomerParams, UpdateSubscriptionParams } from './types';
27
+ /**
28
+ * Polar.sh implementation of the BillingGateway interface.
29
+ * Maps Polar SDK types to provider-agnostic result types.
30
+ */
31
+ export declare class PolarGateway implements BillingGateway {
32
+ createCheckoutSession(params: CreateCheckoutParams): Promise<CheckoutSessionResult>;
33
+ createPortalSession(params: CreatePortalParams): Promise<PortalSessionResult>;
34
+ verifyWebhookSignature(payload: string | Buffer, signatureOrHeaders: string | Record<string, string>): WebhookEventResult;
35
+ getCustomer(customerId: string): Promise<CustomerResult>;
36
+ createCustomer(params: CreateCustomerParams): Promise<CustomerResult>;
37
+ updateSubscriptionPlan(params: UpdateSubscriptionParams): Promise<SubscriptionResult>;
38
+ cancelSubscriptionAtPeriodEnd(subscriptionId: string): Promise<SubscriptionResult>;
39
+ cancelSubscriptionImmediately(subscriptionId: string): Promise<SubscriptionResult>;
40
+ reactivateSubscription(subscriptionId: string): Promise<SubscriptionResult>;
41
+ }
42
+ /**
43
+ * Get raw Polar instance for advanced usage (e.g., webhook handlers that need full types).
44
+ * Prefer using getBillingGateway() for all other operations.
45
+ */
46
+ export declare function getPolarInstance(): Polar;
47
+ //# sourceMappingURL=polar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polar.d.ts","sourceRoot":"","sources":["../../../../src/lib/billing/gateways/polar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAKrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACzB,MAAM,SAAS,CAAA;AAiBhB;;;GAGG;AACH,qBAAa,YAAa,YAAW,cAAc;IAC3C,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA0BnF,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAcnF,sBAAsB,CACpB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAClD,kBAAkB;IA4Bf,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAUxD,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;IAsBrE,sBAAsB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAkBrF,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiBlF,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAalF,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAelF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,KAAK,CAExC"}
@@ -0,0 +1,150 @@
1
+ import { Polar } from "@polar-sh/sdk";
2
+ import { validateEvent, WebhookVerificationError } from "@polar-sh/sdk/webhooks";
3
+ import { PlanService } from "../../services/plan.service.js";
4
+ let polarInstance = null;
5
+ function getPolar() {
6
+ if (!polarInstance) {
7
+ if (!process.env.POLAR_ACCESS_TOKEN) {
8
+ throw new Error("POLAR_ACCESS_TOKEN is not configured");
9
+ }
10
+ polarInstance = new Polar({
11
+ accessToken: process.env.POLAR_ACCESS_TOKEN
12
+ });
13
+ }
14
+ return polarInstance;
15
+ }
16
+ class PolarGateway {
17
+ async createCheckoutSession(params) {
18
+ const { teamId, planSlug, billingPeriod, successUrl, cancelUrl, customerEmail } = params;
19
+ const productId = PlanService.getPriceId(planSlug, billingPeriod);
20
+ if (!productId) {
21
+ throw new Error(`No product ID configured for ${planSlug} ${billingPeriod}`);
22
+ }
23
+ const checkoutParams = {
24
+ products: [productId],
25
+ successUrl,
26
+ metadata: { teamId, planSlug, billingPeriod },
27
+ ...customerEmail && { customerEmail },
28
+ // Polar uses returnUrl for back navigation (equivalent to Stripe's cancel_url)
29
+ ...cancelUrl && { returnUrl: cancelUrl }
30
+ };
31
+ const result = await getPolar().checkouts.create(checkoutParams);
32
+ return {
33
+ id: result.id,
34
+ url: result.url ?? null
35
+ };
36
+ }
37
+ async createPortalSession(params) {
38
+ const { customerId, returnUrl } = params;
39
+ const result = await getPolar().customerSessions.create({
40
+ customerId
41
+ });
42
+ return {
43
+ url: result.customerPortalUrl
44
+ };
45
+ }
46
+ verifyWebhookSignature(payload, signatureOrHeaders) {
47
+ const webhookSecret = process.env.POLAR_WEBHOOK_SECRET;
48
+ if (!webhookSecret) {
49
+ throw new Error("POLAR_WEBHOOK_SECRET is not configured");
50
+ }
51
+ const headers = typeof signatureOrHeaders === "string" ? { "webhook-id": "", "webhook-timestamp": "", "webhook-signature": signatureOrHeaders } : signatureOrHeaders;
52
+ const body = typeof payload === "string" ? payload : payload.toString("utf-8");
53
+ try {
54
+ const event = validateEvent(body, headers, webhookSecret);
55
+ return {
56
+ id: event.id || crypto.randomUUID(),
57
+ type: event.type,
58
+ data: event.data
59
+ };
60
+ } catch (error) {
61
+ if (error instanceof WebhookVerificationError) {
62
+ throw new Error(`Polar webhook verification failed: ${error.message}`);
63
+ }
64
+ throw error;
65
+ }
66
+ }
67
+ async getCustomer(customerId) {
68
+ const customer = await getPolar().customers.get({ id: customerId });
69
+ return {
70
+ id: customer.id,
71
+ email: customer.email ?? null,
72
+ name: customer.name ?? null
73
+ };
74
+ }
75
+ async createCustomer(params) {
76
+ const { email, name, metadata } = params;
77
+ const customerParams = {
78
+ email,
79
+ ...name && { name },
80
+ ...metadata && { metadata },
81
+ // Add organization ID if available
82
+ ...process.env.POLAR_ORGANIZATION_ID && {
83
+ organizationId: process.env.POLAR_ORGANIZATION_ID
84
+ }
85
+ };
86
+ const customer = await getPolar().customers.create(customerParams);
87
+ return {
88
+ id: customer.id,
89
+ email: customer.email ?? null,
90
+ name: customer.name ?? null
91
+ };
92
+ }
93
+ async updateSubscriptionPlan(params) {
94
+ const { subscriptionId, newPriceId } = params;
95
+ const result = await getPolar().subscriptions.update({
96
+ id: subscriptionId,
97
+ subscriptionUpdate: {
98
+ productId: newPriceId
99
+ }
100
+ });
101
+ return {
102
+ id: result.id,
103
+ status: result.status,
104
+ cancelAtPeriodEnd: result.cancelAtPeriodEnd ?? false
105
+ };
106
+ }
107
+ async cancelSubscriptionAtPeriodEnd(subscriptionId) {
108
+ const result = await getPolar().subscriptions.update({
109
+ id: subscriptionId,
110
+ subscriptionUpdate: {
111
+ cancelAtPeriodEnd: true
112
+ }
113
+ });
114
+ return {
115
+ id: result.id,
116
+ status: result.status,
117
+ cancelAtPeriodEnd: true
118
+ };
119
+ }
120
+ async cancelSubscriptionImmediately(subscriptionId) {
121
+ const result = await getPolar().subscriptions.revoke({
122
+ id: subscriptionId
123
+ });
124
+ return {
125
+ id: result.id,
126
+ status: result.status ?? "canceled",
127
+ cancelAtPeriodEnd: false
128
+ };
129
+ }
130
+ async reactivateSubscription(subscriptionId) {
131
+ const result = await getPolar().subscriptions.update({
132
+ id: subscriptionId,
133
+ subscriptionUpdate: {
134
+ cancelAtPeriodEnd: false
135
+ }
136
+ });
137
+ return {
138
+ id: result.id,
139
+ status: result.status,
140
+ cancelAtPeriodEnd: false
141
+ };
142
+ }
143
+ }
144
+ function getPolarInstance() {
145
+ return getPolar();
146
+ }
147
+ export {
148
+ PolarGateway,
149
+ getPolarInstance
150
+ };
@@ -1,76 +1,51 @@
1
1
  /**
2
- * Stripe Payment Gateway Integration
2
+ * Stripe Payment Gateway Implementation
3
3
  *
4
- * Wrapper around Stripe SDK for subscription billing operations.
5
- * Handles checkout sessions, customer portal, and webhooks.
4
+ * Implements BillingGateway interface for Stripe.
5
+ * Wraps Stripe SDK types into provider-agnostic result types.
6
6
  *
7
7
  * P2: Stripe Integration
8
8
  */
9
9
  import Stripe from 'stripe';
10
- export interface CreateCheckoutParams {
11
- teamId: string;
12
- planSlug: string;
13
- billingPeriod: 'monthly' | 'yearly';
14
- successUrl: string;
15
- cancelUrl: string;
16
- customerEmail?: string;
17
- customerId?: string;
10
+ import type { BillingGateway } from './interface';
11
+ import type { CheckoutSessionResult, PortalSessionResult, SubscriptionResult, CustomerResult, WebhookEventResult, CreateCheckoutParams, CreatePortalParams, CreateCustomerParams, UpdateSubscriptionParams } from './types';
12
+ export type { CreateCheckoutParams, CreatePortalParams, UpdateSubscriptionParams } from './types';
13
+ /**
14
+ * Stripe implementation of the BillingGateway interface.
15
+ * Maps Stripe SDK types to provider-agnostic result types.
16
+ */
17
+ export declare class StripeGateway implements BillingGateway {
18
+ createCheckoutSession(params: CreateCheckoutParams): Promise<CheckoutSessionResult>;
19
+ createPortalSession(params: CreatePortalParams): Promise<PortalSessionResult>;
20
+ verifyWebhookSignature(payload: string | Buffer, signatureOrHeaders: string | Record<string, string>): WebhookEventResult;
21
+ getCustomer(customerId: string): Promise<CustomerResult>;
22
+ createCustomer(params: CreateCustomerParams): Promise<CustomerResult>;
23
+ updateSubscriptionPlan(params: UpdateSubscriptionParams): Promise<SubscriptionResult>;
24
+ cancelSubscriptionAtPeriodEnd(subscriptionId: string): Promise<SubscriptionResult>;
25
+ cancelSubscriptionImmediately(subscriptionId: string): Promise<SubscriptionResult>;
26
+ reactivateSubscription(subscriptionId: string): Promise<SubscriptionResult>;
18
27
  }
19
- export interface CreatePortalParams {
20
- customerId: string;
21
- returnUrl: string;
22
- }
23
- /**
24
- * Create Stripe Checkout session for subscription
25
- */
26
- export declare function createCheckoutSession(params: CreateCheckoutParams): Promise<Stripe.Checkout.Session>;
27
- /**
28
- * Create Stripe Customer Portal session for self-service billing
29
- */
30
- export declare function createPortalSession(params: CreatePortalParams): Promise<Stripe.BillingPortal.Session>;
31
- /**
32
- * Verify Stripe webhook signature (MANDATORY for security)
33
- */
34
- export declare function verifyWebhookSignature(payload: string | Buffer, signature: string): Stripe.Event;
35
- /**
36
- * Get Stripe customer by ID
37
- */
38
- export declare function getCustomer(customerId: string): Promise<Stripe.Customer>;
39
- /**
40
- * Create Stripe customer
41
- */
42
- export declare function createCustomer(params: {
43
- email: string;
44
- name?: string;
45
- metadata?: Record<string, string>;
46
- }): Promise<Stripe.Customer>;
47
28
  /**
48
- * Get Stripe instance for advanced usage (lazy-loaded)
29
+ * Get raw Stripe instance for advanced usage (webhook handlers that need Stripe.Event types).
30
+ * Prefer using getBillingGateway() for all other operations.
49
31
  */
50
32
  export declare function getStripeInstance(): Stripe;
51
- /**
52
- * Cancel subscription at period end (soft cancel)
53
- * User keeps access until the current billing period ends
54
- */
55
- export declare function cancelSubscriptionAtPeriodEnd(subscriptionId: string): Promise<Stripe.Subscription>;
56
- /**
57
- * Cancel subscription immediately (hard cancel)
58
- * User loses access immediately
59
- */
60
- export declare function cancelSubscriptionImmediately(subscriptionId: string): Promise<Stripe.Subscription>;
61
- /**
62
- * Reactivate a subscription that was scheduled to cancel
63
- * Only works if cancel_at_period_end was true
64
- */
65
- export declare function reactivateSubscription(subscriptionId: string): Promise<Stripe.Subscription>;
66
- export interface UpdateSubscriptionParams {
67
- subscriptionId: string;
68
- newPriceId: string;
69
- prorationBehavior?: 'create_prorations' | 'none' | 'always_invoice';
70
- }
71
- /**
72
- * Update subscription to a new plan/price
73
- * Stripe handles proration automatically based on prorationBehavior
74
- */
75
- export declare function updateSubscriptionPlan(params: UpdateSubscriptionParams): Promise<Stripe.Subscription>;
33
+ /** @deprecated Use getBillingGateway().createCheckoutSession() instead */
34
+ export declare function createCheckoutSession(params: CreateCheckoutParams): Promise<CheckoutSessionResult>;
35
+ /** @deprecated Use getBillingGateway().createPortalSession() instead */
36
+ export declare function createPortalSession(params: CreatePortalParams): Promise<PortalSessionResult>;
37
+ /** @deprecated Use getBillingGateway().verifyWebhookSignature() instead */
38
+ export declare function verifyWebhookSignature(payload: string | Buffer, signatureOrHeaders: string | Record<string, string>): WebhookEventResult;
39
+ /** @deprecated Use getBillingGateway().getCustomer() instead */
40
+ export declare function getCustomer(customerId: string): Promise<CustomerResult>;
41
+ /** @deprecated Use getBillingGateway().createCustomer() instead */
42
+ export declare function createCustomer(params: CreateCustomerParams): Promise<CustomerResult>;
43
+ /** @deprecated Use getBillingGateway().cancelSubscriptionAtPeriodEnd() instead */
44
+ export declare function cancelSubscriptionAtPeriodEnd(subscriptionId: string): Promise<SubscriptionResult>;
45
+ /** @deprecated Use getBillingGateway().cancelSubscriptionImmediately() instead */
46
+ export declare function cancelSubscriptionImmediately(subscriptionId: string): Promise<SubscriptionResult>;
47
+ /** @deprecated Use getBillingGateway().reactivateSubscription() instead */
48
+ export declare function reactivateSubscription(subscriptionId: string): Promise<SubscriptionResult>;
49
+ /** @deprecated Use getBillingGateway().updateSubscriptionPlan() instead */
50
+ export declare function updateSubscriptionPlan(params: UpdateSubscriptionParams): Promise<SubscriptionResult>;
76
51
  //# sourceMappingURL=stripe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../../../src/lib/billing/gateways/stripe.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAA;AAmB3B,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,SAAS,GAAG,QAAQ,CAAA;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CA0ClC;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAKvC;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,KAAK,CAMd;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAE9E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC3C,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAClC,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAE3B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;GAGG;AACH,wBAAsB,6BAA6B,CACjD,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAI9B;AAED;;;GAGG;AACH,wBAAsB,6BAA6B,CACjD,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAE9B;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAI9B;AAED,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,mBAAmB,GAAG,MAAM,GAAG,gBAAgB,CAAA;CACpE;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAoB9B"}
1
+ {"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../../../src/lib/billing/gateways/stripe.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACzB,MAAM,SAAS,CAAA;AAGhB,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAkBjG;;;GAGG;AACH,qBAAa,aAAc,YAAW,cAAc;IAC5C,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA6CnF,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAQnF,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,kBAAkB;IAiBnH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAYxD,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;IASrE,sBAAsB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA2BrF,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAWlF,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IASlF,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAUlF;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAQD,0EAA0E;AAC1E,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,kCAEvE;AAED,wEAAwE;AACxE,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,gCAEnE;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,sBAEnH;AAED,gEAAgE;AAChE,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,2BAEnD;AAED,mEAAmE;AACnE,wBAAsB,cAAc,CAAC,MAAM,EAAE,oBAAoB,2BAEhE;AAED,kFAAkF;AAClF,wBAAsB,6BAA6B,CAAC,cAAc,EAAE,MAAM,+BAEzE;AAED,kFAAkF;AAClF,wBAAsB,6BAA6B,CAAC,cAAc,EAAE,MAAM,+BAEzE;AAED,2EAA2E;AAC3E,wBAAsB,sBAAsB,CAAC,cAAc,EAAE,MAAM,+BAElE;AAED,2EAA2E;AAC3E,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,wBAAwB,+BAE5E"}
@@ -13,90 +13,162 @@ function getStripe() {
13
13
  }
14
14
  return stripeInstance;
15
15
  }
16
- async function createCheckoutSession(params) {
17
- const { teamId, planSlug, billingPeriod, successUrl, cancelUrl, customerEmail, customerId } = params;
18
- const planConfig = BILLING_REGISTRY.plans.find((p) => p.slug === planSlug);
19
- if (!planConfig) {
20
- throw new Error(`Plan ${planSlug} not found in BILLING_REGISTRY`);
16
+ class StripeGateway {
17
+ async createCheckoutSession(params) {
18
+ const { teamId, planSlug, billingPeriod, successUrl, cancelUrl, customerEmail, customerId } = params;
19
+ const planConfig = BILLING_REGISTRY.plans.find((p) => p.slug === planSlug);
20
+ if (!planConfig) {
21
+ throw new Error(`Plan ${planSlug} not found in BILLING_REGISTRY`);
22
+ }
23
+ const priceId = billingPeriod === "yearly" ? planConfig.stripePriceIdYearly : planConfig.stripePriceIdMonthly;
24
+ if (!priceId) {
25
+ throw new Error(`No Stripe price configured for ${planSlug} ${billingPeriod}`);
26
+ }
27
+ const sessionParams = {
28
+ mode: "subscription",
29
+ payment_method_types: ["card"],
30
+ line_items: [{ price: priceId, quantity: 1 }],
31
+ success_url: successUrl,
32
+ cancel_url: cancelUrl,
33
+ metadata: { teamId, planSlug, billingPeriod },
34
+ client_reference_id: teamId
35
+ };
36
+ if (customerId) {
37
+ sessionParams.customer = customerId;
38
+ } else if (customerEmail) {
39
+ sessionParams.customer_email = customerEmail;
40
+ }
41
+ if (planConfig.trialDays && planConfig.trialDays > 0 && !customerId) {
42
+ sessionParams.subscription_data = {
43
+ trial_period_days: planConfig.trialDays
44
+ };
45
+ }
46
+ const session = await getStripe().checkout.sessions.create(sessionParams);
47
+ return { id: session.id, url: session.url };
48
+ }
49
+ async createPortalSession(params) {
50
+ const session = await getStripe().billingPortal.sessions.create({
51
+ customer: params.customerId,
52
+ return_url: params.returnUrl
53
+ });
54
+ return { url: session.url };
55
+ }
56
+ verifyWebhookSignature(payload, signatureOrHeaders) {
57
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
58
+ if (!webhookSecret) {
59
+ throw new Error("STRIPE_WEBHOOK_SECRET is not configured");
60
+ }
61
+ const signature = typeof signatureOrHeaders === "string" ? signatureOrHeaders : signatureOrHeaders["stripe-signature"] || "";
62
+ const event = getStripe().webhooks.constructEvent(payload, signature, webhookSecret);
63
+ return {
64
+ id: event.id,
65
+ type: event.type,
66
+ data: event.data
67
+ };
68
+ }
69
+ async getCustomer(customerId) {
70
+ const customer = await getStripe().customers.retrieve(customerId);
71
+ if ("deleted" in customer && customer.deleted) {
72
+ throw new Error(`Customer ${customerId} has been deleted`);
73
+ }
74
+ return {
75
+ id: customer.id,
76
+ email: customer.email ?? null,
77
+ name: customer.name ?? null
78
+ };
79
+ }
80
+ async createCustomer(params) {
81
+ const customer = await getStripe().customers.create(params);
82
+ return {
83
+ id: customer.id,
84
+ email: customer.email ?? null,
85
+ name: customer.name ?? null
86
+ };
87
+ }
88
+ async updateSubscriptionPlan(params) {
89
+ var _a;
90
+ const { subscriptionId, newPriceId, prorationBehavior = "create_prorations" } = params;
91
+ const stripe = getStripe();
92
+ const subscription = await stripe.subscriptions.retrieve(subscriptionId);
93
+ const itemId = (_a = subscription.items.data[0]) == null ? void 0 : _a.id;
94
+ if (!itemId) {
95
+ throw new Error("Subscription has no items");
96
+ }
97
+ const updated = await stripe.subscriptions.update(subscriptionId, {
98
+ items: [{
99
+ id: itemId,
100
+ price: newPriceId
101
+ }],
102
+ proration_behavior: prorationBehavior
103
+ });
104
+ return {
105
+ id: updated.id,
106
+ status: updated.status,
107
+ cancelAtPeriodEnd: updated.cancel_at_period_end
108
+ };
21
109
  }
22
- const priceId = billingPeriod === "yearly" ? planConfig.stripePriceIdYearly : planConfig.stripePriceIdMonthly;
23
- if (!priceId) {
24
- throw new Error(`No Stripe price configured for ${planSlug} ${billingPeriod}`);
110
+ async cancelSubscriptionAtPeriodEnd(subscriptionId) {
111
+ const updated = await getStripe().subscriptions.update(subscriptionId, {
112
+ cancel_at_period_end: true
113
+ });
114
+ return {
115
+ id: updated.id,
116
+ status: updated.status,
117
+ cancelAtPeriodEnd: updated.cancel_at_period_end
118
+ };
25
119
  }
26
- const sessionParams = {
27
- mode: "subscription",
28
- payment_method_types: ["card"],
29
- line_items: [{ price: priceId, quantity: 1 }],
30
- success_url: successUrl,
31
- cancel_url: cancelUrl,
32
- metadata: { teamId, planSlug, billingPeriod },
33
- client_reference_id: teamId
34
- };
35
- if (customerId) {
36
- sessionParams.customer = customerId;
37
- } else if (customerEmail) {
38
- sessionParams.customer_email = customerEmail;
120
+ async cancelSubscriptionImmediately(subscriptionId) {
121
+ const canceled = await getStripe().subscriptions.cancel(subscriptionId);
122
+ return {
123
+ id: canceled.id,
124
+ status: canceled.status,
125
+ cancelAtPeriodEnd: canceled.cancel_at_period_end
126
+ };
39
127
  }
40
- if (planConfig.trialDays && planConfig.trialDays > 0 && !customerId) {
41
- sessionParams.subscription_data = {
42
- trial_period_days: planConfig.trialDays
128
+ async reactivateSubscription(subscriptionId) {
129
+ const updated = await getStripe().subscriptions.update(subscriptionId, {
130
+ cancel_at_period_end: false
131
+ });
132
+ return {
133
+ id: updated.id,
134
+ status: updated.status,
135
+ cancelAtPeriodEnd: updated.cancel_at_period_end
43
136
  };
44
137
  }
45
- return getStripe().checkout.sessions.create(sessionParams);
138
+ }
139
+ function getStripeInstance() {
140
+ return getStripe();
141
+ }
142
+ const _defaultGateway = new StripeGateway();
143
+ async function createCheckoutSession(params) {
144
+ return _defaultGateway.createCheckoutSession(params);
46
145
  }
47
146
  async function createPortalSession(params) {
48
- return getStripe().billingPortal.sessions.create({
49
- customer: params.customerId,
50
- return_url: params.returnUrl
51
- });
147
+ return _defaultGateway.createPortalSession(params);
52
148
  }
53
- function verifyWebhookSignature(payload, signature) {
54
- const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
55
- if (!webhookSecret) {
56
- throw new Error("STRIPE_WEBHOOK_SECRET is not configured");
57
- }
58
- return getStripe().webhooks.constructEvent(payload, signature, webhookSecret);
149
+ function verifyWebhookSignature(payload, signatureOrHeaders) {
150
+ return _defaultGateway.verifyWebhookSignature(payload, signatureOrHeaders);
59
151
  }
60
152
  async function getCustomer(customerId) {
61
- return getStripe().customers.retrieve(customerId);
153
+ return _defaultGateway.getCustomer(customerId);
62
154
  }
63
155
  async function createCustomer(params) {
64
- return getStripe().customers.create(params);
65
- }
66
- function getStripeInstance() {
67
- return getStripe();
156
+ return _defaultGateway.createCustomer(params);
68
157
  }
69
158
  async function cancelSubscriptionAtPeriodEnd(subscriptionId) {
70
- return getStripe().subscriptions.update(subscriptionId, {
71
- cancel_at_period_end: true
72
- });
159
+ return _defaultGateway.cancelSubscriptionAtPeriodEnd(subscriptionId);
73
160
  }
74
161
  async function cancelSubscriptionImmediately(subscriptionId) {
75
- return getStripe().subscriptions.cancel(subscriptionId);
162
+ return _defaultGateway.cancelSubscriptionImmediately(subscriptionId);
76
163
  }
77
164
  async function reactivateSubscription(subscriptionId) {
78
- return getStripe().subscriptions.update(subscriptionId, {
79
- cancel_at_period_end: false
80
- });
165
+ return _defaultGateway.reactivateSubscription(subscriptionId);
81
166
  }
82
167
  async function updateSubscriptionPlan(params) {
83
- var _a;
84
- const { subscriptionId, newPriceId, prorationBehavior = "create_prorations" } = params;
85
- const stripe = getStripe();
86
- const subscription = await stripe.subscriptions.retrieve(subscriptionId);
87
- const itemId = (_a = subscription.items.data[0]) == null ? void 0 : _a.id;
88
- if (!itemId) {
89
- throw new Error("Subscription has no items");
90
- }
91
- return stripe.subscriptions.update(subscriptionId, {
92
- items: [{
93
- id: itemId,
94
- price: newPriceId
95
- }],
96
- proration_behavior: prorationBehavior
97
- });
168
+ return _defaultGateway.updateSubscriptionPlan(params);
98
169
  }
99
170
  export {
171
+ StripeGateway,
100
172
  cancelSubscriptionAtPeriodEnd,
101
173
  cancelSubscriptionImmediately,
102
174
  createCheckoutSession,