@xenterprises/fastify-xstripe 1.1.1 → 1.2.1

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/LICENSE CHANGED
@@ -1,15 +1,100 @@
1
- ISC License
1
+ PROPRIETARY SOFTWARE LICENSE
2
2
 
3
- Copyright (c) 2024 Tim Mushen
3
+ Copyright (c) 2024-2026 X Enterprises LLC. All Rights Reserved.
4
4
 
5
- Permission to use, copy, modify, and/or distribute this software for any
6
- purpose with or without fee is hereby granted, provided that the above
7
- copyright notice and this permission notice appear in all copies.
5
+ This software and associated documentation files (the "Software") are the
6
+ exclusive property of X Enterprises LLC, a Washington limited liability
7
+ company ("X Enterprises"). The Software is distributed through public
8
+ package registries (including npm) for operational convenience only; such
9
+ distribution does not grant any rights beyond those expressly stated below.
8
10
 
9
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
11
+ TERMS AND CONDITIONS
12
+
13
+ 1. OWNERSHIP
14
+ All rights, title, and interest in and to the Software, including all
15
+ intellectual property rights, are and shall remain the exclusive property
16
+ of X Enterprises. No rights are granted except as expressly set forth in
17
+ this License.
18
+
19
+ 2. PERMITTED USE
20
+ Subject to the restrictions in Section 3, you are permitted to download,
21
+ install, and execute the Software solely as a dependency of:
22
+
23
+ (a) software developed, owned, or operated by X Enterprises;
24
+
25
+ (b) software that X Enterprises has developed, delivered, or licensed to
26
+ a third party ("Client") under a written engagement agreement with
27
+ X Enterprises, when such use is performed by or on behalf of that
28
+ Client; or
29
+
30
+ (c) end-user access to, or consumption of, a product or service described
31
+ in (a) or (b), provided that such access does not involve
32
+ redistribution, modification, or separate use of the Software.
33
+
34
+ Permitted Use includes automated installation and execution by continuous
35
+ integration systems, container builds, hosting platforms, and similar
36
+ infrastructure, to the extent necessary to support (a), (b), or (c).
37
+
38
+ 3. RESTRICTIONS
39
+ Except as expressly permitted in Section 2, and without the prior written
40
+ consent of X Enterprises, you may not:
41
+
42
+ (a) copy, modify, adapt, translate, or create derivative works of the
43
+ Software for any purpose outside the scope of Section 2;
44
+
45
+ (b) redistribute, republish, sublicense, sell, lease, rent, or otherwise
46
+ transfer the Software, in whole or in part, whether standalone or
47
+ bundled with other software;
48
+
49
+ (c) reverse engineer, decompile, disassemble, or attempt to derive the
50
+ source code or underlying ideas, algorithms, structure, or
51
+ organization of the Software, except to the extent such activity is
52
+ expressly permitted by applicable law notwithstanding this
53
+ restriction;
54
+
55
+ (d) use the Software, in whole or in part, to develop, operate, or
56
+ provide any product or service that competes with or substitutes for
57
+ any X Enterprises product or service;
58
+
59
+ (e) remove, obscure, or alter any copyright, trademark, license, or other
60
+ proprietary notice contained in or on the Software; or
61
+
62
+ (f) use the Software in violation of any applicable law or regulation.
63
+
64
+ 4. NO WARRANTY
65
+ THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
66
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
67
+ FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
68
+ X ENTERPRISES BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY,
69
+ WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT
70
+ OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
71
+ THE SOFTWARE.
72
+
73
+ 5. LIMITATION OF LIABILITY
74
+ IN NO EVENT SHALL X ENTERPRISES BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
75
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
76
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
77
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
78
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
79
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
80
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
81
+
82
+ 6. GOVERNING LAW
83
+ This License shall be governed by and construed in accordance with the
84
+ laws of the State of Washington, United States, without regard to its
85
+ conflict of law provisions. Exclusive jurisdiction for any dispute
86
+ arising out of this License shall lie in the state or federal courts
87
+ located in King County, Washington.
88
+
89
+ 7. TERMINATION
90
+ This License is effective until terminated. Your rights under this
91
+ License will terminate automatically and without notice if you fail to
92
+ comply with any term herein. Upon termination, you must cease all use of
93
+ the Software and destroy all copies in your possession or control.
94
+ Sections 1, 3, 4, 5, 6, and 7 survive termination.
95
+
96
+ For licensing inquiries, contact: legal@x.enterprises
97
+
98
+ ---
99
+ X Enterprises LLC
100
+ Bothell, Washington, United States
package/README.md CHANGED
@@ -1,26 +1,11 @@
1
- # xStripe
1
+ # @xenterprises/fastify-xstripe
2
2
 
3
- Fastify v5 plugin for simplified, testable Stripe webhook handling. Focuses on subscription lifecycle management with clean, readable code.
3
+ Fastify v5 plugin for Stripe webhook handling with built-in signature verification, 23 default event handlers, and the Stripe client decorated on the Fastify instance.
4
4
 
5
- ## Requirements
6
-
7
- - **Fastify v5.0.0+**
8
- - **Node.js v20+**
9
-
10
- ## Features
11
-
12
- - **Simple webhook handling** - Clean event-based architecture
13
- - **Built-in handlers** - 20+ default handlers for common events
14
- - **Easy to test** - Handlers are pure functions
15
- - **Type-safe** - Full TypeScript support
16
- - **Readable** - Clear, self-documenting code
17
- - **Flexible** - Override any default handler
18
- - **Production-ready** - Signature verification, error handling, logging
19
-
20
- ## Installation
5
+ ## Install
21
6
 
22
7
  ```bash
23
- npm install @xenterprises/fastify-xstripe fastify@5
8
+ npm install @xenterprises/fastify-xstripe stripe
24
9
  ```
25
10
 
26
11
  ## Quick Start
@@ -34,298 +19,168 @@ const fastify = Fastify({ logger: true });
34
19
  await fastify.register(xStripe, {
35
20
  apiKey: process.env.STRIPE_API_KEY,
36
21
  webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
37
- webhookPath: '/stripe/webhook', // Optional, defaults to /stripe/webhook
38
22
  });
39
23
 
24
+ // Use the Stripe client directly
25
+ const customer = await fastify.stripe.customers.create({ email: 'user@example.com' });
26
+
40
27
  await fastify.listen({ port: 3000 });
41
28
  ```
42
29
 
30
+ ## Options
31
+
32
+ | Name | Type | Default | Required | Description |
33
+ |------|------|---------|----------|-------------|
34
+ | `apiKey` | `string` | — | Yes | Stripe secret API key (`sk_test_...` or `sk_live_...`) |
35
+ | `webhookSecret` | `string` | — | Yes | Stripe webhook signing secret (`whsec_...`) |
36
+ | `webhookPath` | `string` | `"/stripe/webhook"` | No | Path where the webhook POST route is registered |
37
+ | `handlers` | `object` | `{}` | No | Custom event handlers that override the defaults |
38
+ | `apiVersion` | `string` | `"2024-11-20.acacia"` | No | Stripe API version |
39
+
40
+ All options are validated at startup. Invalid or missing required options throw with an `[xStripe]` prefix.
41
+
42
+ ## Decorated Properties
43
+
44
+ | Property | Type | Description |
45
+ |----------|------|-------------|
46
+ | `fastify.stripe` | `Stripe` | The initialized Stripe SDK client — use it to call any Stripe API |
47
+
43
48
  ## Custom Handlers
44
49
 
45
- Override default handlers with your business logic:
50
+ Override any default handler with your business logic. Handlers receive `(event, fastify, stripe)`:
46
51
 
47
52
  ```javascript
48
53
  await fastify.register(xStripe, {
49
54
  apiKey: process.env.STRIPE_API_KEY,
50
55
  webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
51
56
  handlers: {
52
- // Handle new subscription
53
57
  'customer.subscription.created': async (event, fastify, stripe) => {
54
58
  const subscription = event.data.object;
55
-
56
- // Update your database
57
- await fastify.prisma.user.update({
59
+ await db.users.update({
58
60
  where: { stripeCustomerId: subscription.customer },
59
- data: {
60
- subscriptionId: subscription.id,
61
- subscriptionStatus: subscription.status,
62
- planId: subscription.items.data[0]?.price.id,
63
- },
61
+ data: { subscriptionId: subscription.id, status: subscription.status },
64
62
  });
65
-
66
- // Send welcome email
67
- await fastify.email.send(
68
- subscription.customer.email,
69
- 'Welcome to Premium!',
70
- '<h1>Thanks for subscribing!</h1>'
71
- );
72
63
  },
73
-
74
- // Handle subscription cancellation
75
- 'customer.subscription.deleted': async (event, fastify, stripe) => {
76
- const subscription = event.data.object;
77
-
78
- // Revoke access
79
- await fastify.prisma.user.update({
80
- where: { stripeSubscriptionId: subscription.id },
81
- data: {
82
- subscriptionStatus: 'canceled',
83
- hasAccess: false,
84
- },
85
- });
86
- },
87
-
88
- // Handle failed payment
89
64
  'invoice.payment_failed': async (event, fastify, stripe) => {
90
65
  const invoice = event.data.object;
91
-
92
- // Send payment failure email
93
66
  const customer = await stripe.customers.retrieve(invoice.customer);
94
- await fastify.email.send(
95
- customer.email,
96
- 'Payment Failed',
97
- '<p>Please update your payment method.</p>'
98
- );
67
+ await sendEmail(customer.email, 'Payment Failed', 'Please update your card.');
99
68
  },
100
69
  },
101
70
  });
102
71
  ```
103
72
 
104
- ## Handler Function Signature
105
-
106
- All handlers receive three parameters:
107
-
108
- ```javascript
109
- async function handler(event, fastify, stripe) {
110
- // event - The Stripe webhook event object
111
- // fastify - The Fastify instance (access to decorators)
112
- // stripe - The Stripe client instance
113
- }
114
- ```
73
+ ## Default Event Handlers
115
74
 
116
- ## Supported Events
75
+ All 23 built-in handlers log structured data via `fastify.log`. Override any of them via the `handlers` option.
117
76
 
118
77
  ### Subscription Events
119
- - `customer.subscription.created` - New subscription
120
- - `customer.subscription.updated` - Subscription changed
121
- - `customer.subscription.deleted` - Subscription canceled
122
- - `customer.subscription.trial_will_end` - Trial ending in 3 days
123
- - `customer.subscription.paused` - Subscription paused
124
- - `customer.subscription.resumed` - Subscription resumed
78
+ - `customer.subscription.created` logs subscriptionId, customerId, status, planId
79
+ - `customer.subscription.updated` logs subscriptionId, customerId, status, previous changes
80
+ - `customer.subscription.deleted` logs subscriptionId, customerId, canceledAt
81
+ - `customer.subscription.trial_will_end` logs subscriptionId, customerId, trialEnd
82
+ - `customer.subscription.paused` logs subscriptionId, customerId
83
+ - `customer.subscription.resumed` logs subscriptionId, customerId
125
84
 
126
85
  ### Invoice Events
127
- - `invoice.created` - Invoice created
128
- - `invoice.finalized` - Invoice ready for payment
129
- - `invoice.paid` - Payment succeeded
130
- - `invoice.payment_failed` - Payment failed
131
- - `invoice.upcoming` - Upcoming charge notification
86
+ - `invoice.created` logs invoiceId, customerId, amount, status
87
+ - `invoice.finalized` logs invoiceId, customerId, amount
88
+ - `invoice.paid` logs invoiceId, customerId, subscriptionId, amount
89
+ - `invoice.payment_failed` logs (warn) invoiceId, customerId, amount, attemptCount
90
+ - `invoice.upcoming` logs customerId, subscriptionId, amount, periodEnd
132
91
 
133
92
  ### Payment Events
134
- - `payment_intent.succeeded` - Payment successful
135
- - `payment_intent.payment_failed` - Payment failed
93
+ - `payment_intent.succeeded` logs paymentIntentId, customerId, amount, currency
94
+ - `payment_intent.payment_failed` logs (warn) paymentIntentId, customerId, amount, lastPaymentError
136
95
 
137
96
  ### Customer Events
138
- - `customer.created` - New customer
139
- - `customer.updated` - Customer details changed
140
- - `customer.deleted` - Customer deleted
97
+ - `customer.created` logs customerId, email
98
+ - `customer.updated` logs customerId, previous changes
99
+ - `customer.deleted` logs customerId
141
100
 
142
101
  ### Payment Method Events
143
- - `payment_method.attached` - Payment method added
144
- - `payment_method.detached` - Payment method removed
102
+ - `payment_method.attached` logs paymentMethodId, customerId, type
103
+ - `payment_method.detached` logs paymentMethodId, type
145
104
 
146
105
  ### Checkout Events
147
- - `checkout.session.completed` - Checkout completed
148
- - `checkout.session.expired` - Checkout session expired
106
+ - `checkout.session.completed` logs sessionId, customerId, subscriptionId, mode, paymentStatus
107
+ - `checkout.session.expired` logs sessionId
149
108
 
150
- ## Testing Webhooks Locally
109
+ ### Charge Events
110
+ - `charge.succeeded` — logs chargeId, customerId, amount, currency, paymentMethod
111
+ - `charge.failed` — logs (error) chargeId, customerId, amount, failureCode, failureMessage
112
+ - `charge.refunded` — logs chargeId, customerId, amountRefunded, refundCount
151
113
 
152
- ### 1. Use Stripe CLI
114
+ ## Helper Utilities
153
115
 
154
- ```bash
155
- # Install Stripe CLI
156
- brew install stripe/stripe-cli/stripe
157
-
158
- # Login to Stripe
159
- stripe login
160
-
161
- # Forward webhooks to your local server
162
- stripe listen --forward-to localhost:3000/stripe/webhook
163
-
164
- # Trigger test events
165
- stripe trigger customer.subscription.created
166
- stripe trigger invoice.payment_failed
167
- ```
168
-
169
- ### 2. Test Handlers Directly
170
-
171
- Since handlers are pure functions, they're easy to test:
172
-
173
- ```javascript
174
- import { test } from 'node:test';
175
- import assert from 'node:assert';
176
-
177
- test('subscription.created handler', async () => {
178
- const mockEvent = {
179
- type: 'customer.subscription.created',
180
- data: {
181
- object: {
182
- id: 'sub_123',
183
- customer: 'cus_123',
184
- status: 'active',
185
- },
186
- },
187
- };
188
-
189
- const mockFastify = {
190
- log: { info: () => {} },
191
- prisma: {
192
- user: {
193
- update: async (data) => {
194
- assert.equal(data.where.stripeCustomerId, 'cus_123');
195
- return {};
196
- },
197
- },
198
- },
199
- };
200
-
201
- const mockStripe = {};
202
-
203
- await handlers['customer.subscription.created'](
204
- mockEvent,
205
- mockFastify,
206
- mockStripe
207
- );
208
- });
209
- ```
210
-
211
- ## Common Patterns
212
-
213
- ### Update Database on Subscription Change
214
-
215
- ```javascript
216
- 'customer.subscription.updated': async (event, fastify, stripe) => {
217
- const subscription = event.data.object;
218
- const previous = event.data.previous_attributes || {};
219
-
220
- // Check what changed
221
- if ('status' in previous) {
222
- await fastify.prisma.user.update({
223
- where: { stripeSubscriptionId: subscription.id },
224
- data: { subscriptionStatus: subscription.status },
225
- });
226
-
227
- // Handle specific status changes
228
- if (subscription.status === 'past_due') {
229
- // Send payment reminder
230
- }
231
- }
232
- }
233
- ```
234
-
235
- ### Send Notification Emails
116
+ Import from `@xenterprises/fastify-xstripe/helpers`:
236
117
 
237
118
  ```javascript
238
- 'customer.subscription.trial_will_end': async (event, fastify, stripe) => {
239
- const subscription = event.data.object;
240
- const customer = await stripe.customers.retrieve(subscription.customer);
241
-
242
- await fastify.email.send(
243
- customer.email,
244
- 'Your trial ends soon!',
245
- '<p>Convert to a paid plan to keep access.</p>'
246
- );
247
- }
119
+ import { helpers } from '@xenterprises/fastify-xstripe';
120
+
121
+ helpers.formatAmount(2000, 'USD'); // "$20.00"
122
+ helpers.getPlanName(subscription); // "Pro Plan"
123
+ helpers.isActiveSubscription(subscription); // true
124
+ helpers.isInTrial(subscription); // true/false
125
+ helpers.getDaysUntilTrialEnd(subscription); // 3
126
+ helpers.isRenewal(event); // true/false
127
+ helpers.calculateMRR(subscription); // 2000 (cents)
128
+ helpers.getSubscriptionStatusText('active'); // "Active"
129
+ helpers.getEventDescription(event); // "Payment received"
130
+ helpers.getCustomerEmail(event, stripe); // "user@example.com"
131
+ helpers.isTestEvent(event); // true/false
132
+ helpers.getMetadata(event); // { key: "value" }
133
+ helpers.getPaymentMethodType(pm); // "Card"
134
+ helpers.getInvoiceLineItems(invoice); // [{ description, amount, ... }]
135
+ helpers.isSubscriptionInvoice(invoice); // true/false
136
+ helpers.getNextBillingDate(subscription); // Date
137
+ helpers.formatDate(1700000000); // "November 14, 2023"
248
138
  ```
249
139
 
250
- ### Track Failed Payments
251
-
252
- ```javascript
253
- 'invoice.payment_failed': async (event, fastify, stripe) => {
254
- const invoice = event.data.object;
255
-
256
- await fastify.prisma.user.update({
257
- where: { stripeSubscriptionId: invoice.subscription },
258
- data: {
259
- failedPaymentCount: { increment: 1 },
260
- lastFailedPayment: new Date(),
261
- },
262
- });
263
-
264
- // After 3 failed payments, suspend account
265
- const user = await fastify.prisma.user.findUnique({
266
- where: { stripeSubscriptionId: invoice.subscription },
267
- });
268
-
269
- if (user.failedPaymentCount >= 3) {
270
- await fastify.prisma.user.update({
271
- where: { id: user.id },
272
- data: { accountSuspended: true },
273
- });
274
- }
275
- }
276
- ```
140
+ ## Environment Variables
277
141
 
278
- ## Configuration Options
142
+ | Name | Required | Description |
143
+ |------|----------|-------------|
144
+ | `STRIPE_API_KEY` | Yes | Stripe secret key (`sk_test_...` or `sk_live_...`) |
145
+ | `STRIPE_WEBHOOK_SECRET` | Yes | Webhook signing secret from Stripe Dashboard or CLI (`whsec_...`) |
279
146
 
280
- | Option | Type | Required | Default | Description |
281
- |--------|------|----------|---------|-------------|
282
- | `apiKey` | string | Yes | - | Stripe API key |
283
- | `webhookSecret` | string | No | - | Stripe webhook signing secret |
284
- | `webhookPath` | string | No | `/stripe/webhook` | Webhook endpoint path |
285
- | `handlers` | object | No | `{}` | Custom event handlers |
286
- | `apiVersion` | string | No | `2024-11-20.acacia` | Stripe API version |
147
+ ## Error Reference
287
148
 
288
- ## Security
149
+ All errors use the `[xStripe]` prefix for easy identification in logs.
289
150
 
290
- - **Signature Verification**: All webhooks are verified using Stripe's signature
291
- - **Raw Body Required**: Plugin automatically handles raw body parsing
292
- - **Error Isolation**: Handler errors don't prevent webhook acknowledgment
293
- - **Logging**: All events and errors are logged
151
+ | Error | When |
152
+ |-------|------|
153
+ | `[xStripe] apiKey is required and must be a string` | Missing or non-string `apiKey` option |
154
+ | `[xStripe] webhookSecret is required and must be a string` | Missing or non-string `webhookSecret` option |
155
+ | `[xStripe] webhookPath must be a string` | Non-string `webhookPath` option |
156
+ | `[xStripe] handlers must be a plain object` | `handlers` is not an object or is an array |
157
+ | `[xStripe] apiVersion must be a string` | Non-string `apiVersion` option |
158
+ | `[xStripe] Missing stripe-signature header` | Webhook request without signature header (HTTP 400) |
159
+ | `[xStripe] Webhook signature verification failed: ...` | Invalid webhook signature (HTTP 400) |
294
160
 
295
- ## Best Practices
161
+ ## How It Works
296
162
 
297
- 1. **Always acknowledge webhooks quickly** - Do heavy processing async
298
- 2. **Make handlers idempotent** - Stripe may send events multiple times
299
- 3. **Log everything** - Use structured logging for debugging
300
- 4. **Test with Stripe CLI** - Test all event types before production
301
- 5. **Monitor failed handlers** - Set up alerts for handler errors
163
+ 1. **Registration** Validates all options, initializes the Stripe SDK client, and decorates it as `fastify.stripe`.
164
+ 2. **Webhook Route** Registers a POST route at `webhookPath` that reads the raw body, verifies the Stripe signature using `stripe.webhooks.constructEvent()`, and dispatches to the matching handler.
165
+ 3. **Handler Dispatch** — User-provided handlers override defaults via object spread (`{ ...defaultHandlers, ...userHandlers }`). If a handler throws, the error is logged but the webhook still returns HTTP 200 to prevent Stripe retries.
166
+ 4. **Stripe Client** — The `fastify.stripe` decorator gives full access to the Stripe SDK for any API call (customers, subscriptions, invoices, etc.).
302
167
 
303
- ## Environment Variables
168
+ ## Testing Webhooks Locally
304
169
 
305
170
  ```bash
306
- STRIPE_API_KEY=sk_test_...
307
- STRIPE_WEBHOOK_SECRET=whsec_...
308
- ```
309
-
310
- ## Integration with Other xPlugins
171
+ # Install Stripe CLI
172
+ brew install stripe/stripe-cli/stripe
311
173
 
312
- Works seamlessly with other x-series plugins:
174
+ # Login and forward webhooks
175
+ stripe login
176
+ stripe listen --forward-to localhost:3000/stripe/webhook
313
177
 
314
- ```javascript
315
- await fastify.register(xStripe, { /* ... */ });
316
- await fastify.register(xTwilio, { /* ... */ }); // Send SMS notifications
317
- await fastify.register(xConfig, { /* ... */ }); // Email notifications
318
-
319
- // In your handlers:
320
- 'invoice.payment_failed': async (event, fastify, stripe) => {
321
- // Send email via xConfig/SendGrid
322
- await fastify.email.send(/* ... */);
323
-
324
- // Send SMS via xTwilio
325
- await fastify.sms.send(/* ... */);
326
- }
178
+ # Trigger test events
179
+ stripe trigger customer.subscription.created
180
+ stripe trigger invoice.payment_failed
181
+ stripe trigger checkout.session.completed
327
182
  ```
328
183
 
329
184
  ## License
330
185
 
331
- ISC
186
+ UNLICENSED
package/index.d.ts CHANGED
@@ -3,10 +3,10 @@
3
3
  * TypeScript Type Definitions
4
4
  *
5
5
  * @module @xenterprises/fastify-xstripe
6
- * @version 1.0.0
6
+ * @version 1.2.0
7
7
  */
8
8
 
9
- import { FastifyPluginAsync, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
9
+ import { FastifyPluginAsync, FastifyInstance } from 'fastify';
10
10
  import Stripe from 'stripe';
11
11
 
12
12
  /**
@@ -16,6 +16,10 @@ export type StripeWebhookEvent = Stripe.Event;
16
16
 
17
17
  /**
18
18
  * Webhook Event Handler Function
19
+ *
20
+ * @param event - The Stripe webhook event object
21
+ * @param fastify - The Fastify instance (access to decorators)
22
+ * @param stripe - The Stripe client instance
19
23
  */
20
24
  export type WebhookEventHandler = (
21
25
  event: StripeWebhookEvent,
@@ -72,94 +76,20 @@ export interface DefaultHandlers {
72
76
  * Plugin Configuration Options
73
77
  */
74
78
  export interface XStripePluginOptions {
75
- /** Stripe API Key (Secret Key) */
76
- apiKey?: string;
79
+ /** Stripe API Key (Secret Key). Required. */
80
+ apiKey: string;
77
81
 
78
- /** Stripe Webhook Signing Secret */
79
- webhookSecret?: string;
82
+ /** Stripe Webhook Signing Secret. Required. */
83
+ webhookSecret: string;
80
84
 
81
- /** Webhook endpoint path */
85
+ /** Webhook endpoint path. Defaults to "/stripe/webhook". */
82
86
  webhookPath?: string;
83
87
 
84
- /** Custom event handlers (override defaults) */
88
+ /** Custom event handlers (override defaults). */
85
89
  handlers?: Partial<DefaultHandlers>;
86
90
 
87
- /** Whether to fail on handler errors */
88
- failOnError?: boolean;
89
-
90
- /** Request timeout in milliseconds */
91
- requestTimeout?: number;
92
-
93
- /** Enable event logging */
94
- logEvents?: boolean;
95
- }
96
-
97
- /**
98
- * Helper Functions
99
- */
100
- export namespace helpers {
101
- /**
102
- * Format amount as currency string
103
- * @param amount Amount in cents
104
- * @param currency Currency code (USD, EUR, etc.)
105
- * @returns Formatted currency string (e.g., "$20.00")
106
- */
107
- function formatAmount(amount: number, currency: string): string;
108
-
109
- /**
110
- * Get plan name from subscription
111
- * @param subscription Subscription object
112
- * @returns Plan name or product name
113
- */
114
- function getPlanName(subscription: Stripe.Subscription): string;
115
-
116
- /**
117
- * Check if subscription is active
118
- * @param subscription Subscription object
119
- * @returns True if subscription is active
120
- */
121
- function isActiveSubscription(subscription: Stripe.Subscription): boolean;
122
-
123
- /**
124
- * Get customer email from various objects
125
- * @param obj Stripe object with customer reference
126
- * @param stripe Stripe client instance
127
- * @returns Customer email
128
- */
129
- function getCustomerEmail(obj: any, stripe: Stripe): Promise<string>;
130
-
131
- /**
132
- * Create idempotent request ID from event
133
- * @param event Stripe webhook event
134
- * @returns Idempotency key
135
- */
136
- function createIdempotencyKey(event: StripeWebhookEvent): string;
137
- }
138
-
139
- /**
140
- * xStripe Service Methods (available on fastify.xStripe)
141
- */
142
- export interface XStripeService {
143
- /**
144
- * Register custom event handler
145
- * @param eventType Stripe event type
146
- * @param handler Event handler function
147
- */
148
- onEvent(eventType: string, handler: WebhookEventHandler): void;
149
-
150
- /**
151
- * Get registered handler for event type
152
- * @param eventType Stripe event type
153
- * @returns Handler function or undefined
154
- */
155
- getHandler(eventType: string): WebhookEventHandler | undefined;
156
-
157
- /**
158
- * Execute event handler
159
- * @param event Stripe webhook event
160
- * @returns Promise that resolves when handler completes
161
- */
162
- executeHandler(event: StripeWebhookEvent): Promise<void>;
91
+ /** Stripe API version. Defaults to "2024-11-20.acacia". */
92
+ apiVersion?: string;
163
93
  }
164
94
 
165
95
  /**
@@ -167,101 +97,77 @@ export interface XStripeService {
167
97
  */
168
98
  declare module 'fastify' {
169
99
  interface FastifyInstance {
170
- /** xStripe service methods */
171
- xStripe: XStripeService;
172
-
173
100
  /** Stripe client instance */
174
101
  stripe: Stripe;
175
102
  }
176
103
  }
177
104
 
178
105
  /**
179
- * Webhook Request with Stripe Event
106
+ * Helper Functions
180
107
  */
181
- export interface WebhookRequest extends FastifyRequest {
182
- body: {
183
- /** Stripe event object */
184
- id: string;
185
- type: string;
186
- data: any;
187
- [key: string]: any;
188
- };
189
- }
108
+ export declare namespace helpers {
109
+ /** Extract customer email from various Stripe objects */
110
+ function getCustomerEmail(event: StripeWebhookEvent, stripe: Stripe): string | Promise<string | null> | null;
190
111
 
191
- /**
192
- * Webhook Response
193
- */
194
- export interface WebhookResponse {
195
- /** Status message */
196
- status: 'success' | 'error';
112
+ /** Format amount from cents to currency string */
113
+ function formatAmount(cents: number, currency?: string): string;
114
+
115
+ /** Get plan name from subscription */
116
+ function getPlanName(subscription: Stripe.Subscription): string;
197
117
 
198
- /** Event ID that was processed */
199
- eventId: string;
118
+ /** Check if subscription is in trial */
119
+ function isInTrial(subscription: Stripe.Subscription): boolean;
200
120
 
201
- /** Event type that was processed */
202
- eventType: string;
121
+ /** Check if subscription is active (including trialing) */
122
+ function isActiveSubscription(subscription: Stripe.Subscription): boolean;
203
123
 
204
- /** Optional error message */
205
- error?: string;
124
+ /** Get days until trial ends */
125
+ function getDaysUntilTrialEnd(subscription: Stripe.Subscription): number | null;
206
126
 
207
- /** Timestamp of processing */
208
- processedAt: string;
209
- }
127
+ /** Check if event is a subscription renewal */
128
+ function isRenewal(event: StripeWebhookEvent): boolean;
210
129
 
211
- /**
212
- * API Response Types
213
- */
214
- export interface Plan {
215
- productId: string;
216
- name: string;
217
- description?: string;
218
- images?: string[];
219
- prices: PriceInfo[];
220
- metadata?: Record<string, any>;
221
- }
130
+ /** Get subscription status display text */
131
+ function getSubscriptionStatusText(status: string): string;
222
132
 
223
- export interface PriceInfo {
224
- priceId: string;
225
- amount: number;
226
- currency: string;
227
- interval?: string;
228
- intervalCount?: number;
229
- trialPeriodDays?: number;
230
- nickname?: string;
231
- metadata?: Record<string, any>;
232
- }
133
+ /** Extract metadata from event */
134
+ function getMetadata(event: StripeWebhookEvent): Record<string, string>;
233
135
 
234
- export interface PaymentMethod {
235
- id: string;
236
- type: string;
237
- isDefault: boolean;
238
- card?: {
239
- brand: string;
240
- last4: string;
241
- expMonth: number;
242
- expYear: number;
243
- };
244
- billingDetails?: Record<string, any>;
245
- createdAt: Date;
246
- }
136
+ /** Check if event is a test event */
137
+ function isTestEvent(event: StripeWebhookEvent): boolean;
138
+
139
+ /** Get human-readable event description */
140
+ function getEventDescription(event: StripeWebhookEvent): string;
141
+
142
+ /** Calculate MRR from subscription (in cents) */
143
+ function calculateMRR(subscription: Stripe.Subscription): number;
144
+
145
+ /** Get payment method type display text */
146
+ function getPaymentMethodType(paymentMethod: Stripe.PaymentMethod | null): string;
247
147
 
248
- export interface SubscriptionInfo {
249
- id: string;
250
- status: string;
251
- planId?: string;
252
- planName?: string;
253
- amount?: number;
254
- currency?: string;
255
- interval?: string;
256
- createdAt: Date;
257
- currentPeriodEnd: Date;
258
- cancelAtPeriodEnd: boolean;
259
- canceledAt?: Date;
148
+ /** Extract line items from invoice */
149
+ function getInvoiceLineItems(invoice: Stripe.Invoice): Array<{
150
+ description: string | null;
151
+ amount: number;
152
+ quantity: number | null;
153
+ priceId: string | undefined;
154
+ }>;
155
+
156
+ /** Check if invoice is for subscription vs one-time payment */
157
+ function isSubscriptionInvoice(invoice: Stripe.Invoice): boolean;
158
+
159
+ /** Get next billing date from subscription */
160
+ function getNextBillingDate(subscription: Stripe.Subscription): Date | null;
161
+
162
+ /** Format date from Unix timestamp */
163
+ function formatDate(unixTimestamp: number | null, locale?: string): string | null;
260
164
  }
261
165
 
262
166
  /**
263
167
  * xStripe Plugin
264
- * Registers Stripe webhook handling and provides convenient API endpoints
168
+ *
169
+ * Registers Stripe webhook handling with signature verification and
170
+ * decorates the Fastify instance with a Stripe client.
265
171
  *
266
172
  * @example
267
173
  * ```typescript
@@ -273,37 +179,25 @@ export interface SubscriptionInfo {
273
179
  * await fastify.register(xStripe, {
274
180
  * apiKey: process.env.STRIPE_API_KEY,
275
181
  * webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
276
- * webhookPath: '/webhooks/stripe'
277
- * });
278
- *
279
- * // Register custom handler
280
- * fastify.xStripe.onEvent('customer.subscription.created', async (event, fastify, stripe) => {
281
- * const subscription = event.data.object;
282
- * console.log('New subscription:', subscription.id);
182
+ * webhookPath: '/stripe/webhook',
183
+ * handlers: {
184
+ * 'customer.subscription.created': async (event, fastify, stripe) => {
185
+ * const subscription = event.data.object;
186
+ * console.log('New subscription:', subscription.id);
187
+ * },
188
+ * },
283
189
  * });
284
- *
285
- * // Webhook route automatically registered at webhookPath
286
- * // POST /webhooks/stripe
287
- *
288
- * // Use included API endpoints
289
- * // GET /plans
290
- * // GET /plans/:productId
291
- * // POST /create-checkout-session
292
- * // POST /create-payment-session
293
- * // GET /customer/:customerId/subscriptions
294
- * // POST /subscription/:id/update
295
- * // GET /customer/:customerId/payment-methods
296
- * // POST /customer/:customerId/payment-methods
297
- * // POST /customer/:customerId/payment-methods/:paymentMethodId/default
298
- * // DELETE /customer/:customerId/payment-methods/:paymentMethodId
299
190
  * ```
300
191
  */
301
192
  declare const xStripe: FastifyPluginAsync<XStripePluginOptions>;
302
193
 
303
194
  export default xStripe;
195
+ export { xStripe };
304
196
 
305
- /**
306
- * Re-export Stripe types for convenience
307
- */
197
+ /** Re-export for convenience */
308
198
  export { Stripe };
309
199
  export type { StripeWebhookEvent as WebhookEvent };
200
+
201
+ /** Default handlers */
202
+ export { defaultHandlers } from './src/handlers/defaultHandlers.js';
203
+ export { exampleHandlers } from './src/handlers/exampleHandlers.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xenterprises/fastify-xstripe",
3
3
  "type": "module",
4
- "version": "1.1.1",
4
+ "version": "1.2.1",
5
5
  "description": "Fastify plugin for Stripe webhooks with simplified, testable handlers for subscription events.",
6
6
  "main": "src/index.js",
7
7
  "exports": {
@@ -12,10 +12,10 @@
12
12
  "scripts": {
13
13
  "start": "fastify start -l info server/app.js",
14
14
  "dev": "fastify start -w -l info -P server/app.js",
15
- "test": "node --test test/handlers.test.js"
15
+ "test": "node --test test/handlers.test.js test/xStripe.integration.test.js"
16
16
  },
17
17
  "author": "Tim Mushen",
18
- "license": "ISC",
18
+ "license": "SEE LICENSE IN LICENSE",
19
19
  "engines": {
20
20
  "node": ">=20.0.0",
21
21
  "npm": ">=10.0.0"
@@ -40,7 +40,6 @@
40
40
  "devDependencies": {
41
41
  "@types/node": "^22.7.4",
42
42
  "fastify": "^5.1.0",
43
- "fastify-plugin": "^5.0.0",
44
43
  "typescript": "^5.6.3"
45
44
  },
46
45
  "dependencies": {
@@ -49,5 +48,8 @@
49
48
  },
50
49
  "peerDependencies": {
51
50
  "fastify": "^5.0.0"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
52
54
  }
53
55
  }
@@ -20,8 +20,8 @@ export async function setupWebhooks(fastify, options) {
20
20
  const sig = request.headers["stripe-signature"];
21
21
 
22
22
  if (!sig) {
23
- fastify.log.error("Missing stripe-signature header");
24
- return reply.code(400).send({ error: "Missing stripe-signature header" });
23
+ fastify.log.error("[xStripe] Missing stripe-signature header");
24
+ return reply.code(400).send({ error: "[xStripe] Missing stripe-signature header" });
25
25
  }
26
26
 
27
27
  let event;
@@ -33,12 +33,12 @@ export async function setupWebhooks(fastify, options) {
33
33
  // Verify webhook signature
34
34
  event = stripe.webhooks.constructEvent(rawBody, sig, webhookSecret);
35
35
  } catch (err) {
36
- fastify.log.error(`Webhook signature verification failed: ${err.message}`);
37
- return reply.code(400).send({ error: `Webhook Error: ${err.message}` });
36
+ fastify.log.error(`[xStripe] Webhook signature verification failed: ${err.message}`);
37
+ return reply.code(400).send({ error: `[xStripe] Webhook signature verification failed: ${err.message}` });
38
38
  }
39
39
 
40
40
  // Log the event
41
- fastify.log.info(`Received Stripe webhook: ${event.type}`);
41
+ fastify.log.info(`[xStripe] Received webhook: ${event.type}`);
42
42
 
43
43
  // Get handler for this event type
44
44
  const handler = eventHandlers[event.type];
@@ -47,9 +47,9 @@ export async function setupWebhooks(fastify, options) {
47
47
  try {
48
48
  // Execute handler
49
49
  await handler(event, fastify, stripe);
50
- fastify.log.info(`Successfully processed ${event.type}`);
50
+ fastify.log.info(`[xStripe] Successfully processed ${event.type}`);
51
51
  } catch (err) {
52
- fastify.log.error(`Error processing ${event.type}: ${err.message}`);
52
+ fastify.log.error(`[xStripe] Error processing ${event.type}: ${err.message}`);
53
53
  // Return 200 to acknowledge receipt, even if processing failed
54
54
  // This prevents Stripe from retrying immediately
55
55
  return reply.code(200).send({
@@ -59,7 +59,7 @@ export async function setupWebhooks(fastify, options) {
59
59
  });
60
60
  }
61
61
  } else {
62
- fastify.log.warn(`No handler registered for event type: ${event.type}`);
62
+ fastify.log.warn(`[xStripe] No handler registered for event type: ${event.type}`);
63
63
  }
64
64
 
65
65
  // Always return 200 to acknowledge receipt
@@ -67,6 +67,6 @@ export async function setupWebhooks(fastify, options) {
67
67
  }
68
68
  );
69
69
 
70
- console.info(` Stripe Webhooks Enabled at ${webhookPath}`);
71
- console.info(` 📋 Registered ${Object.keys(eventHandlers).length} event handlers`);
70
+ fastify.log.info(`[xStripe] Webhooks enabled at ${webhookPath}`);
71
+ fastify.log.info(`[xStripe] Registered ${Object.keys(eventHandlers).length} event handlers`);
72
72
  }
package/src/xStripe.js CHANGED
@@ -13,11 +13,27 @@ async function xStripe(fastify, options) {
13
13
  } = options;
14
14
 
15
15
  // Validate required options
16
- if (!apiKey) {
17
- throw new Error("Stripe apiKey is required");
16
+ if (!apiKey || typeof apiKey !== "string") {
17
+ throw new Error("[xStripe] apiKey is required and must be a string");
18
18
  }
19
19
 
20
- console.info("\n 💳 Starting xStripe...\n");
20
+ if (!webhookSecret || typeof webhookSecret !== "string") {
21
+ throw new Error("[xStripe] webhookSecret is required and must be a string");
22
+ }
23
+
24
+ if (typeof webhookPath !== "string") {
25
+ throw new Error("[xStripe] webhookPath must be a string");
26
+ }
27
+
28
+ if (typeof handlers !== "object" || Array.isArray(handlers)) {
29
+ throw new Error("[xStripe] handlers must be a plain object");
30
+ }
31
+
32
+ if (typeof apiVersion !== "string") {
33
+ throw new Error("[xStripe] apiVersion must be a string");
34
+ }
35
+
36
+ fastify.log.info("[xStripe] Initializing Stripe plugin...");
21
37
 
22
38
  // Initialize Stripe client
23
39
  const stripe = new Stripe(apiKey, { apiVersion });
@@ -25,21 +41,18 @@ async function xStripe(fastify, options) {
25
41
  // Decorate Fastify with Stripe client
26
42
  fastify.decorate("stripe", stripe);
27
43
 
28
- // Setup webhook handling if webhook secret is provided
29
- if (webhookSecret) {
30
- await setupWebhooks(fastify, {
31
- stripe,
32
- webhookSecret,
33
- webhookPath,
34
- handlers,
35
- });
36
- } else {
37
- fastify.log.warn("⚠️ Stripe webhook secret not provided. Webhook handling disabled.");
38
- }
44
+ // Setup webhook handling
45
+ await setupWebhooks(fastify, {
46
+ stripe,
47
+ webhookSecret,
48
+ webhookPath,
49
+ handlers,
50
+ });
39
51
 
40
- console.info("\n 💳 xStripe Ready!\n");
52
+ fastify.log.info("[xStripe] Ready");
41
53
  }
42
54
 
43
55
  export default fp(xStripe, {
44
56
  name: "xStripe",
57
+ fastify: ">=5.0.0",
45
58
  });
package/.dockerignore DELETED
@@ -1,62 +0,0 @@
1
- # Git
2
- .git
3
- .gitignore
4
- .gitattributes
5
-
6
- # Node modules (will be installed in container)
7
- node_modules
8
- npm-debug.log*
9
- yarn-debug.log*
10
- yarn-error.log*
11
- lerna-debug.log*
12
-
13
- # Dependencies
14
- .npm
15
- .eslintcache
16
- *.tsbuildinfo
17
-
18
- # Temporary files
19
- .DS_Store
20
- Thumbs.db
21
- *.tmp
22
- .env.local
23
- .env.*.local
24
-
25
- # IDE
26
- .vscode
27
- .idea
28
- *.swp
29
- *.swo
30
- *.swn
31
- *~
32
-
33
- # Test & Coverage
34
- coverage
35
- .nyc_output
36
- test
37
- *.test.js
38
-
39
- # Build artifacts (if any)
40
- dist
41
- build
42
- out
43
-
44
- # Docker files (not needed in image)
45
- Dockerfile*
46
- docker-compose*.yml
47
- .dockerignore
48
-
49
- # CI/CD
50
- .github
51
- .gitlab-ci.yml
52
-
53
- # Documentation (optional - include if you want docs in image)
54
- # *.md
55
-
56
- # Example/demo files
57
- examples
58
- demo
59
-
60
- # Logs
61
- logs
62
- *.log
package/.env.example DELETED
@@ -1,116 +0,0 @@
1
- # ============================================================================
2
- # xStripe Fastify Plugin - Environment Configuration
3
- # ============================================================================
4
- # This file defines environment variables for the xStripe module.
5
- # Copy this to .env and update values for your environment.
6
- # ============================================================================
7
-
8
- # ============================================================================
9
- # SERVER CONFIGURATION
10
- # ============================================================================
11
-
12
- # Server port (default: 3000)
13
- PORT=3000
14
-
15
- # Node environment (development | production | test)
16
- NODE_ENV=development
17
-
18
- # Server hostname/domain (used in webhook URLs and responses)
19
- # Update this to your actual domain in production
20
- DOMAIN=http://localhost:3000
21
-
22
- # ============================================================================
23
- # STRIPE CONFIGURATION - REQUIRED
24
- # ============================================================================
25
-
26
- # Stripe API Key (Secret Key)
27
- # Get from: https://dashboard.stripe.com/apikeys
28
- # Format: sk_test_... (test mode) or sk_live_... (production)
29
- # NEVER commit this value - use environment variables only
30
- STRIPE_API_KEY=sk_test_your_key_here
31
-
32
- # Stripe Webhook Signing Secret
33
- # Get from: https://dashboard.stripe.com/webhooks
34
- # Copy the "Signing secret" for your webhook endpoint
35
- # Format: whsec_...
36
- STRIPE_WEBHOOK_SECRET=whsec_your_secret_here
37
-
38
- # ============================================================================
39
- # WEBHOOK CONFIGURATION
40
- # ============================================================================
41
-
42
- # Webhook endpoint path (relative to domain)
43
- # Default: /webhooks/stripe
44
- # STRIPE_WEBHOOK_PATH=/webhooks/stripe
45
-
46
- # Webhook events to listen for (space or comma-separated)
47
- # Default: all supported events
48
- # Examples:
49
- # customer.created,customer.updated,customer.deleted
50
- # invoice.created,invoice.finalized,invoice.paid
51
- # charge.succeeded,charge.failed
52
- # STRIPE_WEBHOOK_EVENTS=*
53
-
54
- # ============================================================================
55
- # LOGGING & MONITORING
56
- # ============================================================================
57
-
58
- # Fastify logger level (trace, debug, info, warn, error, fatal)
59
- LOG_LEVEL=info
60
-
61
- # Enable webhook event logging
62
- # LOG_WEBHOOK_EVENTS=true
63
-
64
- # Enable handler execution logging
65
- # LOG_HANDLER_EXECUTION=true
66
-
67
- # ============================================================================
68
- # ERROR HANDLING & RECOVERY
69
- # ============================================================================
70
-
71
- # Retry failed webhook handler executions
72
- # ENABLE_HANDLER_RETRY=false
73
-
74
- # Max retry attempts for failed handlers
75
- # HANDLER_RETRY_MAX_ATTEMPTS=3
76
-
77
- # Retry delay in milliseconds
78
- # HANDLER_RETRY_DELAY_MS=5000
79
-
80
- # ============================================================================
81
- # SECURITY
82
- # ============================================================================
83
-
84
- # Enable HTTPS enforcement (if behind reverse proxy)
85
- # HTTPS_ONLY=false
86
-
87
- # Rate limiting: max webhook requests per minute
88
- # WEBHOOK_RATE_LIMIT_WINDOW=1m
89
- # WEBHOOK_RATE_LIMIT_MAX=1000
90
-
91
- # Timeout for webhook handler execution (milliseconds)
92
- # Default: 30000 (30 seconds)
93
- # WEBHOOK_HANDLER_TIMEOUT=30000
94
-
95
- # ============================================================================
96
- # STRIPE ACCOUNT CONFIGURATION
97
- # ============================================================================
98
-
99
- # Stripe API Version (optional - uses account default if not set)
100
- # Format: YYYY-MM-DD or leave empty
101
- # STRIPE_API_VERSION=
102
-
103
- # Enable Stripe CLI webhooks in development
104
- # Set to true if using: stripe listen --forward-to localhost:3000/webhooks/stripe
105
- # STRIPE_CLI_MODE=false
106
-
107
- # ============================================================================
108
- # DATABASE/STORAGE (optional - for persisting event data)
109
- # ============================================================================
110
-
111
- # Database URL for event persistence (optional)
112
- # Example: postgresql://user:pass@localhost/stripe_events
113
- # DATABASE_URL=
114
-
115
- # Enable event persistence
116
- # PERSIST_WEBHOOK_EVENTS=false
package/.gitlab-ci.yml DELETED
@@ -1,45 +0,0 @@
1
- # ============================================================================
2
- # GitLab CI/CD Pipeline - xStripe
3
- # ============================================================================
4
- # Runs tests on merge requests and commits to main/master
5
-
6
- stages:
7
- - test
8
-
9
- variables:
10
- NODE_ENV: test
11
-
12
- # ============================================================================
13
- # Shared Configuration
14
- # ============================================================================
15
- .shared_rules: &shared_rules
16
- rules:
17
- - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
18
- - if: '$CI_COMMIT_BRANCH == "main"'
19
- - if: '$CI_COMMIT_BRANCH == "master"'
20
- - if: '$CI_COMMIT_TAG'
21
-
22
- # ============================================================================
23
- # STAGE: TEST
24
- # ============================================================================
25
- test:
26
- stage: test
27
- image: node:20-alpine
28
- <<: *shared_rules
29
-
30
- cache:
31
- key: ${CI_COMMIT_REF_SLUG}
32
- paths:
33
- - node_modules/
34
-
35
- before_script:
36
- - npm ci
37
-
38
- script:
39
- - echo "Running xStripe tests..."
40
- - npm test
41
- - npm audit --audit-level=high || true
42
-
43
- retry:
44
- max: 2
45
- when: runner_system_failure