@kamod-ch/otok-stripe 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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/actions.d.ts +8 -0
  4. package/dist/actions.d.ts.map +1 -0
  5. package/dist/actions.js +19 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/adapter/index.d.ts +2 -0
  8. package/dist/adapter/index.d.ts.map +1 -0
  9. package/dist/adapter/index.js +2 -0
  10. package/dist/adapter/index.js.map +1 -0
  11. package/dist/adapter/types.d.ts +11 -0
  12. package/dist/adapter/types.d.ts.map +1 -0
  13. package/dist/adapter/types.js +2 -0
  14. package/dist/adapter/types.js.map +1 -0
  15. package/dist/checkout.d.ts +4 -0
  16. package/dist/checkout.d.ts.map +1 -0
  17. package/dist/checkout.js +45 -0
  18. package/dist/checkout.js.map +1 -0
  19. package/dist/client.d.ts +4 -0
  20. package/dist/client.d.ts.map +1 -0
  21. package/dist/client.js +8 -0
  22. package/dist/client.js.map +1 -0
  23. package/dist/dto.d.ts +25 -0
  24. package/dist/dto.d.ts.map +1 -0
  25. package/dist/dto.js +21 -0
  26. package/dist/dto.js.map +1 -0
  27. package/dist/errors.d.ts +11 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +21 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/factory.d.ts +3 -0
  32. package/dist/factory.d.ts.map +1 -0
  33. package/dist/factory.js +16 -0
  34. package/dist/factory.js.map +1 -0
  35. package/dist/idempotency.d.ts +17 -0
  36. package/dist/idempotency.d.ts.map +1 -0
  37. package/dist/idempotency.js +41 -0
  38. package/dist/idempotency.js.map +1 -0
  39. package/dist/index.d.ts +15 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +11 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/plugin.d.ts +14 -0
  44. package/dist/plugin.d.ts.map +1 -0
  45. package/dist/plugin.js +90 -0
  46. package/dist/plugin.js.map +1 -0
  47. package/dist/portal.d.ts +10 -0
  48. package/dist/portal.d.ts.map +1 -0
  49. package/dist/portal.js +15 -0
  50. package/dist/portal.js.map +1 -0
  51. package/dist/provider/types.d.ts +38 -0
  52. package/dist/provider/types.d.ts.map +1 -0
  53. package/dist/provider/types.js +2 -0
  54. package/dist/provider/types.js.map +1 -0
  55. package/dist/providers/live.d.ts +4 -0
  56. package/dist/providers/live.d.ts.map +1 -0
  57. package/dist/providers/live.js +29 -0
  58. package/dist/providers/live.js.map +1 -0
  59. package/dist/providers/test.d.ts +13 -0
  60. package/dist/providers/test.d.ts.map +1 -0
  61. package/dist/providers/test.js +70 -0
  62. package/dist/providers/test.js.map +1 -0
  63. package/dist/registry.d.ts +7 -0
  64. package/dist/registry.d.ts.map +1 -0
  65. package/dist/registry.js +18 -0
  66. package/dist/registry.js.map +1 -0
  67. package/dist/types.d.ts +34 -0
  68. package/dist/types.d.ts.map +1 -0
  69. package/dist/types.js +2 -0
  70. package/dist/types.js.map +1 -0
  71. package/dist/webhook/events.d.ts +8 -0
  72. package/dist/webhook/events.d.ts.map +1 -0
  73. package/dist/webhook/events.js +80 -0
  74. package/dist/webhook/events.js.map +1 -0
  75. package/dist/webhook/index.d.ts +3 -0
  76. package/dist/webhook/index.d.ts.map +1 -0
  77. package/dist/webhook/index.js +3 -0
  78. package/dist/webhook/index.js.map +1 -0
  79. package/dist/webhook/middleware.d.ts +26 -0
  80. package/dist/webhook/middleware.d.ts.map +1 -0
  81. package/dist/webhook/middleware.js +31 -0
  82. package/dist/webhook/middleware.js.map +1 -0
  83. package/package.json +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kamod
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @kamod-ch/otok-stripe
2
+
3
+ Stripe Checkout, Customer Portal, and webhooks for [Otok](https://github.com/kamod-ch/otok) apps.
4
+
5
+ Provider-based integration with a typed Otok plugin, safe client DTOs, idempotent webhook processing, and a test provider for local development.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @kamod-ch/otok-stripe stripe hono @kamod-ch/otok
11
+ ```
12
+
13
+ ## Plugin
14
+
15
+ ```ts
16
+ import { defineConfig } from "@kamod-ch/otok";
17
+ import stripe from "@kamod-ch/otok-stripe";
18
+
19
+ export default defineConfig({
20
+ plugins: [
21
+ stripe({
22
+ provider: { type: "live" },
23
+ webhookPath: "/api/billing/webhook",
24
+ }),
25
+ ],
26
+ });
27
+ ```
28
+
29
+ Use `{ type: "test" }` in development and tests — no Stripe API calls.
30
+
31
+ ## Typed actions (safe client payloads)
32
+
33
+ ```ts
34
+ import { stripeCheckoutAction, stripePortalAction, stripeBillingStatusAction } from "@kamod-ch/otok-stripe";
35
+
36
+ // Returns { sessionId, url } — never raw Stripe objects
37
+ const checkout = await stripeCheckoutAction({
38
+ plan: "launch",
39
+ priceId: process.env.STRIPE_PRICE_LAUNCH!,
40
+ workspaceId,
41
+ userId,
42
+ successUrl: `${appUrl}/billing?success=1`,
43
+ cancelUrl: `${appUrl}/billing?cancelled=1`,
44
+ });
45
+
46
+ const status = await stripeBillingStatusAction(adapter, workspaceId, "active");
47
+ ```
48
+
49
+ ## Billing adapter
50
+
51
+ Apps own persistence via `BillingAdapter`:
52
+
53
+ ```ts
54
+ import type { BillingAdapter } from "@kamod-ch/otok-stripe/adapter";
55
+ ```
56
+
57
+ ## Webhooks with idempotency
58
+
59
+ Mount via plugin + adapter in `configureStripeApp`, or use the low-level handler from `@kamod-ch/otok-stripe/webhook`.
60
+
61
+ Plugin webhooks deduplicate by Stripe event ID — retries return `{ received: true, duplicate: true }` without double-processing.
62
+
63
+ ## Providers
64
+
65
+ | Provider | Config | Notes |
66
+ | -------- | ------------------------------ | ----------------------------------------------------------------- |
67
+ | `live` | `{ type: "live", secretKey? }` | Real Stripe SDK. Uses `STRIPE_SECRET_KEY` when omitted. |
68
+ | `test` | `{ type: "test" }` | Deterministic checkout/portal URLs and webhook parsing for tests. |
69
+
70
+ ## Composition API (still supported)
71
+
72
+ Direct helpers remain available without the plugin:
73
+
74
+ - `createStripeClient`
75
+ - `createCheckoutSession`
76
+ - `createBillingPortalSession`
77
+ - `createStripeWebhookHandler`
78
+
79
+ ## Env vars
80
+
81
+ ```
82
+ STRIPE_SECRET_KEY=
83
+ STRIPE_WEBHOOK_SECRET=
84
+ ```
85
+
86
+ ## Exports
87
+
88
+ | Subpath | Purpose |
89
+ | -------------------------------------- | -------------------------------- |
90
+ | `@kamod-ch/otok-stripe` | Plugin, actions, DTOs, providers |
91
+ | `@kamod-ch/otok-stripe/adapter` | `BillingAdapter` contract |
92
+ | `@kamod-ch/otok-stripe/webhook` | Low-level webhook handler |
93
+ | `@kamod-ch/otok-stripe/providers/test` | Test provider helpers |
@@ -0,0 +1,8 @@
1
+ import type { BillingAdapter } from "./adapter/types.js";
2
+ import type { BillingPortalSessionInput } from "./portal.js";
3
+ import type { CheckoutSessionInput } from "./types.js";
4
+ import { type BillingPortalSessionDto, type BillingStatusDto, type CheckoutSessionDto, type SubscriptionStatus } from "./dto.js";
5
+ export declare function stripeCheckoutAction<TPlan extends string>(input: CheckoutSessionInput<TPlan>): Promise<CheckoutSessionDto>;
6
+ export declare function stripePortalAction(input: BillingPortalSessionInput): Promise<BillingPortalSessionDto>;
7
+ export declare function stripeBillingStatusAction<TPlan extends string>(adapter: BillingAdapter<TPlan>, workspaceId: string, stripeStatus?: SubscriptionStatus | string | null): Promise<BillingStatusDto<TPlan> | null>;
8
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAIL,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAIlB,wBAAsB,oBAAoB,CAAC,KAAK,SAAS,MAAM,EAC7D,KAAK,EAAE,oBAAoB,CAAC,KAAK,CAAC,GACjC,OAAO,CAAC,kBAAkB,CAAC,CAI7B;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAI3G;AAED,wBAAsB,yBAAyB,CAAC,KAAK,SAAS,MAAM,EAClE,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,EAC9B,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,GAChD,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAIzC"}
@@ -0,0 +1,19 @@
1
+ import { toBillingPortalSessionDto, toBillingStatusDto, toCheckoutSessionDto, } from "./dto.js";
2
+ import { getStripeProvider } from "./registry.js";
3
+ import { resolveSubscriptionStatus } from "./idempotency.js";
4
+ export async function stripeCheckoutAction(input) {
5
+ const provider = getStripeProvider();
6
+ const result = await provider.createCheckoutSession(input);
7
+ return toCheckoutSessionDto(result);
8
+ }
9
+ export async function stripePortalAction(input) {
10
+ const provider = getStripeProvider();
11
+ const result = await provider.createBillingPortalSession(input);
12
+ return toBillingPortalSessionDto(result);
13
+ }
14
+ export async function stripeBillingStatusAction(adapter, workspaceId, stripeStatus) {
15
+ const record = await adapter.getRecord(workspaceId);
16
+ const status = resolveSubscriptionStatus(record, stripeStatus ?? null);
17
+ return toBillingStatusDto(record, status);
18
+ }
19
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,yBAAyB,EACzB,kBAAkB,EAClB,oBAAoB,GAKrB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,KAAkC;IAElC,MAAM,QAAQ,GAAG,iBAAiB,EAAS,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC3D,OAAO,oBAAoB,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAgC;IACvE,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;IAChE,OAAO,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,OAA8B,EAC9B,WAAmB,EACnB,YAAiD;IAEjD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,yBAAyB,CAAC,MAAM,EAAE,YAAY,IAAI,IAAI,CAAC,CAAC;IACvE,OAAO,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export type { BillingAdapter, BillingRecord } from "./types.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import type { BillingRecord } from "../types.js";
2
+ export interface BillingAdapter<TPlan extends string = string> {
3
+ getRecord(workspaceId: string): Promise<BillingRecord<TPlan> | null>;
4
+ upsertRecord(record: BillingRecord<TPlan>): Promise<void>;
5
+ /** Map a Stripe Price ID to an app plan. Used by subscription sync handlers. */
6
+ resolvePlanFromPriceId?(priceId: string): TPlan | null;
7
+ /** Plan used when a subscription is cancelled. Defaults to "free" if not set. */
8
+ freePlan?: TPlan;
9
+ }
10
+ export type { BillingRecord };
11
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapter/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IAC3D,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACrE,YAAY,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,gFAAgF;IAChF,sBAAsB,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;IACvD,iFAAiF;IACjF,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED,YAAY,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapter/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type Stripe from "stripe";
2
+ import type { CheckoutSessionInput, CheckoutSessionResult } from "./types.js";
3
+ export declare function createCheckoutSession<TPlan extends string = string>(stripe: Stripe, input: CheckoutSessionInput<TPlan>): Promise<CheckoutSessionResult>;
4
+ //# sourceMappingURL=checkout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE9E,wBAAsB,qBAAqB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EACvE,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,oBAAoB,CAAC,KAAK,CAAC,GACjC,OAAO,CAAC,qBAAqB,CAAC,CA2ChC"}
@@ -0,0 +1,45 @@
1
+ export async function createCheckoutSession(stripe, input) {
2
+ if (!input.priceId)
3
+ throw new Error("createCheckoutSession requires priceId");
4
+ if (!input.workspaceId)
5
+ throw new Error("createCheckoutSession requires workspaceId");
6
+ if (!input.userId)
7
+ throw new Error("createCheckoutSession requires userId");
8
+ if (!input.successUrl)
9
+ throw new Error("createCheckoutSession requires successUrl");
10
+ if (!input.cancelUrl)
11
+ throw new Error("createCheckoutSession requires cancelUrl");
12
+ const mode = input.mode ?? "subscription";
13
+ const quantity = input.quantity ?? 1;
14
+ const session = await stripe.checkout.sessions.create({
15
+ mode,
16
+ line_items: [{ price: input.priceId, quantity }],
17
+ success_url: input.successUrl,
18
+ cancel_url: input.cancelUrl,
19
+ customer_email: input.customerEmail,
20
+ client_reference_id: input.workspaceId,
21
+ metadata: {
22
+ workspaceId: input.workspaceId,
23
+ userId: input.userId,
24
+ plan: input.plan,
25
+ ...input.metadata,
26
+ },
27
+ subscription_data: mode === "subscription"
28
+ ? {
29
+ metadata: {
30
+ workspaceId: input.workspaceId,
31
+ userId: input.userId,
32
+ plan: input.plan,
33
+ },
34
+ }
35
+ : undefined,
36
+ });
37
+ if (!session.url) {
38
+ throw new Error("Stripe Checkout Session was created without a URL");
39
+ }
40
+ return {
41
+ sessionId: session.id,
42
+ url: session.url,
43
+ };
44
+ }
45
+ //# sourceMappingURL=checkout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout.js","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAc,EACd,KAAkC;IAElC,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC9E,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IACtF,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC5E,IAAI,CAAC,KAAK,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IACpF,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAElF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,cAAc,CAAC;IAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;IAErC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QACpD,IAAI;QACJ,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;QAChD,WAAW,EAAE,KAAK,CAAC,UAAU;QAC7B,UAAU,EAAE,KAAK,CAAC,SAAS;QAC3B,cAAc,EAAE,KAAK,CAAC,aAAa;QACnC,mBAAmB,EAAE,KAAK,CAAC,WAAW;QACtC,QAAQ,EAAE;YACR,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,KAAK,CAAC,QAAQ;SAClB;QACD,iBAAiB,EACf,IAAI,KAAK,cAAc;YACrB,CAAC,CAAC;gBACE,QAAQ,EAAE;oBACR,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,IAAI,EAAE,KAAK,CAAC,IAAI;iBACjB;aACF;YACH,CAAC,CAAC,SAAS;KAChB,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,GAAG,EAAE,OAAO,CAAC,GAAG;KACjB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import Stripe from "stripe";
2
+ import type { StripeClientConfig } from "./types.js";
3
+ export declare function createStripeClient(config: StripeClientConfig): Stripe;
4
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAKrE"}
package/dist/client.js ADDED
@@ -0,0 +1,8 @@
1
+ import Stripe from "stripe";
2
+ export function createStripeClient(config) {
3
+ if (!config.secretKey) {
4
+ throw new Error("createStripeClient requires a non-empty secretKey");
5
+ }
6
+ return new Stripe(config.secretKey, config.apiVersion ? { apiVersion: config.apiVersion } : {});
7
+ }
8
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAClG,CAAC"}
package/dist/dto.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { BillingRecord, CheckoutSessionResult } from "./types.js";
2
+ /** Safe checkout payload for client redirects — no raw Stripe objects. */
3
+ export interface CheckoutSessionDto {
4
+ sessionId: string;
5
+ url: string;
6
+ }
7
+ /** Safe billing portal payload for client redirects. */
8
+ export interface BillingPortalSessionDto {
9
+ url: string;
10
+ }
11
+ /** Safe subscription snapshot exposed to the client. */
12
+ export interface BillingStatusDto<TPlan extends string = string> {
13
+ workspaceId: string;
14
+ plan: TPlan;
15
+ subscriptionStatus: SubscriptionStatus;
16
+ hasActiveSubscription: boolean;
17
+ updatedAt: string;
18
+ }
19
+ export type SubscriptionStatus = "none" | "active" | "trialing" | "past_due" | "canceled" | "incomplete";
20
+ export declare function toCheckoutSessionDto(result: CheckoutSessionResult): CheckoutSessionDto;
21
+ export declare function toBillingStatusDto<TPlan extends string>(record: BillingRecord<TPlan> | null, subscriptionStatus?: SubscriptionStatus): BillingStatusDto<TPlan> | null;
22
+ export declare function toBillingPortalSessionDto(result: {
23
+ url: string;
24
+ }): BillingPortalSessionDto;
25
+ //# sourceMappingURL=dto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../src/dto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEvE,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wDAAwD;AACxD,MAAM,WAAW,gBAAgB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC;IACZ,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC;AAEzG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,qBAAqB,GAAG,kBAAkB,CAKtF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,MAAM,EACrD,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,EACnC,kBAAkB,GAAE,kBAA2B,GAC9C,gBAAgB,CAAC,KAAK,CAAC,GAAG,IAAI,CAShC;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,uBAAuB,CAE1F"}
package/dist/dto.js ADDED
@@ -0,0 +1,21 @@
1
+ export function toCheckoutSessionDto(result) {
2
+ return {
3
+ sessionId: result.sessionId,
4
+ url: result.url,
5
+ };
6
+ }
7
+ export function toBillingStatusDto(record, subscriptionStatus = "none") {
8
+ if (!record)
9
+ return null;
10
+ return {
11
+ workspaceId: record.workspaceId,
12
+ plan: record.plan,
13
+ subscriptionStatus,
14
+ hasActiveSubscription: subscriptionStatus === "active" || subscriptionStatus === "trialing",
15
+ updatedAt: record.updatedAt,
16
+ };
17
+ }
18
+ export function toBillingPortalSessionDto(result) {
19
+ return { url: result.url };
20
+ }
21
+ //# sourceMappingURL=dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dto.js","sourceRoot":"","sources":["../src/dto.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,oBAAoB,CAAC,MAA6B;IAChE,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,GAAG,EAAE,MAAM,CAAC,GAAG;KAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,MAAmC,EACnC,qBAAyC,MAAM;IAE/C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO;QACL,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,kBAAkB;QAClB,qBAAqB,EAAE,kBAAkB,KAAK,QAAQ,IAAI,kBAAkB,KAAK,UAAU;QAC3F,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,MAAuB;IAC/D,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare class OtokStripeError extends Error {
2
+ readonly code: string;
3
+ constructor(code: string, message: string);
4
+ }
5
+ export declare class OtokStripeConfigError extends OtokStripeError {
6
+ constructor(message: string);
7
+ }
8
+ export declare class OtokStripeRuntimeError extends OtokStripeError {
9
+ constructor(message: string);
10
+ }
11
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,qBAAa,qBAAsB,SAAQ,eAAe;gBAC5C,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,sBAAuB,SAAQ,eAAe;gBAC7C,OAAO,EAAE,MAAM;CAI5B"}
package/dist/errors.js ADDED
@@ -0,0 +1,21 @@
1
+ export class OtokStripeError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(`[otok-stripe:${code}] ${message}`);
5
+ this.name = "OtokStripeError";
6
+ this.code = code;
7
+ }
8
+ }
9
+ export class OtokStripeConfigError extends OtokStripeError {
10
+ constructor(message) {
11
+ super("config", message);
12
+ this.name = "OtokStripeConfigError";
13
+ }
14
+ }
15
+ export class OtokStripeRuntimeError extends OtokStripeError {
16
+ constructor(message) {
17
+ super("runtime", message);
18
+ this.name = "OtokStripeRuntimeError";
19
+ }
20
+ }
21
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,OAAe;QACvC,KAAK,CAAC,gBAAgB,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,eAAe;IACxD,YAAY,OAAe;QACzB,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,eAAe;IACzD,YAAY,OAAe;QACzB,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { StripeProvider, StripeProviderConfig } from "./provider/types.js";
2
+ export declare function createStripeProvider<TPlan extends string = string>(config: StripeProviderConfig): StripeProvider<TPlan>;
3
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIhF,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAChE,MAAM,EAAE,oBAAoB,GAC3B,cAAc,CAAC,KAAK,CAAC,CAWvB"}
@@ -0,0 +1,16 @@
1
+ import { OtokStripeConfigError } from "./errors.js";
2
+ import { createLiveStripeProvider } from "./providers/live.js";
3
+ import { createTestStripeProvider } from "./providers/test.js";
4
+ export function createStripeProvider(config) {
5
+ switch (config.type) {
6
+ case "live":
7
+ return createLiveStripeProvider(config);
8
+ case "test":
9
+ return createTestStripeProvider();
10
+ default: {
11
+ const unknown = config;
12
+ throw new OtokStripeConfigError(`unknown stripe provider "${unknown.type ?? "undefined"}"`);
13
+ }
14
+ }
15
+ }
16
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,UAAU,oBAAoB,CAClC,MAA4B;IAE5B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM;YACT,OAAO,wBAAwB,CAAQ,MAAM,CAAC,CAAC;QACjD,KAAK,MAAM;YACT,OAAO,wBAAwB,EAAS,CAAC;QAC3C,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,OAAO,GAAG,MAA2B,CAAC;YAC5C,MAAM,IAAI,qBAAqB,CAAC,4BAA4B,OAAO,CAAC,IAAI,IAAI,WAAW,GAAG,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { BillingRecord } from "./types.js";
2
+ import type { SubscriptionStatus } from "./dto.js";
3
+ export interface EventIdempotencyStore {
4
+ hasProcessed(eventId: string): Promise<boolean>;
5
+ markProcessed(eventId: string): Promise<void>;
6
+ }
7
+ export declare function createMemoryEventIdempotencyStore(): EventIdempotencyStore;
8
+ /** @internal Test helper */
9
+ export declare function resetMemoryEventIdempotencyStore(store: EventIdempotencyStore & {
10
+ _reset?: () => void;
11
+ }): void;
12
+ export declare function resolveSubscriptionStatus(record: BillingRecord | null, stripeStatus?: string | null): SubscriptionStatus;
13
+ export declare function processStripeEventIdempotently<T>(store: EventIdempotencyStore, eventId: string, handler: () => Promise<T>): Promise<{
14
+ duplicate: boolean;
15
+ result: T | null;
16
+ }>;
17
+ //# sourceMappingURL=idempotency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,qBAAqB;IACpC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,wBAAgB,iCAAiC,IAAI,qBAAqB,CAUzE;AAED,4BAA4B;AAC5B,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,qBAAqB,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,QAItG;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,aAAa,GAAG,IAAI,EAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAC3B,kBAAkB,CAQpB;AAED,wBAAsB,8BAA8B,CAAC,CAAC,EACpD,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAA;CAAE,CAAC,CAOnD"}
@@ -0,0 +1,41 @@
1
+ export function createMemoryEventIdempotencyStore() {
2
+ const processed = new Set();
3
+ return {
4
+ async hasProcessed(eventId) {
5
+ return processed.has(eventId);
6
+ },
7
+ async markProcessed(eventId) {
8
+ processed.add(eventId);
9
+ },
10
+ };
11
+ }
12
+ /** @internal Test helper */
13
+ export function resetMemoryEventIdempotencyStore(store) {
14
+ if ("_reset" in store && typeof store._reset === "function") {
15
+ store._reset();
16
+ }
17
+ }
18
+ export function resolveSubscriptionStatus(record, stripeStatus) {
19
+ if (!record?.stripeSubscriptionId)
20
+ return "none";
21
+ if (stripeStatus === "active")
22
+ return "active";
23
+ if (stripeStatus === "trialing")
24
+ return "trialing";
25
+ if (stripeStatus === "past_due")
26
+ return "past_due";
27
+ if (stripeStatus === "canceled" || stripeStatus === "unpaid")
28
+ return "canceled";
29
+ if (stripeStatus === "incomplete" || stripeStatus === "incomplete_expired")
30
+ return "incomplete";
31
+ return record.plan === "free" ? "none" : "active";
32
+ }
33
+ export async function processStripeEventIdempotently(store, eventId, handler) {
34
+ if (await store.hasProcessed(eventId)) {
35
+ return { duplicate: true, result: null };
36
+ }
37
+ const result = await handler();
38
+ await store.markProcessed(eventId);
39
+ return { duplicate: false, result };
40
+ }
41
+ //# sourceMappingURL=idempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,iCAAiC;IAC/C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,OAAO;QACL,KAAK,CAAC,YAAY,CAAC,OAAO;YACxB,OAAO,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,OAAO;YACzB,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,gCAAgC,CAAC,KAAsD;IACrG,IAAI,QAAQ,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC5D,KAAK,CAAC,MAAM,EAAE,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,MAA4B,EAC5B,YAA4B;IAE5B,IAAI,CAAC,MAAM,EAAE,oBAAoB;QAAE,OAAO,MAAM,CAAC;IACjD,IAAI,YAAY,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC/C,IAAI,YAAY,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IACnD,IAAI,YAAY,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IACnD,IAAI,YAAY,KAAK,UAAU,IAAI,YAAY,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC;IAChF,IAAI,YAAY,KAAK,YAAY,IAAI,YAAY,KAAK,oBAAoB;QAAE,OAAO,YAAY,CAAC;IAChG,OAAO,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,KAA4B,EAC5B,OAAe,EACf,OAAyB;IAEzB,IAAI,MAAM,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;IAC/B,MAAM,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACtC,CAAC"}
@@ -0,0 +1,15 @@
1
+ export { default } from "./plugin.js";
2
+ export { createStripeClient } from "./client.js";
3
+ export { createCheckoutSession } from "./checkout.js";
4
+ export { createBillingPortalSession, type BillingPortalSessionInput, type BillingPortalSessionResult, } from "./portal.js";
5
+ export { createStripeProvider } from "./factory.js";
6
+ export { getStripeProvider, getStripeRuntime } from "./registry.js";
7
+ export { stripeBillingStatusAction, stripeCheckoutAction, stripePortalAction } from "./actions.js";
8
+ export { createMemoryEventIdempotencyStore, processStripeEventIdempotently, resolveSubscriptionStatus, } from "./idempotency.js";
9
+ export { toBillingPortalSessionDto, toBillingStatusDto, toCheckoutSessionDto } from "./dto.js";
10
+ export { OtokStripeConfigError, OtokStripeError, OtokStripeRuntimeError } from "./errors.js";
11
+ export type { BillingPortalSessionDto, BillingStatusDto, CheckoutSessionDto, SubscriptionStatus } from "./dto.js";
12
+ export type { BillingRecord, CheckoutMode, CheckoutSessionInput, CheckoutSessionResult, StripeClientConfig, } from "./types.js";
13
+ export type { BillingAdapter } from "./adapter/types.js";
14
+ export type { LiveStripeProviderConfig, StripePluginOptions, StripeProvider, StripeProviderCapabilities, StripeProviderConfig, StripeRuntime, TestStripeProviderConfig, } from "./provider/types.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,GAChC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACnG,OAAO,EACL,iCAAiC,EACjC,8BAA8B,EAC9B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC7F,YAAY,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAClH,YAAY,EACV,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EACV,wBAAwB,EACxB,mBAAmB,EACnB,cAAc,EACd,0BAA0B,EAC1B,oBAAoB,EACpB,aAAa,EACb,wBAAwB,GACzB,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { default } from "./plugin.js";
2
+ export { createStripeClient } from "./client.js";
3
+ export { createCheckoutSession } from "./checkout.js";
4
+ export { createBillingPortalSession, } from "./portal.js";
5
+ export { createStripeProvider } from "./factory.js";
6
+ export { getStripeProvider, getStripeRuntime } from "./registry.js";
7
+ export { stripeBillingStatusAction, stripeCheckoutAction, stripePortalAction } from "./actions.js";
8
+ export { createMemoryEventIdempotencyStore, processStripeEventIdempotently, resolveSubscriptionStatus, } from "./idempotency.js";
9
+ export { toBillingPortalSessionDto, toBillingStatusDto, toCheckoutSessionDto } from "./dto.js";
10
+ export { OtokStripeConfigError, OtokStripeError, OtokStripeRuntimeError } from "./errors.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACL,0BAA0B,GAG3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACnG,OAAO,EACL,iCAAiC,EACjC,8BAA8B,EAC9B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Hono } from "hono";
2
+ import type { BillingAdapter } from "./adapter/types.js";
3
+ import type { StripePluginOptions, StripeRuntime } from "./provider/types.js";
4
+ /** @internal Test helper */
5
+ export declare function resetStripeIdempotencyForTests(): void;
6
+ export declare function getStripeIdempotencyStore(): import("./idempotency.js").EventIdempotencyStore;
7
+ export declare function configureStripeApp<TPlan extends string = string>(app: Hono, options: StripePluginOptions<TPlan>, adapter?: BillingAdapter<TPlan>): Promise<StripeRuntime<TPlan>>;
8
+ /**
9
+ * Otok Stripe plugin factory.
10
+ *
11
+ * Webhook routes require passing a BillingAdapter to configureStripeApp or mounting manually.
12
+ */
13
+ export default function stripe<TPlan extends string = string>(options: StripePluginOptions<TPlan>): import("@kamod-ch/otok-config").OtokPlugin<unknown>;
14
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAM9E,4BAA4B;AAC5B,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAED,wBAAgB,yBAAyB,qDAExC;AAMD,wBAAsB,kBAAkB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EACpE,GAAG,EAAE,IAAI,EACT,OAAO,EAAE,mBAAmB,CAAC,KAAK,CAAC,EACnC,OAAO,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,GAC9B,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAsC/B;AAgCD;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,mBAAmB,CAAC,KAAK,CAAC,uDAQhG"}
package/dist/plugin.js ADDED
@@ -0,0 +1,90 @@
1
+ import { definePlugin } from "@kamod-ch/otok";
2
+ import { createStripeProvider } from "./factory.js";
3
+ import { OtokStripeConfigError } from "./errors.js";
4
+ import { registerStripeRuntime } from "./registry.js";
5
+ import { createMemoryEventIdempotencyStore, processStripeEventIdempotently } from "./idempotency.js";
6
+ import { dispatchStripeEvent } from "./webhook/events.js";
7
+ let idempotencyStore = createMemoryEventIdempotencyStore();
8
+ /** @internal Test helper */
9
+ export function resetStripeIdempotencyForTests() {
10
+ idempotencyStore = createMemoryEventIdempotencyStore();
11
+ }
12
+ export function getStripeIdempotencyStore() {
13
+ return idempotencyStore;
14
+ }
15
+ function resolveWebhookSecret(options) {
16
+ return options.webhookSecret ?? process.env[options.webhookSecretEnv ?? "STRIPE_WEBHOOK_SECRET"]?.trim();
17
+ }
18
+ export async function configureStripeApp(app, options, adapter) {
19
+ const provider = createStripeProvider(options.provider);
20
+ const webhookPath = options.webhookPath ?? "/api/billing/webhook";
21
+ const webhookSecret = resolveWebhookSecret(options);
22
+ const runtime = {
23
+ provider,
24
+ webhookPath,
25
+ webhookSecret,
26
+ };
27
+ registerStripeRuntime(runtime);
28
+ if (adapter && webhookSecret) {
29
+ app.post(webhookPath, async (c) => {
30
+ const signature = c.req.header("stripe-signature");
31
+ if (!signature) {
32
+ return c.json({ error: { code: "missing_signature", message: "Missing stripe-signature header" } }, 400);
33
+ }
34
+ const rawBody = await c.req.text();
35
+ let event;
36
+ try {
37
+ event = provider.constructWebhookEvent(rawBody, signature, webhookSecret);
38
+ }
39
+ catch (error) {
40
+ const message = error instanceof Error ? error.message : "Invalid signature";
41
+ return c.json({ error: { code: "invalid_signature", message } }, 400);
42
+ }
43
+ const { duplicate, result } = await processStripeEventIdempotently(idempotencyStore, event.id, async () => dispatchStripeEvent(adapter, event));
44
+ return c.json({ received: true, duplicate, updated: Boolean(result) });
45
+ });
46
+ }
47
+ return runtime;
48
+ }
49
+ const stripePluginFactory = definePlugin({
50
+ name: "@kamod-ch/otok-stripe",
51
+ version: "1.1.0",
52
+ schema: {
53
+ parse(input) {
54
+ if (!input || typeof input !== "object") {
55
+ throw new OtokStripeConfigError("stripe() options must be an object");
56
+ }
57
+ const record = input;
58
+ if (!record.provider || typeof record.provider !== "object") {
59
+ throw new OtokStripeConfigError("stripe() requires provider configuration");
60
+ }
61
+ return input;
62
+ },
63
+ },
64
+ envSchema: {
65
+ parse(input) {
66
+ const secretKey = input.STRIPE_SECRET_KEY;
67
+ const webhookSecret = input.STRIPE_WEBHOOK_SECRET;
68
+ if (secretKey !== undefined && !secretKey) {
69
+ throw new OtokStripeConfigError("STRIPE_SECRET_KEY must not be empty when set");
70
+ }
71
+ if (webhookSecret !== undefined && !webhookSecret) {
72
+ throw new OtokStripeConfigError("STRIPE_WEBHOOK_SECRET must not be empty when set");
73
+ }
74
+ return { stripeSecretKey: secretKey, stripeWebhookSecret: webhookSecret };
75
+ },
76
+ },
77
+ });
78
+ /**
79
+ * Otok Stripe plugin factory.
80
+ *
81
+ * Webhook routes require passing a BillingAdapter to configureStripeApp or mounting manually.
82
+ */
83
+ export default function stripe(options) {
84
+ const plugin = stripePluginFactory(options);
85
+ plugin.configureApp = async ({ app }) => {
86
+ await configureStripeApp(app, options);
87
+ };
88
+ return plugin;
89
+ }
90
+ //# sourceMappingURL=plugin.js.map