@frigear-nu/stripe 0.0.0-stage → 0.0.2

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 CHANGED
@@ -1,3 +1,76 @@
1
- # Temporary Holding Version
1
+ # @frigear-nu/stripe
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Core Stripe plumbing for the frigear ecosystem. It owns the SDK, the user <-> Stripe customer link and a single
4
+ verified, idempotent webhook that other modules (subscriptions, payments, ...) subscribe to. It contains no product
5
+ logic itself.
6
+
7
+ ## Setup
8
+
9
+ ```ts
10
+ export default defineNuxtConfig({
11
+ modules: ['@nuxthub/core', '@frigear-nu/users', '@frigear-nu/stripe'],
12
+ hub: { db: { dialect: 'sqlite', driver: 'libsql' }, kv: true }, // kv is required (webhook idempotency)
13
+ stripe: { settings: { webhookPath: '/api/stripe/webhook' } }, // optional, this is the default
14
+ })
15
+ ```
16
+
17
+ `unstorage` must be resolvable by the host app (NuxtHub KV needs it).
18
+
19
+ Environment variables:
20
+
21
+ | Variable | Purpose |
22
+ | ------------------------------------ | ----------------------------------------------- |
23
+ | `NUXT_STRIPE_SECRET_KEY` | Stripe secret key (`runtimeConfig.stripe.secretKey`) |
24
+ | `NUXT_STRIPE_WEBHOOK_SECRET` | Webhook signing secret (`whsec_...`) |
25
+ | `NUXT_STRIPE_API_VERSION` | Optional override, defaults to the pinned `2026-09-30.endive` |
26
+ | `NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Publishable key (`runtimeConfig.public.stripe.publishableKey`) |
27
+
28
+ ## Database
29
+
30
+ Extends the `users` table with a nullable `stripe_customer_id` column. Generate and commit migrations in the host app.
31
+
32
+ ## Server utils (auto-imported in Nitro)
33
+
34
+ - `useStripe()` - the Stripe SDK, using the fetch HTTP client (Cloudflare Workers compatible).
35
+ - `ensureStripeCustomer(userId)` - returns the user's Stripe customer id: stored id, else a Stripe search by email, else a new customer (persisted).
36
+ - `getStripeCustomerUser(customerId)` - the local user row for a Stripe customer id, or `null`.
37
+
38
+ ## Webhook
39
+
40
+ `POST /api/stripe/webhook` reads the raw body, verifies the signature (`constructEventAsync` + SubtleCrypto), and
41
+ dispatches to handlers. Processed event ids are stored in NuxtHub KV for 7 days, so duplicates are ignored. If any
42
+ handler throws, the others still run, the response is 500 (Stripe retries) and the event is not marked processed -
43
+ **handlers must be idempotent**. The Nitro hook `stripe:event` is fired for every dispatched event.
44
+
45
+ ## Subscribing to events
46
+
47
+ From a module (build time, bundled into the server - no runtime scanning):
48
+
49
+ ```ts
50
+ import { registerStripeEventHandler } from '@frigear-nu/stripe/utils'
51
+
52
+ registerStripeEventHandler(nuxt, {
53
+ events: ['customer.subscription.updated', 'checkout.session.completed'], // or '*'
54
+ handler: resolver.resolve('./runtime/server/stripe/subscription-events'),
55
+ order: 100, // optional, lower runs first
56
+ })
57
+ ```
58
+
59
+ ```ts
60
+ // runtime/server/stripe/subscription-events.ts
61
+ import { defineStripeEventHandler } from '@frigear-nu/stripe/utils'
62
+
63
+ export default defineStripeEventHandler<'customer.subscription.updated' | 'checkout.session.completed'>(
64
+ async (event, { stripe, userId, event: h3Event }) => {
65
+ // `event` is narrowed by `event.type`; `userId` is null for unknown customers
66
+ },
67
+ )
68
+ ```
69
+
70
+ From a host app's Nitro plugin:
71
+
72
+ ```ts
73
+ export default defineNitroPlugin(() => {
74
+ onStripeEvent('invoice.payment_failed', (event, ctx) => { /* ... */ })
75
+ })
76
+ ```
@@ -0,0 +1,74 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import Stripe from 'stripe';
3
+ import { H3Event } from 'h3';
4
+
5
+ type ModuleOptions = {
6
+ settings?: {
7
+ /** Path the Stripe webhook is served on. Defaults to `/api/stripe/webhook`. */
8
+ webhookPath?: string;
9
+ };
10
+ };
11
+ /** Private runtime config (`useRuntimeConfig().stripe`), set through `NUXT_STRIPE_*` env vars. */
12
+ type StripeRuntimeConfig = {
13
+ secretKey: string;
14
+ webhookSecret: string;
15
+ apiVersion: string;
16
+ };
17
+ /** Public runtime config (`useRuntimeConfig().public.stripe`). */
18
+ type StripePublicRuntimeConfig = {
19
+ publishableKey: string;
20
+ };
21
+ /** Every Stripe event type string, e.g. `'customer.subscription.updated'`. */
22
+ type StripeEventType = Stripe.Event['type'];
23
+ /** The `Stripe.Event` for a given event type (or union of event types). */
24
+ type StripeEventOf<T extends StripeEventType = StripeEventType> = Extract<Stripe.Event, {
25
+ type: T;
26
+ }>;
27
+ type StripeEventHandlerContext = {
28
+ /** A ready-to-use Stripe SDK instance (see `useStripe`). */
29
+ stripe: Stripe;
30
+ /** The local user owning the event's Stripe customer, or `null` if there is none / it is unknown. */
31
+ userId: number | null;
32
+ /** The Nitro request event of the webhook call. */
33
+ event: H3Event;
34
+ };
35
+ type StripeEventHandler<T extends StripeEventType = StripeEventType> = (event: StripeEventOf<T>, ctx: StripeEventHandlerContext) => void | Promise<void>;
36
+ /** A handler registered at build time through `registerStripeEventHandler`. */
37
+ type RegisteredStripeEventHandler = {
38
+ /** Event types to subscribe to, or `'*'` for every event. */
39
+ events: string[] | '*';
40
+ /** Absolute path to a file whose default export is a `defineStripeEventHandler(...)` result. */
41
+ handler: string;
42
+ /** Lower runs first. Defaults to `100`. */
43
+ order?: number;
44
+ };
45
+
46
+ declare module '@nuxt/schema' {
47
+ interface NuxtConfig {
48
+ stripe?: ModuleOptions;
49
+ }
50
+ interface NuxtOptions {
51
+ stripe?: ModuleOptions & {
52
+ _handlers?: RegisteredStripeEventHandler[];
53
+ };
54
+ }
55
+ interface RuntimeConfig {
56
+ stripe: StripeRuntimeConfig;
57
+ }
58
+ interface PublicRuntimeConfig {
59
+ stripe: StripePublicRuntimeConfig;
60
+ }
61
+ }
62
+ declare module 'nitropack' {
63
+ interface NitroRuntimeHooks {
64
+ /** Fired for every verified, not-yet-processed Stripe webhook event, once handlers have been dispatched. */
65
+ 'stripe:event': (event: Stripe.Event, ctx: StripeEventHandlerContext) => void | Promise<void>;
66
+ }
67
+ }
68
+ /** The Stripe API version this package is written against (matches the pinned `stripe` SDK). */
69
+ declare const STRIPE_API_VERSION = "2026-09-30.endive";
70
+ declare const DEFAULT_WEBHOOK_PATH = "/api/stripe/webhook";
71
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
72
+
73
+ export { DEFAULT_WEBHOOK_PATH, STRIPE_API_VERSION, _default as default };
74
+ export type { ModuleOptions, RegisteredStripeEventHandler, StripeEventHandler, StripeEventHandlerContext, StripeEventOf, StripeEventType, StripePublicRuntimeConfig, StripeRuntimeConfig };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@frigear-nu/stripe",
3
+ "configKey": "stripe",
4
+ "version": "0.0.2",
5
+ "builder": {
6
+ "@nuxt/module-builder": "1.0.3",
7
+ "unbuild": "unknown"
8
+ }
9
+ }
@@ -0,0 +1,80 @@
1
+ import { defineNuxtModule, createResolver, addServerHandler, addServerImportsDir, addServerImports, addTypeTemplate, addServerTemplate } from '@nuxt/kit';
2
+ import { registerModule, extendDatabaseTable } from '@frigear-nu/module-kit/utils';
3
+
4
+ const STRIPE_API_VERSION = "2026-09-30.endive";
5
+ const DEFAULT_WEBHOOK_PATH = "/api/stripe/webhook";
6
+ const DEFAULT_HANDLER_ORDER = 100;
7
+ const module$1 = defineNuxtModule({
8
+ meta: { name: "@frigear-nu/stripe", configKey: "stripe" },
9
+ defaults: {
10
+ settings: { webhookPath: DEFAULT_WEBHOOK_PATH }
11
+ },
12
+ moduleDependencies: {
13
+ "@nuxthub/core": {
14
+ version: ">=0.10",
15
+ // The webhook stores processed event ids in NuxtHub KV.
16
+ defaults: { kv: true }
17
+ },
18
+ "@frigear-nu/module-kit": {
19
+ version: ">=0.0.1"
20
+ },
21
+ "@frigear-nu/users": {
22
+ version: ">=0.0.1"
23
+ }
24
+ },
25
+ async setup(options, nuxt) {
26
+ registerModule("@frigear-nu/stripe", nuxt);
27
+ const resolver = createResolver(import.meta.url);
28
+ const webhookPath = options.settings?.webhookPath || DEFAULT_WEBHOOK_PATH;
29
+ if (!nuxt.options.hub?.kv) {
30
+ throw new Error("[@frigear-nu/stripe] NuxtHub KV is required for webhook idempotency. Set `hub: { kv: true }` in your Nuxt config.");
31
+ }
32
+ const runtimeConfig = nuxt.options.runtimeConfig;
33
+ runtimeConfig.stripe = {
34
+ secretKey: "",
35
+ webhookSecret: "",
36
+ apiVersion: STRIPE_API_VERSION,
37
+ ...runtimeConfig.stripe
38
+ };
39
+ runtimeConfig.public.stripe = {
40
+ publishableKey: "",
41
+ ...runtimeConfig.public.stripe
42
+ };
43
+ addServerHandler({
44
+ route: webhookPath,
45
+ method: "post",
46
+ handler: resolver.resolve("./runtime/server/routes/webhook.post")
47
+ });
48
+ addServerImportsDir(resolver.resolve("./runtime/server/utils"));
49
+ addServerImports([
50
+ { name: "onStripeEvent", from: resolver.resolve("./runtime/utils/registry") },
51
+ { name: "defineStripeEventHandler", from: resolver.resolve("./runtime/utils/handlers") }
52
+ ]);
53
+ addTypeTemplate({
54
+ filename: "types/stripe-handlers.d.ts",
55
+ getContents: () => `declare module '#stripe/handlers' {
56
+ import type { StripeEventHandler } from ${JSON.stringify(resolver.resolve("./types"))}
57
+ export const handlers: Array<{ events: string[] | '*', order?: number, handler: StripeEventHandler }>
58
+ }
59
+ `
60
+ });
61
+ nuxt.hook("modules:done", () => {
62
+ const registered = Array.isArray(nuxt.options.stripe?._handlers) ? nuxt.options.stripe._handlers : [];
63
+ addServerTemplate({
64
+ filename: "#stripe/handlers",
65
+ getContents: () => `
66
+ ${registered.map((entry, index) => `import handler${index} from ${JSON.stringify(entry.handler)}`).join("\n")}
67
+ export const handlers = [${registered.map(
68
+ (entry, index) => `{ events: ${JSON.stringify(entry.events)}, order: ${entry.order ?? DEFAULT_HANDLER_ORDER}, handler: handler${index} }`
69
+ ).join(", ")}]
70
+ `
71
+ });
72
+ });
73
+ extendDatabaseTable(nuxt, {
74
+ resolver,
75
+ file: (dialect) => `./runtime/server/db/schema.${dialect}`
76
+ });
77
+ }
78
+ });
79
+
80
+ export { DEFAULT_WEBHOOK_PATH, STRIPE_API_VERSION, module$1 as default };
@@ -0,0 +1,26 @@
1
+ export declare const users: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2
+ name: "users";
3
+ schema: undefined;
4
+ columns: {
5
+ stripeCustomerId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
6
+ name: "stripe_customer_id";
7
+ tableName: "users";
8
+ dataType: "string";
9
+ columnType: "SQLiteText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: false;
13
+ hasDefault: false;
14
+ isPrimaryKey: false;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {
22
+ length: number | undefined;
23
+ }>;
24
+ };
25
+ dialect: "sqlite";
26
+ }>;
@@ -0,0 +1,4 @@
1
+ import { sqliteTable, text } from "drizzle-orm/sqlite-core";
2
+ export const users = sqliteTable("users", {
3
+ stripeCustomerId: text("stripe_customer_id")
4
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,82 @@
1
+ import Stripe from "stripe";
2
+ import { kv } from "@nuxthub/kv";
3
+ import { handlers as registeredHandlers } from "#stripe/handlers";
4
+ import { getRuntimeStripeEventHandlers } from "../../utils/registry.js";
5
+ const PROCESSED_TTL_SECONDS = 60 * 60 * 24 * 7;
6
+ const DEFAULT_ORDER = 100;
7
+ const matches = (events, type) => events === "*" || events.includes(type);
8
+ function collectHandlers(type) {
9
+ const all = [
10
+ ...registeredHandlers.map((entry) => ({ events: entry.events, order: entry.order ?? DEFAULT_ORDER, handler: entry.handler })),
11
+ ...getRuntimeStripeEventHandlers().map((entry) => ({ events: entry.events, order: DEFAULT_ORDER, handler: entry.handler }))
12
+ ];
13
+ return all.filter((entry) => matches(entry.events, type)).sort((a, b) => a.order - b.order).map((entry) => entry.handler);
14
+ }
15
+ function getEventCustomerId(stripeEvent) {
16
+ const object = stripeEvent.data.object;
17
+ if (!object) return null;
18
+ if (object.object === "customer" && typeof object.id === "string") return object.id;
19
+ const customer = object.customer;
20
+ if (typeof customer === "string") return customer;
21
+ if (customer && typeof customer === "object" && typeof customer.id === "string") {
22
+ return customer.id;
23
+ }
24
+ return null;
25
+ }
26
+ export default defineEventHandler(async (event) => {
27
+ const { webhookSecret } = useRuntimeConfig(event).stripe;
28
+ if (!webhookSecret) {
29
+ throw createError({ statusCode: 500, statusMessage: "Stripe webhook is not configured (missing NUXT_STRIPE_WEBHOOK_SECRET)." });
30
+ }
31
+ const signature = getHeader(event, "stripe-signature");
32
+ if (!signature) {
33
+ throw createError({ statusCode: 400, statusMessage: "Missing stripe-signature header." });
34
+ }
35
+ const rawBody = await readRawBody(event, "utf8");
36
+ if (!rawBody) {
37
+ throw createError({ statusCode: 400, statusMessage: "Missing request body." });
38
+ }
39
+ let stripeEvent;
40
+ try {
41
+ stripeEvent = await Stripe.webhooks.constructEventAsync(
42
+ rawBody,
43
+ signature,
44
+ webhookSecret,
45
+ void 0,
46
+ Stripe.createSubtleCryptoProvider()
47
+ );
48
+ } catch (error) {
49
+ console.warn("[@frigear-nu/stripe] Rejected webhook with an invalid signature:", error instanceof Error ? error.message : error);
50
+ throw createError({ statusCode: 400, statusMessage: "Invalid Stripe signature." });
51
+ }
52
+ const processedKey = `stripe:event:${stripeEvent.id}`;
53
+ if (await kv.has(processedKey)) {
54
+ return { received: true, duplicate: true };
55
+ }
56
+ const customerId = getEventCustomerId(stripeEvent);
57
+ const user = customerId ? await getStripeCustomerUser(customerId) : null;
58
+ const context = {
59
+ stripe: useStripe(),
60
+ userId: user?.id ?? null,
61
+ event
62
+ };
63
+ let failures = 0;
64
+ for (const handler of collectHandlers(stripeEvent.type)) {
65
+ try {
66
+ await handler(stripeEvent, context);
67
+ } catch (error) {
68
+ failures++;
69
+ console.error(`[@frigear-nu/stripe] Handler failed for "${stripeEvent.type}" (${stripeEvent.id}):`, error);
70
+ }
71
+ }
72
+ try {
73
+ await useNitroApp().hooks.callHook("stripe:event", stripeEvent, context);
74
+ } catch (error) {
75
+ console.error(`[@frigear-nu/stripe] "stripe:event" hook failed for ${stripeEvent.id}:`, error);
76
+ }
77
+ if (failures > 0) {
78
+ throw createError({ statusCode: 500, statusMessage: `${failures} Stripe event handler(s) failed.` });
79
+ }
80
+ await kv.set(processedKey, Date.now(), { ttl: PROCESSED_TTL_SECONDS });
81
+ return { received: true };
82
+ });
@@ -0,0 +1,14 @@
1
+ import Stripe from 'stripe';
2
+ /**
3
+ * The shared Stripe SDK instance, configured from `runtimeConfig.stripe`. It uses the fetch HTTP
4
+ * client, so it works on Cloudflare Workers as well as Node.
5
+ */
6
+ export declare function useStripe(): Stripe;
7
+ /**
8
+ * Returns the Stripe customer id for a user, creating (and persisting) one when needed. Lookup
9
+ * order: `users.stripeCustomerId`, then a Stripe customer search by email, then a new customer.
10
+ * Safe to call lazily from consumers - there is no login coupling.
11
+ */
12
+ export declare function ensureStripeCustomer(userId: number): Promise<string>;
13
+ /** Resolves the local user owning a Stripe customer id, or `null` if none is linked. */
14
+ export declare function getStripeCustomerUser(customerId: string): Promise<any>;
@@ -0,0 +1,47 @@
1
+ import Stripe from "stripe";
2
+ import { db, schema } from "@nuxthub/db";
3
+ import { and, eq, isNull } from "drizzle-orm";
4
+ let cached;
5
+ export function useStripe() {
6
+ const { secretKey, apiVersion } = useRuntimeConfig().stripe;
7
+ if (!secretKey) {
8
+ throw createError({ statusCode: 500, statusMessage: "Stripe is not configured (missing `runtimeConfig.stripe.secretKey` / NUXT_STRIPE_SECRET_KEY)." });
9
+ }
10
+ if (!cached || cached.key !== secretKey || cached.apiVersion !== apiVersion) {
11
+ cached = {
12
+ key: secretKey,
13
+ apiVersion,
14
+ client: new Stripe(secretKey, {
15
+ apiVersion,
16
+ httpClient: Stripe.createFetchHttpClient()
17
+ })
18
+ };
19
+ }
20
+ return cached.client;
21
+ }
22
+ const escapeSearchValue = (value) => value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
23
+ export async function ensureStripeCustomer(userId) {
24
+ const [user] = await db.select().from(schema.users).where(eq(schema.users.id, userId)).limit(1);
25
+ if (!user) {
26
+ throw createError({ statusCode: 404, statusMessage: "User not found." });
27
+ }
28
+ if (user.stripeCustomerId) return user.stripeCustomerId;
29
+ const stripe = useStripe();
30
+ const existing = await stripe.customers.search({
31
+ query: `email:'${escapeSearchValue(user.email)}'`,
32
+ limit: 1
33
+ });
34
+ const customerId = existing.data[0]?.id ?? (await stripe.customers.create({
35
+ email: user.email,
36
+ name: user.name,
37
+ metadata: { userId: String(user.id) }
38
+ })).id;
39
+ const [updated] = await db.update(schema.users).set({ stripeCustomerId: customerId }).where(and(eq(schema.users.id, userId), isNull(schema.users.stripeCustomerId))).returning({ stripeCustomerId: schema.users.stripeCustomerId });
40
+ if (updated?.stripeCustomerId) return updated.stripeCustomerId;
41
+ const [winner] = await db.select({ stripeCustomerId: schema.users.stripeCustomerId }).from(schema.users).where(eq(schema.users.id, userId)).limit(1);
42
+ return winner?.stripeCustomerId ?? customerId;
43
+ }
44
+ export async function getStripeCustomerUser(customerId) {
45
+ const [user] = await db.select().from(schema.users).where(eq(schema.users.stripeCustomerId, customerId)).limit(1);
46
+ return user ?? null;
47
+ }
@@ -0,0 +1,32 @@
1
+ import type { Nuxt } from '@nuxt/schema';
2
+ import type { RegisteredStripeEventHandler, StripeEventHandler, StripeEventType } from '../../types.js';
3
+ export type RegisterStripeEventHandlerOptions = RegisteredStripeEventHandler;
4
+ /**
5
+ * A typed identity helper for authoring a Stripe event handler file. The event is narrowed to the
6
+ * event type(s) given as the generic; the default export of the file is the handler.
7
+ *
8
+ * ```ts
9
+ * export default defineStripeEventHandler<'customer.subscription.updated' | 'customer.subscription.deleted'>(
10
+ * async (event, { stripe, userId }) => {
11
+ * const subscription = event.data.object
12
+ * },
13
+ * )
14
+ * ```
15
+ *
16
+ * Handlers must be idempotent: if any handler for an event throws, the webhook answers 5xx and
17
+ * Stripe retries the whole event.
18
+ */
19
+ export declare function defineStripeEventHandler<T extends StripeEventType = StripeEventType>(handler: StripeEventHandler<T>): StripeEventHandler;
20
+ /**
21
+ * Build-time API for any module (or host app module) to subscribe to Stripe webhook events. Call it
22
+ * during `setup()`; registration order between modules doesn't matter, since handlers are only
23
+ * bundled once every module has finished setting up. `handler` must be an absolute path.
24
+ *
25
+ * ```ts
26
+ * registerStripeEventHandler(nuxt, {
27
+ * events: ['customer.subscription.updated', 'checkout.session.completed'],
28
+ * handler: resolver.resolve('./runtime/server/stripe/subscription-events'),
29
+ * })
30
+ * ```
31
+ */
32
+ export declare function registerStripeEventHandler(nuxt: Nuxt, options: RegisterStripeEventHandlerOptions): void;
@@ -0,0 +1,15 @@
1
+ export function defineStripeEventHandler(handler) {
2
+ return handler;
3
+ }
4
+ export function registerStripeEventHandler(nuxt, options) {
5
+ nuxt.options.stripe ||= {};
6
+ const handlers = Array.isArray(nuxt.options.stripe._handlers) ? nuxt.options.stripe._handlers : [];
7
+ nuxt.options.stripe._handlers = handlers;
8
+ const events = options.events === "*" ? "*" : [...options.events];
9
+ const exists = handlers.some(
10
+ (existing) => existing.handler === options.handler && JSON.stringify(existing.events) === JSON.stringify(events)
11
+ );
12
+ if (!exists) {
13
+ handlers.push({ events, handler: options.handler, order: options.order });
14
+ }
15
+ }
@@ -0,0 +1,4 @@
1
+ export { registerStripeEventHandler, defineStripeEventHandler } from './handlers.js';
2
+ export type { RegisterStripeEventHandlerOptions } from './handlers.js';
3
+ export { onStripeEvent } from './registry.js';
4
+ export type { ModuleOptions, StripeEventType, StripeEventOf, StripeEventHandler, StripeEventHandlerContext, RegisteredStripeEventHandler, StripeRuntimeConfig, StripePublicRuntimeConfig, } from '../../types.js';
@@ -0,0 +1,2 @@
1
+ export { registerStripeEventHandler, defineStripeEventHandler } from "./handlers.js";
2
+ export { onStripeEvent } from "./registry.js";
@@ -0,0 +1,18 @@
1
+ import type { StripeEventHandler, StripeEventType } from '../../types.js';
2
+ export type RuntimeStripeEventHandler = {
3
+ events: string[] | '*';
4
+ handler: StripeEventHandler;
5
+ };
6
+ /**
7
+ * Runtime counterpart of `registerStripeEventHandler`, for Nitro plugins in host apps:
8
+ *
9
+ * ```ts
10
+ * // server/plugins/stripe.ts
11
+ * export default defineNitroPlugin(() => {
12
+ * onStripeEvent('invoice.payment_failed', (event, ctx) => { ... })
13
+ * })
14
+ * ```
15
+ */
16
+ export declare function onStripeEvent<T extends StripeEventType>(type: T | T[] | '*', handler: StripeEventHandler<T>): void;
17
+ /** Handlers registered through `onStripeEvent`, in registration order. Internal: used by the webhook route. */
18
+ export declare function getRuntimeStripeEventHandlers(): readonly RuntimeStripeEventHandler[];
@@ -0,0 +1,10 @@
1
+ const handlers = [];
2
+ export function onStripeEvent(type, handler) {
3
+ handlers.push({
4
+ events: type === "*" ? "*" : Array.isArray(type) ? type : [type],
5
+ handler
6
+ });
7
+ }
8
+ export function getRuntimeStripeEventHandlers() {
9
+ return handlers;
10
+ }
@@ -0,0 +1,3 @@
1
+ export { type DEFAULT_WEBHOOK_PATH, type STRIPE_API_VERSION, default } from './module.mjs'
2
+
3
+ export { type ModuleOptions, type RegisteredStripeEventHandler, type StripeEventHandler, type StripeEventHandlerContext, type StripeEventOf, type StripeEventType, type StripePublicRuntimeConfig, type StripeRuntimeConfig } from './module.mjs'
package/package.json CHANGED
@@ -1,6 +1,81 @@
1
1
  {
2
2
  "name": "@frigear-nu/stripe",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.2",
4
+ "description": "Core Stripe plumbing for the frigear ecosystem: SDK access, customer linking, and a verified, idempotent webhook that other modules can subscribe to.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/frigear-nu/frigear-nu.git",
8
+ "directory": "packages/stripe"
9
+ },
10
+ "license": "MIT",
11
+ "type": "module",
12
+ "main": "./dist/module.mjs",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/types.d.mts",
16
+ "import": "./dist/module.mjs",
17
+ "default": "./dist/module.mjs"
18
+ },
19
+ "./utils": {
20
+ "types": "./dist/runtime/utils/index.d.ts",
21
+ "import": "./dist/runtime/utils/index.js",
22
+ "default": "./dist/runtime/utils/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "workspaces": [
29
+ "playground"
30
+ ],
31
+ "dependencies": {
32
+ "@frigear-nu/module-kit": "0.1.0",
33
+ "@frigear-nu/users": "1.0.2",
34
+ "@libsql/client": "^0.18.0",
35
+ "@nuxt/kit": "^4.6.0",
36
+ "h3": "^1.15.11",
37
+ "drizzle-kit": "^0.31.11",
38
+ "drizzle-orm": "^0.45.3",
39
+ "stripe": "^23.0.0",
40
+ "unstorage": "^1.17.5",
41
+ "zod": "^4.6.5"
42
+ },
43
+ "peerDependencies": {
44
+ "@nuxthub/core": "^0.10.8"
45
+ },
46
+ "devDependencies": {
47
+ "@nuxt/devtools": "^3.4.2",
48
+ "@nuxt/eslint-config": "^1.17.0",
49
+ "@nuxt/module-builder": "^1.0.3",
50
+ "@nuxt/schema": "^4.6.0",
51
+ "@nuxt/test-utils": "^4.3.2",
52
+ "@nuxthub/core": "^0.10.8",
53
+ "@types/node": "latest",
54
+ "changelogen": "^0.6.2",
55
+ "eslint": "^10.10.0",
56
+ "nuxt": "^4.6.0",
57
+ "typescript": "^6.0.3",
58
+ "vitest": "^5.0.1",
59
+ "vue-tsc": "^3.3.11"
60
+ },
61
+ "scripts": {
62
+ "postinstall": "nuxt-module-build build --stub && nuxt prepare",
63
+ "build": "nuxt-module-build build",
64
+ "dev": "npm run dev:prepare && nuxt dev playground",
65
+ "dev:build": "nuxt build playground",
66
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
67
+ "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
68
+ "lint": "eslint .",
69
+ "lint:fix": "eslint . --fix",
70
+ "test": "vitest run",
71
+ "test:watch": "vitest watch",
72
+ "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
73
+ },
74
+ "typesVersions": {
75
+ "*": {
76
+ ".": [
77
+ "./dist/types.d.mts"
78
+ ]
79
+ }
80
+ }
6
81
  }