@chaicodes-com/stripe-helpers 1.0.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.
- package/README.md +162 -0
- package/dist/checkout/createCheckoutSession.d.ts +26 -0
- package/dist/checkout/createCheckoutSession.d.ts.map +1 -0
- package/dist/checkout/createCheckoutSession.js +55 -0
- package/dist/checkout/createCheckoutSession.js.map +1 -0
- package/dist/checkout/handlePriceSwitching.d.ts +20 -0
- package/dist/checkout/handlePriceSwitching.d.ts.map +1 -0
- package/dist/checkout/handlePriceSwitching.js +59 -0
- package/dist/checkout/handlePriceSwitching.js.map +1 -0
- package/dist/config/defaultConfig.d.ts +45 -0
- package/dist/config/defaultConfig.d.ts.map +1 -0
- package/dist/config/defaultConfig.js +71 -0
- package/dist/config/defaultConfig.js.map +1 -0
- package/dist/database/databaseUtilities.d.ts +45 -0
- package/dist/database/databaseUtilities.d.ts.map +1 -0
- package/dist/database/databaseUtilities.js +224 -0
- package/dist/database/databaseUtilities.js.map +1 -0
- package/dist/email/emailTemplates.d.ts +18 -0
- package/dist/email/emailTemplates.d.ts.map +1 -0
- package/dist/email/emailTemplates.js +130 -0
- package/dist/email/emailTemplates.js.map +1 -0
- package/dist/email/sendAccessCodeEmail.d.ts +30 -0
- package/dist/email/sendAccessCodeEmail.d.ts.map +1 -0
- package/dist/email/sendAccessCodeEmail.js +61 -0
- package/dist/email/sendAccessCodeEmail.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/utilityFunctions.d.ts +20 -0
- package/dist/utils/utilityFunctions.d.ts.map +1 -0
- package/dist/utils/utilityFunctions.js +68 -0
- package/dist/utils/utilityFunctions.js.map +1 -0
- package/dist/webhooks/handleCheckoutCompleted.d.ts +26 -0
- package/dist/webhooks/handleCheckoutCompleted.d.ts.map +1 -0
- package/dist/webhooks/handleCheckoutCompleted.js +75 -0
- package/dist/webhooks/handleCheckoutCompleted.js.map +1 -0
- package/dist/webhooks/handleStripeWebhook.d.ts +21 -0
- package/dist/webhooks/handleStripeWebhook.d.ts.map +1 -0
- package/dist/webhooks/handleStripeWebhook.js +68 -0
- package/dist/webhooks/handleStripeWebhook.js.map +1 -0
- package/dist/webhooks/subscriptionHandlers.d.ts +25 -0
- package/dist/webhooks/subscriptionHandlers.d.ts.map +1 -0
- package/dist/webhooks/subscriptionHandlers.js +72 -0
- package/dist/webhooks/subscriptionHandlers.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# @chaicodes/stripe-helpers
|
|
2
|
+
|
|
3
|
+
Reusable Stripe payment and subscription utilities for ChAICodes applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
✅ **Monthly + Annual Subscriptions** — Flexible billing periods
|
|
8
|
+
✅ **Access Code Generation** — Automatic per-user access codes
|
|
9
|
+
✅ **Webhook Handling** — Auto-routes Stripe events
|
|
10
|
+
✅ **Session Management** — Max 2 concurrent sessions per code
|
|
11
|
+
✅ **Email Integration** — Sends access codes via SMTP
|
|
12
|
+
✅ **Supabase Integration** — Full database abstraction
|
|
13
|
+
✅ **Price Switching** — Change billing periods with proration
|
|
14
|
+
✅ **Token Support** — Limited-use codes for paid tokens
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @chaicodes/stripe-helpers
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import {
|
|
26
|
+
createCheckoutSession,
|
|
27
|
+
handleStripeWebhook,
|
|
28
|
+
sendAccessCodeEmail,
|
|
29
|
+
} from '@chaicodes/stripe-helpers';
|
|
30
|
+
|
|
31
|
+
// Create checkout session
|
|
32
|
+
const session = await createCheckoutSession({
|
|
33
|
+
stripe: stripeClient,
|
|
34
|
+
email: 'user@example.com',
|
|
35
|
+
billingPeriod: 'annual',
|
|
36
|
+
priceIds: {
|
|
37
|
+
monthly: 'price_xxx',
|
|
38
|
+
annual: 'price_yyy',
|
|
39
|
+
},
|
|
40
|
+
appConfig: { appName: 'MyApp', appUrl: 'https://myapp.com' },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Handle webhook
|
|
44
|
+
const result = await handleStripeWebhook({
|
|
45
|
+
body: request.body,
|
|
46
|
+
signature: request.headers['stripe-signature'],
|
|
47
|
+
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
|
|
48
|
+
supabaseClient: supabase,
|
|
49
|
+
config: appConfig,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
Required environment variables:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
STRIPE_SECRET_KEY
|
|
59
|
+
STRIPE_PUBLIC_KEY
|
|
60
|
+
STRIPE_WEBHOOK_SECRET
|
|
61
|
+
STRIPE_PRICE_ID_MONTHLY
|
|
62
|
+
STRIPE_PRICE_ID_ANNUAL
|
|
63
|
+
SUPABASE_URL
|
|
64
|
+
SUPABASE_ANON_KEY
|
|
65
|
+
SMTP_HOST
|
|
66
|
+
SMTP_PORT
|
|
67
|
+
SMTP_USER
|
|
68
|
+
SMTP_PASSWORD
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Supabase Schema
|
|
72
|
+
|
|
73
|
+
Create these tables:
|
|
74
|
+
|
|
75
|
+
```sql
|
|
76
|
+
-- Access codes table
|
|
77
|
+
CREATE TABLE access_codes (
|
|
78
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
79
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
80
|
+
code VARCHAR(50) UNIQUE NOT NULL,
|
|
81
|
+
max_uses INTEGER DEFAULT 1,
|
|
82
|
+
current_uses INTEGER DEFAULT 0,
|
|
83
|
+
is_active BOOLEAN DEFAULT TRUE,
|
|
84
|
+
is_subscription BOOLEAN DEFAULT FALSE,
|
|
85
|
+
email VARCHAR(255),
|
|
86
|
+
subscription_id VARCHAR(255),
|
|
87
|
+
expires_at TIMESTAMP,
|
|
88
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
-- Sessions table
|
|
92
|
+
CREATE TABLE sessions (
|
|
93
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
94
|
+
access_code VARCHAR(50) REFERENCES access_codes(code),
|
|
95
|
+
session_id VARCHAR(255) UNIQUE NOT NULL,
|
|
96
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
97
|
+
last_activity_at TIMESTAMP DEFAULT NOW(),
|
|
98
|
+
expires_at TIMESTAMP,
|
|
99
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## API Reference
|
|
104
|
+
|
|
105
|
+
### createCheckoutSession(options)
|
|
106
|
+
|
|
107
|
+
Creates a Stripe checkout session for subscriptions.
|
|
108
|
+
|
|
109
|
+
**Options:**
|
|
110
|
+
- `stripe` — Stripe client instance
|
|
111
|
+
- `email` — Customer email
|
|
112
|
+
- `billingPeriod` — 'monthly' or 'annual'
|
|
113
|
+
- `priceIds` — Object with monthly/annual price IDs
|
|
114
|
+
- `appConfig` — App-specific configuration
|
|
115
|
+
- `successUrl` — Redirect URL on success
|
|
116
|
+
- `cancelUrl` — Redirect URL on cancel
|
|
117
|
+
|
|
118
|
+
### handleStripeWebhook(options)
|
|
119
|
+
|
|
120
|
+
Handles all Stripe webhook events.
|
|
121
|
+
|
|
122
|
+
**Options:**
|
|
123
|
+
- `body` — Raw request body
|
|
124
|
+
- `signature` — Stripe-Signature header
|
|
125
|
+
- `stripeSecretKey` — Your Stripe secret key
|
|
126
|
+
- `supabaseClient` — Initialized Supabase client
|
|
127
|
+
- `config` — Configuration object
|
|
128
|
+
|
|
129
|
+
**Handles:**
|
|
130
|
+
- `checkout.session.completed` — Creates access code
|
|
131
|
+
- `customer.subscription.updated` — Updates access status
|
|
132
|
+
- `customer.subscription.deleted` — Revokes access
|
|
133
|
+
|
|
134
|
+
### sendAccessCodeEmail(options)
|
|
135
|
+
|
|
136
|
+
Sends access code via email.
|
|
137
|
+
|
|
138
|
+
**Options:**
|
|
139
|
+
- `email` — Recipient email
|
|
140
|
+
- `accessCode` — Access code to send
|
|
141
|
+
- `appName` — Application name
|
|
142
|
+
- `emailTemplate` — HTML template
|
|
143
|
+
- `smtpConfig` — SMTP configuration
|
|
144
|
+
- `expiresAt` — Expiration date
|
|
145
|
+
|
|
146
|
+
### Database Functions
|
|
147
|
+
|
|
148
|
+
- `createAccessCode(supabaseClient, codeData)`
|
|
149
|
+
- `getAccessCodeByCode(supabaseClient, code)`
|
|
150
|
+
- `updateAccessCodeStatus(supabaseClient, updates)`
|
|
151
|
+
- `createSession(supabaseClient, {access_code, session_id})`
|
|
152
|
+
- `validateSession(supabaseClient, accessCode, sessionId)`
|
|
153
|
+
|
|
154
|
+
### Utility Functions
|
|
155
|
+
|
|
156
|
+
- `generateAccessCode(email, prefix)` — Generate formatted code
|
|
157
|
+
- `calculateExpiryDate(billingPeriod)` — Get expiry date
|
|
158
|
+
- `getDaysRemaining(expiresAt)` — Calculate days left
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
MIT © ChAI Wang
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createCheckoutSession.ts
|
|
3
|
+
* Creates a Stripe checkout session for subscriptions or one-time purchases
|
|
4
|
+
*/
|
|
5
|
+
interface CheckoutSessionParams {
|
|
6
|
+
stripe: any;
|
|
7
|
+
email: string;
|
|
8
|
+
billingPeriod?: 'monthly' | 'annual';
|
|
9
|
+
priceIds?: Record<string, string>;
|
|
10
|
+
appConfig?: {
|
|
11
|
+
appUrl?: string;
|
|
12
|
+
appName?: string;
|
|
13
|
+
};
|
|
14
|
+
successUrl?: string;
|
|
15
|
+
cancelUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
interface CheckoutSessionResponse {
|
|
18
|
+
success: boolean;
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
session?: any;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function createCheckoutSession({ stripe, email, billingPeriod, priceIds, appConfig, successUrl, cancelUrl, }: CheckoutSessionParams): Promise<CheckoutSessionResponse>;
|
|
25
|
+
export default createCheckoutSession;
|
|
26
|
+
//# sourceMappingURL=createCheckoutSession.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createCheckoutSession.d.ts","sourceRoot":"","sources":["../../src/checkout/createCheckoutSession.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,UAAU,qBAAqB;IAC7B,MAAM,EAAE,GAAG,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,SAAS,CAAC,EAAE;QACV,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,uBAAuB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,qBAAqB,CAAC,EAC1C,MAAM,EACN,KAAK,EACL,aAAwB,EACxB,QAAa,EACb,SAAc,EACd,UAAe,EACf,SAAc,GACf,EAAE,qBAAqB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAiD1D;AAED,eAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* createCheckoutSession.ts
|
|
4
|
+
* Creates a Stripe checkout session for subscriptions or one-time purchases
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createCheckoutSession = createCheckoutSession;
|
|
8
|
+
async function createCheckoutSession({ stripe, email, billingPeriod = 'annual', priceIds = {}, appConfig = {}, successUrl = '', cancelUrl = '', }) {
|
|
9
|
+
if (!stripe) {
|
|
10
|
+
throw new Error('Stripe client is required');
|
|
11
|
+
}
|
|
12
|
+
if (!email) {
|
|
13
|
+
throw new Error('Email is required');
|
|
14
|
+
}
|
|
15
|
+
if (!priceIds[billingPeriod]) {
|
|
16
|
+
throw new Error(`Price ID for billing period "${billingPeriod}" not found`);
|
|
17
|
+
}
|
|
18
|
+
const priceId = priceIds[billingPeriod];
|
|
19
|
+
try {
|
|
20
|
+
const session = await stripe.checkout.sessions.create({
|
|
21
|
+
customer_email: email,
|
|
22
|
+
mode: 'subscription',
|
|
23
|
+
payment_method_types: ['card'],
|
|
24
|
+
line_items: [
|
|
25
|
+
{
|
|
26
|
+
price: priceId,
|
|
27
|
+
quantity: 1,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
success_url: successUrl || `${appConfig.appUrl || ''}/success?session_id={CHECKOUT_SESSION_ID}`,
|
|
31
|
+
cancel_url: cancelUrl || `${appConfig.appUrl || ''}/cancel`,
|
|
32
|
+
metadata: {
|
|
33
|
+
appName: appConfig.appName || 'ChAICodes App',
|
|
34
|
+
billingPeriod: billingPeriod,
|
|
35
|
+
email: email,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
success: true,
|
|
40
|
+
sessionId: session.id,
|
|
41
|
+
url: session.url,
|
|
42
|
+
session: session,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
47
|
+
console.error('Error creating checkout session:', error);
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
error: message,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.default = createCheckoutSession;
|
|
55
|
+
//# sourceMappingURL=createCheckoutSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createCheckoutSession.js","sourceRoot":"","sources":["../../src/checkout/createCheckoutSession.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAuBH,sDAyDC;AAzDM,KAAK,UAAU,qBAAqB,CAAC,EAC1C,MAAM,EACN,KAAK,EACL,aAAa,GAAG,QAAQ,EACxB,QAAQ,GAAG,EAAE,EACb,SAAS,GAAG,EAAE,EACd,UAAU,GAAG,EAAE,EACf,SAAS,GAAG,EAAE,GACQ;IACtB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,gCAAgC,aAAa,aAAa,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;YACpD,cAAc,EAAE,KAAK;YACrB,IAAI,EAAE,cAAc;YACpB,oBAAoB,EAAE,CAAC,MAAM,CAAC;YAC9B,UAAU,EAAE;gBACV;oBACE,KAAK,EAAE,OAAO;oBACd,QAAQ,EAAE,CAAC;iBACZ;aACF;YACD,WAAW,EAAE,UAAU,IAAI,GAAG,SAAS,CAAC,MAAM,IAAI,EAAE,2CAA2C;YAC/F,UAAU,EAAE,SAAS,IAAI,GAAG,SAAS,CAAC,MAAM,IAAI,EAAE,SAAS;YAC3D,QAAQ,EAAE;gBACR,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,eAAe;gBAC7C,aAAa,EAAE,aAAa;gBAC5B,KAAK,EAAE,KAAK;aACb;SACF,CAAC,CAAC;QAEH,OAAO;YACL,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,OAAO,EAAE,OAAO;SACjB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,OAAO;SACf,CAAC;IACJ,CAAC;AACH,CAAC;AAED,kBAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* handlePriceSwitching.ts
|
|
3
|
+
* Handles subscription price changes (monthly to annual or vice versa)
|
|
4
|
+
*/
|
|
5
|
+
interface PriceSwitchingParams {
|
|
6
|
+
stripe: any;
|
|
7
|
+
subscriptionId: string;
|
|
8
|
+
newPriceId: string;
|
|
9
|
+
billingPeriod?: 'annual' | 'monthly';
|
|
10
|
+
proRate?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface PriceSwitchingResponse {
|
|
13
|
+
success: boolean;
|
|
14
|
+
subscription?: any;
|
|
15
|
+
message?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function handlePriceSwitching({ stripe, subscriptionId, newPriceId, billingPeriod, proRate, }: PriceSwitchingParams): Promise<PriceSwitchingResponse>;
|
|
19
|
+
export default handlePriceSwitching;
|
|
20
|
+
//# sourceMappingURL=handlePriceSwitching.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlePriceSwitching.d.ts","sourceRoot":"","sources":["../../src/checkout/handlePriceSwitching.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,UAAU,oBAAoB;IAC5B,MAAM,EAAE,GAAG,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,sBAAsB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,oBAAoB,CAAC,EACzC,MAAM,EACN,cAAc,EACd,UAAU,EACV,aAAwB,EACxB,OAAc,GACf,EAAE,oBAAoB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAwDxD;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* handlePriceSwitching.ts
|
|
4
|
+
* Handles subscription price changes (monthly to annual or vice versa)
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.handlePriceSwitching = handlePriceSwitching;
|
|
8
|
+
async function handlePriceSwitching({ stripe, subscriptionId, newPriceId, billingPeriod = 'annual', proRate = true, }) {
|
|
9
|
+
if (!stripe) {
|
|
10
|
+
throw new Error('Stripe client is required');
|
|
11
|
+
}
|
|
12
|
+
if (!subscriptionId) {
|
|
13
|
+
throw new Error('Subscription ID is required');
|
|
14
|
+
}
|
|
15
|
+
if (!newPriceId) {
|
|
16
|
+
throw new Error('New price ID is required');
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
// Get current subscription
|
|
20
|
+
const subscription = await stripe.subscriptions.retrieve(subscriptionId);
|
|
21
|
+
if (!subscription) {
|
|
22
|
+
throw new Error('Subscription not found');
|
|
23
|
+
}
|
|
24
|
+
// Get current item
|
|
25
|
+
const currentItem = subscription.items.data[0];
|
|
26
|
+
if (!currentItem) {
|
|
27
|
+
throw new Error('No items found in subscription');
|
|
28
|
+
}
|
|
29
|
+
// Update subscription with new price
|
|
30
|
+
const updatedSubscription = await stripe.subscriptions.update(subscriptionId, {
|
|
31
|
+
items: [
|
|
32
|
+
{
|
|
33
|
+
id: currentItem.id,
|
|
34
|
+
price: newPriceId,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
proration_behavior: proRate ? 'create_prorations' : 'none',
|
|
38
|
+
metadata: {
|
|
39
|
+
billingPeriod: billingPeriod,
|
|
40
|
+
changedAt: new Date().toISOString(),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
success: true,
|
|
45
|
+
subscription: updatedSubscription,
|
|
46
|
+
message: `Subscription updated to ${billingPeriod} billing`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
+
console.error('Error switching subscription price:', error);
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: message,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.default = handlePriceSwitching;
|
|
59
|
+
//# sourceMappingURL=handlePriceSwitching.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlePriceSwitching.js","sourceRoot":"","sources":["../../src/checkout/handlePriceSwitching.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAiBH,oDA8DC;AA9DM,KAAK,UAAU,oBAAoB,CAAC,EACzC,MAAM,EACN,cAAc,EACd,UAAU,EACV,aAAa,GAAG,QAAQ,EACxB,OAAO,GAAG,IAAI,GACO;IACrB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC;QACH,2BAA2B;QAC3B,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QAEzE,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QAED,mBAAmB;QACnB,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE/C,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;QAED,qCAAqC;QACrC,MAAM,mBAAmB,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE;YAC5E,KAAK,EAAE;gBACL;oBACE,EAAE,EAAE,WAAW,CAAC,EAAE;oBAClB,KAAK,EAAE,UAAU;iBAClB;aACF;YACD,kBAAkB,EAAE,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,MAAM;YAC1D,QAAQ,EAAE;gBACR,aAAa,EAAE,aAAa;gBAC5B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC;SACF,CAAC,CAAC;QAEH,OAAO;YACL,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,mBAAmB;YACjC,OAAO,EAAE,2BAA2B,aAAa,UAAU;SAC5D,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC5D,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,OAAO;SACf,CAAC;IACJ,CAAC;AACH,CAAC;AAED,kBAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defaultConfig.ts
|
|
3
|
+
* Default configuration for @chaicodes/stripe-helpers
|
|
4
|
+
*/
|
|
5
|
+
interface SmtpConfig {
|
|
6
|
+
host?: string;
|
|
7
|
+
port?: number | string;
|
|
8
|
+
secure?: boolean;
|
|
9
|
+
user?: string;
|
|
10
|
+
password?: string;
|
|
11
|
+
from?: string;
|
|
12
|
+
}
|
|
13
|
+
interface PriceIds {
|
|
14
|
+
monthly?: string;
|
|
15
|
+
annual?: string;
|
|
16
|
+
}
|
|
17
|
+
interface AppConfig {
|
|
18
|
+
appName: string;
|
|
19
|
+
appUrl: string;
|
|
20
|
+
supportEmail: string;
|
|
21
|
+
accessCodePrefix: string;
|
|
22
|
+
accessCodeLength: number;
|
|
23
|
+
stripeSecretKey?: string;
|
|
24
|
+
stripePublicKey?: string;
|
|
25
|
+
stripeWebhookSecret?: string;
|
|
26
|
+
priceIds: PriceIds;
|
|
27
|
+
supabaseUrl?: string;
|
|
28
|
+
supabaseAnonKey?: string;
|
|
29
|
+
smtpConfig: SmtpConfig;
|
|
30
|
+
successUrl?: string;
|
|
31
|
+
cancelUrl?: string;
|
|
32
|
+
emailTemplate?: string | null;
|
|
33
|
+
brandColor: string;
|
|
34
|
+
logoUrl: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const DEFAULT_CONFIG: AppConfig;
|
|
37
|
+
export declare function validateConfig(config: any): boolean;
|
|
38
|
+
export declare function mergeConfig(userConfig?: Partial<AppConfig>): AppConfig;
|
|
39
|
+
declare const _default: {
|
|
40
|
+
DEFAULT_CONFIG: AppConfig;
|
|
41
|
+
validateConfig: typeof validateConfig;
|
|
42
|
+
mergeConfig: typeof mergeConfig;
|
|
43
|
+
};
|
|
44
|
+
export default _default;
|
|
45
|
+
//# sourceMappingURL=defaultConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaultConfig.d.ts","sourceRoot":"","sources":["../../src/config/defaultConfig.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,QAAQ;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,cAAc,EAAE,SA6C5B,CAAC;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAcnD;AAED,wBAAgB,WAAW,CAAC,UAAU,GAAE,OAAO,CAAC,SAAS,CAAM,GAAG,SAAS,CAK1E;;;;;;AAED,wBAIE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* defaultConfig.ts
|
|
4
|
+
* Default configuration for @chaicodes/stripe-helpers
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.DEFAULT_CONFIG = void 0;
|
|
8
|
+
exports.validateConfig = validateConfig;
|
|
9
|
+
exports.mergeConfig = mergeConfig;
|
|
10
|
+
exports.DEFAULT_CONFIG = {
|
|
11
|
+
// App info
|
|
12
|
+
appName: 'ChAICodes App',
|
|
13
|
+
appUrl: 'https://chaicodes.com',
|
|
14
|
+
supportEmail: 'support@chaicodes.com',
|
|
15
|
+
// Access codes
|
|
16
|
+
accessCodePrefix: 'SUB',
|
|
17
|
+
accessCodeLength: 20,
|
|
18
|
+
// Stripe
|
|
19
|
+
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
|
|
20
|
+
stripePublicKey: process.env.STRIPE_PUBLIC_KEY,
|
|
21
|
+
stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
|
|
22
|
+
// Price IDs (override per app)
|
|
23
|
+
priceIds: {
|
|
24
|
+
monthly: process.env.STRIPE_PRICE_ID_MONTHLY,
|
|
25
|
+
annual: process.env.STRIPE_PRICE_ID_ANNUAL,
|
|
26
|
+
},
|
|
27
|
+
// Supabase
|
|
28
|
+
supabaseUrl: process.env.SUPABASE_URL,
|
|
29
|
+
supabaseAnonKey: process.env.SUPABASE_ANON_KEY,
|
|
30
|
+
// SMTP Email
|
|
31
|
+
smtpConfig: {
|
|
32
|
+
host: process.env.SMTP_HOST,
|
|
33
|
+
port: process.env.SMTP_PORT || 587,
|
|
34
|
+
secure: process.env.SMTP_SECURE !== 'false',
|
|
35
|
+
user: process.env.SMTP_USER,
|
|
36
|
+
password: process.env.SMTP_PASSWORD,
|
|
37
|
+
from: process.env.SMTP_FROM || 'noreply@chaicodes.com',
|
|
38
|
+
},
|
|
39
|
+
// URLs
|
|
40
|
+
successUrl: process.env.SUCCESS_URL,
|
|
41
|
+
cancelUrl: process.env.CANCEL_URL,
|
|
42
|
+
// Email templates
|
|
43
|
+
emailTemplate: null, // Use default if null
|
|
44
|
+
// Branding
|
|
45
|
+
brandColor: '#C8102E',
|
|
46
|
+
logoUrl: 'https://chaicodes.com/logo.png',
|
|
47
|
+
};
|
|
48
|
+
function validateConfig(config) {
|
|
49
|
+
const requiredKeys = [
|
|
50
|
+
'stripeSecretKey',
|
|
51
|
+
'appName',
|
|
52
|
+
'supportEmail',
|
|
53
|
+
];
|
|
54
|
+
const missing = requiredKeys.filter(key => !config[key]);
|
|
55
|
+
if (missing.length > 0) {
|
|
56
|
+
throw new Error(`Missing required config: ${missing.join(', ')}`);
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
function mergeConfig(userConfig = {}) {
|
|
61
|
+
return {
|
|
62
|
+
...exports.DEFAULT_CONFIG,
|
|
63
|
+
...userConfig,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
exports.default = {
|
|
67
|
+
DEFAULT_CONFIG: exports.DEFAULT_CONFIG,
|
|
68
|
+
validateConfig,
|
|
69
|
+
mergeConfig,
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=defaultConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaultConfig.js","sourceRoot":"","sources":["../../src/config/defaultConfig.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAmFH,wCAcC;AAED,kCAKC;AApEY,QAAA,cAAc,GAAc;IACvC,WAAW;IACX,OAAO,EAAE,eAAe;IACxB,MAAM,EAAE,uBAAuB;IAC/B,YAAY,EAAE,uBAAuB;IAErC,eAAe;IACf,gBAAgB,EAAE,KAAK;IACvB,gBAAgB,EAAE,EAAE;IAEpB,SAAS;IACT,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;IAC9C,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;IAC9C,mBAAmB,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB;IAEtD,+BAA+B;IAC/B,QAAQ,EAAE;QACR,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB;QAC5C,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB;KAC3C;IAED,WAAW;IACX,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;IACrC,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;IAE9C,aAAa;IACb,UAAU,EAAE;QACV,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;QAC3B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,GAAG;QAClC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,OAAO;QAC3C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;QAC3B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;QACnC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,uBAAuB;KACvD;IAED,OAAO;IACP,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;IACnC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAEjC,kBAAkB;IAClB,aAAa,EAAE,IAAI,EAAE,sBAAsB;IAE3C,WAAW;IACX,UAAU,EAAE,SAAS;IACrB,OAAO,EAAE,gCAAgC;CAC1C,CAAC;AAEF,SAAgB,cAAc,CAAC,MAAW;IACxC,MAAM,YAAY,GAAG;QACnB,iBAAiB;QACjB,SAAS;QACT,cAAc;KACf,CAAC;IAEF,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,WAAW,CAAC,aAAiC,EAAE;IAC7D,OAAO;QACL,GAAG,sBAAc;QACjB,GAAG,UAAU;KACd,CAAC;AACJ,CAAC;AAED,kBAAe;IACb,cAAc,EAAd,sBAAc;IACd,cAAc;IACd,WAAW;CACZ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* databaseUtilities.ts
|
|
3
|
+
* Supabase database functions for access codes and sessions
|
|
4
|
+
*/
|
|
5
|
+
interface AccessCodeData {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
interface SessionData {
|
|
9
|
+
access_code: string;
|
|
10
|
+
session_id: string;
|
|
11
|
+
}
|
|
12
|
+
interface UpdateData {
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
subscription_id: string;
|
|
15
|
+
}
|
|
16
|
+
interface SuccessResponse<T> {
|
|
17
|
+
success: true;
|
|
18
|
+
data?: T;
|
|
19
|
+
valid?: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface ErrorResponse {
|
|
22
|
+
success: false;
|
|
23
|
+
error: string;
|
|
24
|
+
valid?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function createAccessCode(supabaseClient: any, codeData: AccessCodeData): Promise<SuccessResponse<any> | ErrorResponse>;
|
|
27
|
+
export declare function getAccessCodeByCode(supabaseClient: any, code: string): Promise<SuccessResponse<any> | ErrorResponse>;
|
|
28
|
+
export declare function updateAccessCodeStatus(supabaseClient: any, updates: UpdateData): Promise<SuccessResponse<any> | ErrorResponse>;
|
|
29
|
+
export declare function incrementAccessCodeUses(supabaseClient: any, code: string): Promise<SuccessResponse<any> | ErrorResponse>;
|
|
30
|
+
export declare function createSession(supabaseClient: any, { access_code, session_id }: SessionData): Promise<SuccessResponse<any> | ErrorResponse>;
|
|
31
|
+
export declare function validateSession(supabaseClient: any, accessCode: string, sessionId: string): Promise<SuccessResponse<any> | ErrorResponse>;
|
|
32
|
+
export declare function deleteSession(supabaseClient: any, sessionId: string): Promise<SuccessResponse<null> | ErrorResponse>;
|
|
33
|
+
export declare function cleanupExpiredSessions(supabaseClient: any, accessCode: string): Promise<SuccessResponse<null> | ErrorResponse>;
|
|
34
|
+
declare const _default: {
|
|
35
|
+
createAccessCode: typeof createAccessCode;
|
|
36
|
+
getAccessCodeByCode: typeof getAccessCodeByCode;
|
|
37
|
+
updateAccessCodeStatus: typeof updateAccessCodeStatus;
|
|
38
|
+
incrementAccessCodeUses: typeof incrementAccessCodeUses;
|
|
39
|
+
createSession: typeof createSession;
|
|
40
|
+
validateSession: typeof validateSession;
|
|
41
|
+
deleteSession: typeof deleteSession;
|
|
42
|
+
cleanupExpiredSessions: typeof cleanupExpiredSessions;
|
|
43
|
+
};
|
|
44
|
+
export default _default;
|
|
45
|
+
//# sourceMappingURL=databaseUtilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"databaseUtilities.d.ts","sourceRoot":"","sources":["../../src/database/databaseUtilities.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,UAAU,cAAc;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,UAAU,WAAW;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,UAAU;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,eAAe,CAAC,CAAC;IACzB,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID,wBAAsB,gBAAgB,CACpC,cAAc,EAAE,GAAG,EACnB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,CAqB/C;AAED,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,GAAG,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,CAsB/C;AAED,wBAAsB,sBAAsB,CAC1C,cAAc,EAAE,GAAG,EACnB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,CAsB/C;AAED,wBAAsB,uBAAuB,CAC3C,cAAc,EAAE,GAAG,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,CA8B/C;AAID,wBAAsB,aAAa,CACjC,cAAc,EAAE,GAAG,EACnB,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,WAAW,GACvC,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,CA6B/C;AAED,wBAAsB,eAAe,CACnC,cAAc,EAAE,GAAG,EACnB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,CA0B/C;AAED,wBAAsB,aAAa,CACjC,cAAc,EAAE,GAAG,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAoBhD;AAED,wBAAsB,sBAAsB,CAC1C,cAAc,EAAE,GAAG,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAqBhD;;;;;;;;;;;AAED,wBASE"}
|