@beeblock/svelar 0.4.4 → 0.4.6
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/README.md +1 -0
- package/dist/cli/Command.d.ts +14 -0
- package/dist/cli/bin.js +731 -91
- package/dist/cli/commands/NewCommandTemplates.d.ts +10 -0
- package/dist/cli/index.js +97 -94
- package/dist/cli/ts-resolve-hook.mjs +22 -1
- package/dist/stripe/Invoice.d.ts +44 -0
- package/dist/stripe/StripeService.d.ts +52 -0
- package/dist/stripe/StripeWebhookHandler.d.ts +14 -0
- package/dist/stripe/Subscription.d.ts +40 -0
- package/dist/stripe/SubscriptionManager.d.ts +39 -0
- package/dist/stripe/SubscriptionPlan.d.ts +41 -0
- package/dist/stripe/SyncStripeCustomerJob.d.ts +11 -0
- package/dist/stripe/index.d.ts +22 -0
- package/dist/stripe/index.js +1 -0
- package/package.json +17 -4
|
@@ -49,7 +49,28 @@ export async function resolve(specifier, context, nextResolve) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// 2.
|
|
52
|
+
// 2. Resolve $lib/ alias (SvelteKit convention) to src/lib/ in the project
|
|
53
|
+
if (specifier.startsWith('$lib/')) {
|
|
54
|
+
const subpath = specifier.slice('$lib/'.length);
|
|
55
|
+
const projectRoot = process.cwd();
|
|
56
|
+
// Try .ts extension (most common in dev)
|
|
57
|
+
const withTs = pathResolve(projectRoot, 'src', 'lib', subpath.replace(/\.js$/, '.ts'));
|
|
58
|
+
if (existsSync(withTs)) {
|
|
59
|
+
return { url: pathToFileURL(withTs).href, shortCircuit: true };
|
|
60
|
+
}
|
|
61
|
+
// Try exact path (.js or extensionless)
|
|
62
|
+
const exact = pathResolve(projectRoot, 'src', 'lib', subpath);
|
|
63
|
+
if (existsSync(exact)) {
|
|
64
|
+
return { url: pathToFileURL(exact).href, shortCircuit: true };
|
|
65
|
+
}
|
|
66
|
+
// Try adding .ts (for imports without extension like '$lib/modules/auth/schemas')
|
|
67
|
+
const addTs = pathResolve(projectRoot, 'src', 'lib', subpath + '.ts');
|
|
68
|
+
if (existsSync(addTs)) {
|
|
69
|
+
return { url: pathToFileURL(addTs).href, shortCircuit: true };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 3. Handle relative .js → .ts resolution
|
|
53
74
|
if (specifier.startsWith('.') && specifier.endsWith('.js')) {
|
|
54
75
|
try {
|
|
55
76
|
const parentPath = context.parentURL ? fileURLToPath(context.parentURL) : process.cwd();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type InvoiceStatus = 'draft' | 'open' | 'paid' | 'void' | 'uncollectible';
|
|
2
|
+
export interface InvoiceAttributes {
|
|
3
|
+
id: number;
|
|
4
|
+
userId: number;
|
|
5
|
+
subscriptionId: number | null;
|
|
6
|
+
stripeInvoiceId: string;
|
|
7
|
+
amountDue: number;
|
|
8
|
+
amountPaid: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
status: InvoiceStatus;
|
|
11
|
+
paidAt: Date | null;
|
|
12
|
+
dueDate: Date | null;
|
|
13
|
+
invoicePdf: string | null;
|
|
14
|
+
createdAt: Date;
|
|
15
|
+
updatedAt?: Date;
|
|
16
|
+
}
|
|
17
|
+
export declare class Invoice implements InvoiceAttributes {
|
|
18
|
+
id: number;
|
|
19
|
+
userId: number;
|
|
20
|
+
subscriptionId: number | null;
|
|
21
|
+
stripeInvoiceId: string;
|
|
22
|
+
amountDue: number;
|
|
23
|
+
amountPaid: number;
|
|
24
|
+
currency: string;
|
|
25
|
+
status: InvoiceStatus;
|
|
26
|
+
paidAt: Date | null;
|
|
27
|
+
dueDate: Date | null;
|
|
28
|
+
invoicePdf: string | null;
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
updatedAt?: Date;
|
|
31
|
+
constructor(attributes: InvoiceAttributes);
|
|
32
|
+
isPaid(): boolean;
|
|
33
|
+
isOpen(): boolean;
|
|
34
|
+
isDraft(): boolean;
|
|
35
|
+
isVoid(): boolean;
|
|
36
|
+
isUncollectible(): boolean;
|
|
37
|
+
isOverdue(): boolean;
|
|
38
|
+
outstandingAmount(): number;
|
|
39
|
+
formattedAmountDue(): string;
|
|
40
|
+
formattedAmountPaid(): string;
|
|
41
|
+
formattedOutstanding(): string;
|
|
42
|
+
daysUntilDue(): number | null;
|
|
43
|
+
private formatCurrency;
|
|
44
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type Stripe from 'stripe';
|
|
2
|
+
export interface StripeConfig {
|
|
3
|
+
secretKey: string;
|
|
4
|
+
publishableKey: string;
|
|
5
|
+
webhookSecret: string;
|
|
6
|
+
currency?: string;
|
|
7
|
+
trialDays?: number;
|
|
8
|
+
portalReturnUrl?: string;
|
|
9
|
+
checkoutSuccessUrl?: string;
|
|
10
|
+
checkoutCancelUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class StripeService {
|
|
13
|
+
private stripe;
|
|
14
|
+
private config;
|
|
15
|
+
configure(config: StripeConfig): void;
|
|
16
|
+
getConfig(): StripeConfig;
|
|
17
|
+
getClient(): Promise<Stripe>;
|
|
18
|
+
createCustomer(user: {
|
|
19
|
+
id: number | string;
|
|
20
|
+
name?: string;
|
|
21
|
+
email?: string;
|
|
22
|
+
}): Promise<Stripe.Customer>;
|
|
23
|
+
updateCustomer(customerId: string, data: Stripe.CustomerUpdateParams): Promise<Stripe.Customer>;
|
|
24
|
+
deleteCustomer(customerId: string): Promise<void>;
|
|
25
|
+
getCustomer(customerId: string): Promise<Stripe.Customer>;
|
|
26
|
+
createSubscription(customerId: string, priceId: string, opts?: {
|
|
27
|
+
trialDays?: number;
|
|
28
|
+
metadata?: Record<string, string>;
|
|
29
|
+
collectionMethod?: 'send_invoice' | 'charge_automatically';
|
|
30
|
+
daysUntilDue?: number;
|
|
31
|
+
}): Promise<Stripe.Subscription>;
|
|
32
|
+
getSubscription(subscriptionId: string): Promise<Stripe.Subscription>;
|
|
33
|
+
cancelSubscription(subscriptionId: string, immediately?: boolean): Promise<Stripe.Subscription>;
|
|
34
|
+
resumeSubscription(subscriptionId: string): Promise<Stripe.Subscription>;
|
|
35
|
+
updateSubscription(subscriptionId: string, newPriceId: string, opts?: {
|
|
36
|
+
proration?: 'create_prorations' | 'none';
|
|
37
|
+
metadata?: Record<string, string>;
|
|
38
|
+
}): Promise<Stripe.Subscription>;
|
|
39
|
+
getInvoices(customerId: string, limit?: number): Promise<Stripe.Invoice[]>;
|
|
40
|
+
getInvoice(invoiceId: string): Promise<Stripe.Invoice>;
|
|
41
|
+
createCheckoutSession(customerId: string, priceId: string, successUrl: string, cancelUrl: string, opts?: {
|
|
42
|
+
trialDays?: number;
|
|
43
|
+
metadata?: Record<string, string>;
|
|
44
|
+
}): Promise<Stripe.Checkout.Session>;
|
|
45
|
+
createPortalSession(customerId: string, returnUrl: string): Promise<Stripe.BillingPortal.Session>;
|
|
46
|
+
constructWebhookEvent(body: string | Buffer, signature: string): Promise<Stripe.Event>;
|
|
47
|
+
listPrices(productId?: string, limit?: number): Promise<Stripe.Price[]>;
|
|
48
|
+
listProducts(limit?: number): Promise<Stripe.Product[]>;
|
|
49
|
+
refundInvoice(invoiceId: string): Promise<Stripe.Refund>;
|
|
50
|
+
getRefund(refundId: string): Promise<Stripe.Refund>;
|
|
51
|
+
listRefunds(chargeId: string): Promise<Stripe.Refund[]>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type Stripe from 'stripe';
|
|
2
|
+
export type WebhookEventType = 'customer.subscription.created' | 'customer.subscription.updated' | 'customer.subscription.deleted' | 'invoice.payment_succeeded' | 'invoice.payment_failed' | 'customer.created' | 'customer.updated' | 'customer.deleted' | 'checkout.session.completed' | 'invoice.created' | 'invoice.finalized' | 'charge.refunded' | string;
|
|
3
|
+
export type WebhookHandler = (event: Stripe.Event) => Promise<void>;
|
|
4
|
+
export declare class StripeWebhookHandler {
|
|
5
|
+
private handlers;
|
|
6
|
+
on(eventType: WebhookEventType, handler: WebhookHandler): this;
|
|
7
|
+
once(eventType: WebhookEventType, handler: WebhookHandler): this;
|
|
8
|
+
off(eventType: WebhookEventType, handler: WebhookHandler): void;
|
|
9
|
+
removeAllListeners(eventType?: WebhookEventType): void;
|
|
10
|
+
handle(event: Stripe.Event): Promise<void>;
|
|
11
|
+
getHandlers(eventType: WebhookEventType): WebhookHandler[];
|
|
12
|
+
getEventTypes(): WebhookEventType[];
|
|
13
|
+
hasHandlers(eventType: WebhookEventType): boolean;
|
|
14
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type SubscriptionStatus = 'active' | 'past_due' | 'canceled' | 'trialing' | 'incomplete' | 'paused';
|
|
2
|
+
export interface SubscriptionAttributes {
|
|
3
|
+
id: number;
|
|
4
|
+
userId: number;
|
|
5
|
+
stripeSubscriptionId: string;
|
|
6
|
+
stripeCustomerId: string;
|
|
7
|
+
planId: number;
|
|
8
|
+
status: SubscriptionStatus;
|
|
9
|
+
currentPeriodStart: Date;
|
|
10
|
+
currentPeriodEnd: Date;
|
|
11
|
+
cancelAtPeriodEnd: boolean;
|
|
12
|
+
trialEndsAt: Date | null;
|
|
13
|
+
canceledAt: Date | null;
|
|
14
|
+
createdAt: Date;
|
|
15
|
+
updatedAt: Date;
|
|
16
|
+
}
|
|
17
|
+
export declare class Subscription implements SubscriptionAttributes {
|
|
18
|
+
id: number;
|
|
19
|
+
userId: number;
|
|
20
|
+
stripeSubscriptionId: string;
|
|
21
|
+
stripeCustomerId: string;
|
|
22
|
+
planId: number;
|
|
23
|
+
status: SubscriptionStatus;
|
|
24
|
+
currentPeriodStart: Date;
|
|
25
|
+
currentPeriodEnd: Date;
|
|
26
|
+
cancelAtPeriodEnd: boolean;
|
|
27
|
+
trialEndsAt: Date | null;
|
|
28
|
+
canceledAt: Date | null;
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
updatedAt: Date;
|
|
31
|
+
constructor(attributes: SubscriptionAttributes);
|
|
32
|
+
isActive(): boolean;
|
|
33
|
+
isPastDue(): boolean;
|
|
34
|
+
isCanceled(): boolean;
|
|
35
|
+
isOnTrial(): boolean;
|
|
36
|
+
isOnGracePeriod(): boolean;
|
|
37
|
+
isIncomplete(): boolean;
|
|
38
|
+
isPaused(): boolean;
|
|
39
|
+
daysUntilEnd(): number;
|
|
40
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { StripeService } from './StripeService.js';
|
|
2
|
+
import type { Subscription } from './Subscription.js';
|
|
3
|
+
import type { SubscriptionPlan } from './SubscriptionPlan.js';
|
|
4
|
+
export interface SubscriptionManagerDependencies {
|
|
5
|
+
stripeService: StripeService;
|
|
6
|
+
subscriptionRepository?: {
|
|
7
|
+
findByUserId(userId: number): Promise<Subscription | null>;
|
|
8
|
+
findByStripeSubscriptionId(id: string): Promise<Subscription | null>;
|
|
9
|
+
create(data: any): Promise<Subscription>;
|
|
10
|
+
update(id: number, data: any): Promise<Subscription>;
|
|
11
|
+
delete(id: number): Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
planRepository?: {
|
|
14
|
+
findById(id: number): Promise<SubscriptionPlan | null>;
|
|
15
|
+
findByStripePriceId(id: string): Promise<SubscriptionPlan | null>;
|
|
16
|
+
all(): Promise<SubscriptionPlan[]>;
|
|
17
|
+
};
|
|
18
|
+
userRepository?: {
|
|
19
|
+
findById(id: number): Promise<any>;
|
|
20
|
+
update(id: number, data: any): Promise<any>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare class SubscriptionManager {
|
|
24
|
+
private stripeService;
|
|
25
|
+
private deps;
|
|
26
|
+
constructor(deps: SubscriptionManagerDependencies);
|
|
27
|
+
subscribe(userId: number, planId: number, opts?: {
|
|
28
|
+
trialDays?: number;
|
|
29
|
+
metadata?: Record<string, string>;
|
|
30
|
+
}): Promise<Subscription>;
|
|
31
|
+
upgrade(userId: number, newPlanId: number): Promise<Subscription>;
|
|
32
|
+
downgrade(userId: number, newPlanId: number): Promise<Subscription>;
|
|
33
|
+
cancel(userId: number, immediately?: boolean): Promise<void>;
|
|
34
|
+
resume(userId: number): Promise<Subscription>;
|
|
35
|
+
isSubscribed(userId: number, planId?: number): Promise<boolean>;
|
|
36
|
+
onTrial(userId: number): Promise<boolean>;
|
|
37
|
+
hasFeature(userId: number, feature: string): Promise<boolean>;
|
|
38
|
+
syncFromStripe(userId: number): Promise<Subscription>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type PlanInterval = 'month' | 'year';
|
|
2
|
+
export interface SubscriptionPlanAttributes {
|
|
3
|
+
id: number;
|
|
4
|
+
name: string;
|
|
5
|
+
stripePriceId: string;
|
|
6
|
+
stripeProductId: string;
|
|
7
|
+
price: number;
|
|
8
|
+
currency: string;
|
|
9
|
+
interval: PlanInterval;
|
|
10
|
+
intervalCount: number;
|
|
11
|
+
trialDays: number;
|
|
12
|
+
features: string[];
|
|
13
|
+
sortOrder: number;
|
|
14
|
+
active: boolean;
|
|
15
|
+
createdAt: Date;
|
|
16
|
+
updatedAt: Date;
|
|
17
|
+
}
|
|
18
|
+
export declare class SubscriptionPlan implements SubscriptionPlanAttributes {
|
|
19
|
+
id: number;
|
|
20
|
+
name: string;
|
|
21
|
+
stripePriceId: string;
|
|
22
|
+
stripeProductId: string;
|
|
23
|
+
price: number;
|
|
24
|
+
currency: string;
|
|
25
|
+
interval: PlanInterval;
|
|
26
|
+
intervalCount: number;
|
|
27
|
+
trialDays: number;
|
|
28
|
+
features: string[];
|
|
29
|
+
sortOrder: number;
|
|
30
|
+
active: boolean;
|
|
31
|
+
createdAt: Date;
|
|
32
|
+
updatedAt: Date;
|
|
33
|
+
constructor(attributes: SubscriptionPlanAttributes);
|
|
34
|
+
isActive(): boolean;
|
|
35
|
+
monthlyPrice(): number;
|
|
36
|
+
yearlyPrice(): number;
|
|
37
|
+
hasFeature(feature: string): boolean;
|
|
38
|
+
formattedPrice(): string;
|
|
39
|
+
intervalLabel(): string;
|
|
40
|
+
trialLabel(): string;
|
|
41
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StripeService } from './StripeService.js';
|
|
2
|
+
export declare class SyncStripeCustomerJob {
|
|
3
|
+
userId: number;
|
|
4
|
+
email: string;
|
|
5
|
+
userName: string;
|
|
6
|
+
readonly name = "sync-stripe-customer";
|
|
7
|
+
constructor(userId: number, email: string, userName: string);
|
|
8
|
+
handle(stripeService: StripeService): Promise<void>;
|
|
9
|
+
serialize(): string;
|
|
10
|
+
static restore(data: any): SyncStripeCustomerJob;
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { StripeService, type StripeConfig } from './StripeService.js';
|
|
2
|
+
import { StripeWebhookHandler } from './StripeWebhookHandler.js';
|
|
3
|
+
import { SubscriptionManager, type SubscriptionManagerDependencies } from './SubscriptionManager.js';
|
|
4
|
+
declare class StripeManager {
|
|
5
|
+
private stripeService;
|
|
6
|
+
private webhookHandler;
|
|
7
|
+
private subscriptionManager;
|
|
8
|
+
configure(config: StripeConfig): void;
|
|
9
|
+
service(): StripeService;
|
|
10
|
+
webhooks(): StripeWebhookHandler;
|
|
11
|
+
subscriptions(deps?: SubscriptionManagerDependencies): SubscriptionManager;
|
|
12
|
+
initSubscriptions(deps: Omit<SubscriptionManagerDependencies, 'stripeService'>): SubscriptionManager;
|
|
13
|
+
}
|
|
14
|
+
export declare const Stripe: StripeManager;
|
|
15
|
+
export { StripeManager };
|
|
16
|
+
export { StripeService, type StripeConfig } from './StripeService.js';
|
|
17
|
+
export { StripeWebhookHandler, type WebhookEventType, type WebhookHandler } from './StripeWebhookHandler.js';
|
|
18
|
+
export { SubscriptionManager, type SubscriptionManagerDependencies } from './SubscriptionManager.js';
|
|
19
|
+
export { Subscription, type SubscriptionAttributes, type SubscriptionStatus } from './Subscription.js';
|
|
20
|
+
export { SubscriptionPlan, type SubscriptionPlanAttributes, type PlanInterval } from './SubscriptionPlan.js';
|
|
21
|
+
export { Invoice, type InvoiceAttributes, type InvoiceStatus } from './Invoice.js';
|
|
22
|
+
export { SyncStripeCustomerJob } from './SyncStripeCustomerJob.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{randomBytes as D}from"crypto";function h(n,t){let e=Symbol.for(n),i=globalThis;return i[e]||(i[e]=t()),i[e]}var p=class{stripe=null;config=null;configure(t){this.config=t,this.stripe=null}getConfig(){if(!this.config)throw new Error("StripeService not configured. Call Stripe.configure() in app.ts first.");return this.config}async getClient(){if(!this.stripe){if(!this.config)throw new Error("StripeService not configured. Call Stripe.configure() in app.ts first.");let{default:t}=await import("stripe");this.stripe=new t(this.config.secretKey,{apiVersion:"2024-04-10"})}return this.stripe}async createCustomer(t){return(await this.getClient()).customers.create({email:t.email,name:t.name||void 0,metadata:{userId:String(t.id)}})}async updateCustomer(t,e){return(await this.getClient()).customers.update(t,e)}async deleteCustomer(t){await(await this.getClient()).customers.del(t)}async getCustomer(t){return await(await this.getClient()).customers.retrieve(t)}async createSubscription(t,e,i){let r=await this.getClient(),s={customer:t,items:[{price:e}],metadata:i?.metadata,collection_method:i?.collectionMethod||"charge_automatically"};return i?.trialDays&&(s.trial_period_days=i.trialDays),i?.daysUntilDue&&(s.days_until_due=i.daysUntilDue),r.subscriptions.create(s)}async getSubscription(t){return(await this.getClient()).subscriptions.retrieve(t)}async cancelSubscription(t,e=!1){let i=await this.getClient();return e?i.subscriptions.cancel(t):i.subscriptions.update(t,{cancel_at_period_end:!0})}async resumeSubscription(t){return(await this.getClient()).subscriptions.update(t,{cancel_at_period_end:!1})}async updateSubscription(t,e,i){let r=await this.getClient(),s=await r.subscriptions.retrieve(t);if(!s.items.data[0])throw new Error("Subscription has no items");return r.subscriptions.update(t,{items:[{id:s.items.data[0].id,price:e}],proration_behavior:i?.proration||"create_prorations",metadata:i?.metadata})}async getInvoices(t,e=10){return(await(await this.getClient()).invoices.list({customer:t,limit:e})).data}async getInvoice(t){return(await this.getClient()).invoices.retrieve(t)}async createCheckoutSession(t,e,i,r,s){let o=await this.getClient(),a={customer:t,payment_method_types:["card"],line_items:[{price:e,quantity:1}],mode:"subscription",success_url:i,cancel_url:r,metadata:s?.metadata};return s?.trialDays&&(a.subscription_data={trial_period_days:s.trialDays}),o.checkout.sessions.create(a)}async createPortalSession(t,e){return(await this.getClient()).billingPortal.sessions.create({customer:t,return_url:e})}async constructWebhookEvent(t,e){if(!this.config)throw new Error("StripeService not configured");return(await this.getClient()).webhooks.constructEvent(t,e,this.config.webhookSecret)}async listPrices(t,e=100){let i=await this.getClient(),r={limit:e,expand:["data.product"]};return t&&(r.product=t),(await i.prices.list(r)).data}async listProducts(t=100){return(await(await this.getClient()).products.list({limit:t})).data}async refundInvoice(t){let e=await this.getClient(),i=await e.invoices.retrieve(t),r=i.charge??i.payment_intent;if(!r)throw new Error("Invoice has no associated charge or payment intent");return e.refunds.create({charge:String(r)})}async getRefund(t){return(await this.getClient()).refunds.retrieve(t)}async listRefunds(t){return(await(await this.getClient()).refunds.list({charge:t})).data}};var d=class{handlers=new Map;on(t,e){return this.handlers.has(t)||this.handlers.set(t,[]),this.handlers.get(t).push(e),this}once(t,e){let i=async r=>{await e(r),this.off(t,i)};return this.on(t,i)}off(t,e){let i=this.handlers.get(t);if(i){let r=i.indexOf(e);r>-1&&i.splice(r,1)}}removeAllListeners(t){t?this.handlers.delete(t):this.handlers.clear()}async handle(t){let e=this.handlers.get(t.type)||[];for(let i of e)try{await i(t)}catch(r){throw console.error(`Error handling webhook event ${t.type}:`,r),r}}getHandlers(t){return this.handlers.get(t)||[]}getEventTypes(){return Array.from(this.handlers.keys())}hasHandlers(t){let e=this.handlers.get(t);return!!e&&e.length>0}};function m(n){let t=n.current_period_start;return typeof t=="number"?new Date(t*1e3):new Date(t)}function b(n){let t=n.current_period_end;return typeof t=="number"?new Date(t*1e3):new Date(t)}function w(n){let t=n.trial_end;return t?typeof t=="number"?new Date(t*1e3):new Date(t):null}function P(n){let t=n.canceled_at;return t?typeof t=="number"?new Date(t*1e3):new Date(t):null}var u=class{stripeService;deps;constructor(t){this.stripeService=t.stripeService,this.deps=t}async subscribe(t,e,i){let r=await this.deps.userRepository?.findById(t);if(!r)throw new Error(`User ${t} not found`);let s=await this.deps.planRepository?.findById(e);if(!s)throw new Error(`Plan ${e} not found`);let o=r.stripe_customer_id;o||(o=(await this.stripeService.createCustomer({id:t,name:r.name,email:r.email})).id,await this.deps.userRepository?.update(t,{stripe_customer_id:o}));let a=await this.stripeService.createSubscription(o,s.stripePriceId,{trialDays:i?.trialDays||s.trialDays,metadata:{userId:String(t),planId:String(e),...i?.metadata}}),c=await this.deps.subscriptionRepository?.create({user_id:t,stripe_subscription_id:a.id,stripe_customer_id:o,plan_id:e,status:a.status,current_period_start:m(a),current_period_end:b(a),trial_ends_at:w(a),cancel_at_period_end:a.cancel_at_period_end,canceled_at:P(a)});if(!c)throw new Error("Failed to create subscription record");return c}async upgrade(t,e){let i=await this.deps.subscriptionRepository?.findByUserId(t);if(!i)throw new Error(`User ${t} has no active subscription`);let r=await this.deps.planRepository?.findById(e);if(!r)throw new Error(`Plan ${e} not found`);let s=await this.stripeService.updateSubscription(i.stripeSubscriptionId,r.stripePriceId,{proration:"create_prorations"}),o=await this.deps.subscriptionRepository?.update(i.id,{plan_id:e,status:s.status,current_period_start:m(s),current_period_end:b(s)});if(!o)throw new Error("Failed to update subscription record");return o}async downgrade(t,e){let i=await this.deps.subscriptionRepository?.findByUserId(t);if(!i)throw new Error(`User ${t} has no active subscription`);let r=await this.deps.planRepository?.findById(e);if(!r)throw new Error(`Plan ${e} not found`);let s=await this.stripeService.updateSubscription(i.stripeSubscriptionId,r.stripePriceId,{proration:"none"}),o=await this.deps.subscriptionRepository?.update(i.id,{plan_id:e,status:s.status});if(!o)throw new Error("Failed to update subscription record");return o}async cancel(t,e=!1){let i=await this.deps.subscriptionRepository?.findByUserId(t);if(!i)throw new Error(`User ${t} has no active subscription`);await this.stripeService.cancelSubscription(i.stripeSubscriptionId,e),e?await this.deps.subscriptionRepository?.update(i.id,{status:"canceled",canceled_at:new Date}):await this.deps.subscriptionRepository?.update(i.id,{cancel_at_period_end:!0})}async resume(t){let e=await this.deps.subscriptionRepository?.findByUserId(t);if(!e)throw new Error(`User ${t} has no subscription`);let i=await this.stripeService.resumeSubscription(e.stripeSubscriptionId),r=await this.deps.subscriptionRepository?.update(e.id,{cancel_at_period_end:!1,canceled_at:null,status:i.status});if(!r)throw new Error("Failed to update subscription record");return r}async isSubscribed(t,e){let i=await this.deps.subscriptionRepository?.findByUserId(t);return!(!i||!i.isActive()||e&&i.planId!==e)}async onTrial(t){let e=await this.deps.subscriptionRepository?.findByUserId(t);return e?e.isOnTrial():!1}async hasFeature(t,e){let i=await this.deps.subscriptionRepository?.findByUserId(t);if(!i)return!1;let r=await this.deps.planRepository?.findById(i.planId);return r?r.hasFeature(e):!1}async syncFromStripe(t){let e=await this.deps.userRepository?.findById(t);if(!e||!e.stripe_customer_id)throw new Error(`User ${t} not found or has no Stripe customer`);let r=await(await this.stripeService.getClient()).subscriptions.list({customer:e.stripe_customer_id,status:"active",limit:1});if(r.data.length===0)throw new Error(`User ${t} has no active Stripe subscription`);let s=r.data[0],o=s.items.data[0]?.price?.id,a=await this.deps.planRepository?.findByStripePriceId(o);if(!a)throw new Error(`No plan found for Stripe price ${o}`);let c=await this.deps.subscriptionRepository?.findByStripeSubscriptionId(s.id),l={user_id:t,stripe_subscription_id:s.id,stripe_customer_id:e.stripe_customer_id,plan_id:a.id,status:s.status,current_period_start:m(s),current_period_end:b(s),trial_ends_at:w(s),cancel_at_period_end:s.cancel_at_period_end,canceled_at:P(s)};if(c?c=await this.deps.subscriptionRepository?.update(c.id,l):c=await this.deps.subscriptionRepository?.create(l),!c)throw new Error("Failed to sync subscription");return c}};var y=class{id;userId;stripeSubscriptionId;stripeCustomerId;planId;status;currentPeriodStart;currentPeriodEnd;cancelAtPeriodEnd;trialEndsAt;canceledAt;createdAt;updatedAt;constructor(t){this.id=t.id,this.userId=t.userId,this.stripeSubscriptionId=t.stripeSubscriptionId,this.stripeCustomerId=t.stripeCustomerId,this.planId=t.planId,this.status=t.status,this.currentPeriodStart=t.currentPeriodStart,this.currentPeriodEnd=t.currentPeriodEnd,this.cancelAtPeriodEnd=t.cancelAtPeriodEnd,this.trialEndsAt=t.trialEndsAt,this.canceledAt=t.canceledAt,this.createdAt=t.createdAt,this.updatedAt=t.updatedAt}isActive(){return this.status==="active"}isPastDue(){return this.status==="past_due"}isCanceled(){return this.status==="canceled"}isOnTrial(){return this.status==="trialing"||this.trialEndsAt!==null&&new Date<this.trialEndsAt}isOnGracePeriod(){return this.cancelAtPeriodEnd&&this.status!=="canceled"}isIncomplete(){return this.status==="incomplete"}isPaused(){return this.status==="paused"}daysUntilEnd(){let t=this.currentPeriodEnd.getTime()-Date.now();return Math.ceil(t/(1e3*60*60*24))}};var f=class{id;name;stripePriceId;stripeProductId;price;currency;interval;intervalCount;trialDays;features;sortOrder;active;createdAt;updatedAt;constructor(t){this.id=t.id,this.name=t.name,this.stripePriceId=t.stripePriceId,this.stripeProductId=t.stripeProductId,this.price=t.price,this.currency=t.currency,this.interval=t.interval,this.intervalCount=t.intervalCount,this.trialDays=t.trialDays,this.features=t.features,this.sortOrder=t.sortOrder,this.active=t.active,this.createdAt=t.createdAt,this.updatedAt=t.updatedAt}isActive(){return this.active}monthlyPrice(){let t=this.price/100;return this.interval==="month"?t:t/12}yearlyPrice(){let t=this.price/100;return this.interval==="year"?t:t*12}hasFeature(t){return this.features.includes(t)}formattedPrice(){let t=(this.price/100).toFixed(2),e=this.intervalCount>1?`${this.intervalCount} ${this.interval}s`:this.interval;return`${this.currency.toUpperCase()} ${t}/${e}`}intervalLabel(){return this.intervalCount>1?`Every ${this.intervalCount} ${this.interval}s`:`Every ${this.interval}`}trialLabel(){return this.trialDays===0?"No trial":`${this.trialDays} day${this.trialDays!==1?"s":""} free trial`}};var g=class{id;userId;subscriptionId;stripeInvoiceId;amountDue;amountPaid;currency;status;paidAt;dueDate;invoicePdf;createdAt;updatedAt;constructor(t){this.id=t.id,this.userId=t.userId,this.subscriptionId=t.subscriptionId,this.stripeInvoiceId=t.stripeInvoiceId,this.amountDue=t.amountDue,this.amountPaid=t.amountPaid,this.currency=t.currency,this.status=t.status,this.paidAt=t.paidAt,this.dueDate=t.dueDate,this.invoicePdf=t.invoicePdf,this.createdAt=t.createdAt,this.updatedAt=t.updatedAt}isPaid(){return this.status==="paid"}isOpen(){return this.status==="open"}isDraft(){return this.status==="draft"}isVoid(){return this.status==="void"}isUncollectible(){return this.status==="uncollectible"}isOverdue(){return!this.dueDate||this.status==="paid"||this.status==="void"?!1:new Date>this.dueDate}outstandingAmount(){return this.amountDue-this.amountPaid}formattedAmountDue(){return this.formatCurrency(this.amountDue)}formattedAmountPaid(){return this.formatCurrency(this.amountPaid)}formattedOutstanding(){return this.formatCurrency(this.outstandingAmount())}daysUntilDue(){return this.dueDate?Math.ceil((this.dueDate.getTime()-Date.now())/(1e3*60*60*24)):null}formatCurrency(t){return`${{usd:"$",eur:"\u20AC",gbp:"\xA3",jpy:"\xA5",cad:"C$",aud:"A$",chf:"CHF"}[this.currency.toLowerCase()]||this.currency.toUpperCase()}${(t/100).toFixed(2)}`}};var S=class n{constructor(t,e,i){this.userId=t;this.email=e;this.userName=i}name="sync-stripe-customer";async handle(t){let e=await t.createCustomer({id:this.userId,name:this.userName,email:this.email});console.log(`Synced user ${this.userId} to Stripe customer ${e.id}`)}serialize(){return JSON.stringify({userId:this.userId,email:this.email,name:this.userName})}static restore(t){return new n(t.userId,t.email,t.name)}};var v=class{stripeService=new p;webhookHandler=new d;subscriptionManager=null;configure(t){this.stripeService.configure(t)}service(){return this.stripeService}webhooks(){return this.webhookHandler}subscriptions(t){if(!this.subscriptionManager){if(!t)throw new Error("SubscriptionManager not initialized. Call Stripe.subscriptions({ stripeService, ... }) with dependencies first.");this.subscriptionManager=new u(t)}return this.subscriptionManager}initSubscriptions(t){return this.subscriptionManager=new u({stripeService:this.stripeService,...t}),this.subscriptionManager}},q=h("svelar.stripe",()=>new v);export{g as Invoice,q as Stripe,v as StripeManager,p as StripeService,d as StripeWebhookHandler,y as Subscription,u as SubscriptionManager,f as SubscriptionPlan,S as SyncStripeCustomerJob};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beeblock/svelar",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Laravel-inspired framework on top of SvelteKit 2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -241,6 +241,10 @@
|
|
|
241
241
|
"./excel": {
|
|
242
242
|
"types": "./dist/excel/index.d.ts",
|
|
243
243
|
"import": "./dist/excel/index.js"
|
|
244
|
+
},
|
|
245
|
+
"./stripe": {
|
|
246
|
+
"types": "./dist/stripe/index.d.ts",
|
|
247
|
+
"import": "./dist/stripe/index.js"
|
|
244
248
|
}
|
|
245
249
|
},
|
|
246
250
|
"scripts": {
|
|
@@ -262,8 +266,10 @@
|
|
|
262
266
|
"devDependencies": {
|
|
263
267
|
"@types/node": "^22.0.0",
|
|
264
268
|
"@types/pdfkit": "^0.17.5",
|
|
269
|
+
"@vitest/coverage-v8": "^2.1.9",
|
|
265
270
|
"better-sqlite3": "^11.0.0",
|
|
266
271
|
"exceljs": "^4.4.0",
|
|
272
|
+
"stripe": "^21.0.1",
|
|
267
273
|
"sveltekit-superforms": "^2.30.1",
|
|
268
274
|
"tsup": "^8.3.0",
|
|
269
275
|
"typescript": "^5.7.0",
|
|
@@ -277,12 +283,13 @@
|
|
|
277
283
|
"bullmq": ">=5.0.0",
|
|
278
284
|
"exceljs": ">=4.4.0",
|
|
279
285
|
"ioredis": ">=5.0.0",
|
|
286
|
+
"meilisearch": ">=0.44.0",
|
|
280
287
|
"mysql2": "*",
|
|
281
288
|
"pdfkit": ">=0.18.0",
|
|
282
289
|
"postgres": "*",
|
|
283
290
|
"pusher-js": ">=8.0.0",
|
|
284
|
-
"
|
|
285
|
-
"
|
|
291
|
+
"stripe": ">=14.0.0",
|
|
292
|
+
"sveltekit-superforms": ">=2.0.0"
|
|
286
293
|
},
|
|
287
294
|
"peerDependenciesMeta": {
|
|
288
295
|
"@aws-sdk/client-s3": {
|
|
@@ -320,6 +327,9 @@
|
|
|
320
327
|
},
|
|
321
328
|
"meilisearch": {
|
|
322
329
|
"optional": true
|
|
330
|
+
},
|
|
331
|
+
"stripe": {
|
|
332
|
+
"optional": true
|
|
323
333
|
}
|
|
324
334
|
},
|
|
325
335
|
"keywords": [
|
|
@@ -334,7 +344,10 @@
|
|
|
334
344
|
"session",
|
|
335
345
|
"cache",
|
|
336
346
|
"queue",
|
|
337
|
-
"mail"
|
|
347
|
+
"mail",
|
|
348
|
+
"stripe",
|
|
349
|
+
"billing",
|
|
350
|
+
"subscriptions"
|
|
338
351
|
],
|
|
339
352
|
"license": "MIT",
|
|
340
353
|
"author": "Raoni <alephtus@gmail.com>",
|