@augmenting-integrations/billing 0.0.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/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @augmenting-integrations/billing
2
+
3
+ Stripe primitives for augint product apps.
4
+
5
+ ## Server (`@augmenting-integrations/billing/server`)
6
+
7
+ - `getStripe()` — cached `{client, publishableKey, apiVersion, webhookSecret}` bundle from Secrets Manager (or env vars in local dev)
8
+ - `verifyStripeWebhook(rawBody, signature)` — wraps `stripe.webhooks.constructEvent`
9
+ - `findOrCreateStripeCustomer(input, stripe)` — idempotent customer find-or-create; spoke persists the resulting id
10
+
11
+ ## Client (`@augmenting-integrations/billing/client`)
12
+
13
+ - `<StripeProvider publishableKey={...}>` — Elements wrapper with loadStripe caching
14
+ - `useCart<T>({ storageKey })` — generic localStorage-backed cart hook
15
+ - `<CreditBalanceBadge balance={...} />` — small chip
16
+ - `<PaymentMethodSelect cards={...} value={...} onChange={...} />` — saved-card dropdown
17
+
18
+ Always import from the `/server` or `/client` subpaths so server code doesn't leak into client bundles.
19
+
20
+ ```ts
21
+ // route handler
22
+ import {
23
+ getStripe,
24
+ findOrCreateStripeCustomer,
25
+ } from "@augmenting-integrations/billing/server";
26
+
27
+ // React component
28
+ import { StripeProvider, useCart } from "@augmenting-integrations/billing/client";
29
+ ```
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ export declare function CreditBalanceBadge({ balance, label, className, }: {
3
+ balance: number;
4
+ label?: string;
5
+ className?: string;
6
+ }): React.JSX.Element;
7
+ //# sourceMappingURL=CreditBalanceBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreditBalanceBadge.d.ts","sourceRoot":"","sources":["../../src/client/CreditBalanceBadge.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,KAAiB,EACjB,SAAS,GACV,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,qBAMA"}
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ export type SavedCard = {
3
+ id: string;
4
+ brand: string;
5
+ last4: string;
6
+ exp_month: number;
7
+ exp_year: number;
8
+ is_default: boolean;
9
+ };
10
+ export declare function PaymentMethodSelect({ cards, value, onChange, className, emptyLabel, }: {
11
+ cards: SavedCard[];
12
+ value: string | null;
13
+ onChange: (id: string) => void;
14
+ className?: string;
15
+ emptyLabel?: string;
16
+ }): React.JSX.Element;
17
+ //# sourceMappingURL=PaymentMethodSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PaymentMethodSelect.d.ts","sourceRoot":"","sources":["../../src/client/PaymentMethodSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAW/B,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,KAAK,EACL,QAAQ,EACR,SAAS,EACT,UAA6B,GAC9B,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,qBA2BA"}
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ import { Elements } from "@stripe/react-stripe-js";
3
+ export declare function StripeProvider({ publishableKey, options, children, }: {
4
+ publishableKey: string;
5
+ options?: Parameters<typeof Elements>[0]["options"];
6
+ children: React.ReactNode;
7
+ }): React.JSX.Element;
8
+ //# sourceMappingURL=StripeProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StripeProvider.d.ts","sourceRoot":"","sources":["../../src/client/StripeProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAgBnD,wBAAgB,cAAc,CAAC,EAC7B,cAAc,EACd,OAAO,EACP,QAAQ,GACT,EAAE;IACD,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACpD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAUA"}
@@ -0,0 +1,3 @@
1
+ import { type Stripe } from "@stripe/stripe-js";
2
+ export declare function loadStripeClient(publishableKey: string): Promise<Stripe | null>;
3
+ //# sourceMappingURL=stripe-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe-loader.d.ts","sourceRoot":"","sources":["../../src/client/stripe-loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAgB5D,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK/E"}
@@ -0,0 +1,27 @@
1
+ export type CartItemBase = {
2
+ id: string;
3
+ unitPrice: number;
4
+ quantity: number;
5
+ };
6
+ export type UseCartOptions = {
7
+ storageKey: string;
8
+ };
9
+ export type UseCartReturn<T extends CartItemBase> = {
10
+ items: T[];
11
+ add: (item: T) => void;
12
+ /**
13
+ * Merge an array of items into the cart by id. Duplicates increment
14
+ * existing quantities; new ids are appended.
15
+ */
16
+ mergeMany: (newItems: T[]) => void;
17
+ remove: (id: string) => void;
18
+ updateQuantity: (id: string, quantity: number) => void;
19
+ setItems: (items: T[]) => void;
20
+ clear: () => void;
21
+ /** Sum of unitPrice * quantity across all items. */
22
+ total: number;
23
+ /** Sum of quantities across all items. */
24
+ count: number;
25
+ };
26
+ export declare function useCart<T extends CartItemBase>(opts: UseCartOptions): UseCartReturn<T>;
27
+ //# sourceMappingURL=useCart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCart.d.ts","sourceRoot":"","sources":["../../src/client/useCart.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,YAAY,IAAI;IAClD,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IACvB;;;OAGG;IACH,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IACnC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,OAAO,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,CAAC,CAuFtF"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var client_exports = {};
20
+ __export(client_exports, {
21
+ CreditBalanceBadge: () => import_CreditBalanceBadge.CreditBalanceBadge,
22
+ PaymentMethodSelect: () => import_PaymentMethodSelect.PaymentMethodSelect,
23
+ StripeProvider: () => import_StripeProvider.StripeProvider,
24
+ loadStripeClient: () => import_stripe_loader.loadStripeClient,
25
+ useCart: () => import_useCart.useCart
26
+ });
27
+ module.exports = __toCommonJS(client_exports);
28
+ var import_stripe_loader = require("./client/stripe-loader.js");
29
+ var import_StripeProvider = require("./client/StripeProvider.js");
30
+ var import_useCart = require("./client/useCart.js");
31
+ var import_CreditBalanceBadge = require("./client/CreditBalanceBadge.js");
32
+ var import_PaymentMethodSelect = require("./client/PaymentMethodSelect.js");
33
+ // Annotate the CommonJS export names for ESM import in node:
34
+ 0 && (module.exports = {
35
+ CreditBalanceBadge,
36
+ PaymentMethodSelect,
37
+ StripeProvider,
38
+ loadStripeClient,
39
+ useCart
40
+ });
41
+ //# sourceMappingURL=client.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["export { loadStripeClient } from \"./client/stripe-loader.js\";\nexport { StripeProvider } from \"./client/StripeProvider.js\";\nexport {\n useCart,\n type CartItemBase,\n type UseCartOptions,\n type UseCartReturn,\n} from \"./client/useCart.js\";\nexport { CreditBalanceBadge } from \"./client/CreditBalanceBadge.js\";\nexport { PaymentMethodSelect, type SavedCard } from \"./client/PaymentMethodSelect.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAAiC;AACjC,4BAA+B;AAC/B,qBAKO;AACP,gCAAmC;AACnC,iCAAoD;","names":[]}
@@ -0,0 +1,6 @@
1
+ export { loadStripeClient } from "./client/stripe-loader.js";
2
+ export { StripeProvider } from "./client/StripeProvider.js";
3
+ export { useCart, type CartItemBase, type UseCartOptions, type UseCartReturn, } from "./client/useCart.js";
4
+ export { CreditBalanceBadge } from "./client/CreditBalanceBadge.js";
5
+ export { PaymentMethodSelect, type SavedCard } from "./client/PaymentMethodSelect.js";
6
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EACL,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,iCAAiC,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,15 @@
1
+ import { loadStripeClient } from "./client/stripe-loader.js";
2
+ import { StripeProvider } from "./client/StripeProvider.js";
3
+ import {
4
+ useCart
5
+ } from "./client/useCart.js";
6
+ import { CreditBalanceBadge } from "./client/CreditBalanceBadge.js";
7
+ import { PaymentMethodSelect } from "./client/PaymentMethodSelect.js";
8
+ export {
9
+ CreditBalanceBadge,
10
+ PaymentMethodSelect,
11
+ StripeProvider,
12
+ loadStripeClient,
13
+ useCart
14
+ };
15
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["export { loadStripeClient } from \"./client/stripe-loader.js\";\nexport { StripeProvider } from \"./client/StripeProvider.js\";\nexport {\n useCart,\n type CartItemBase,\n type UseCartOptions,\n type UseCartReturn,\n} from \"./client/useCart.js\";\nexport { CreditBalanceBadge } from \"./client/CreditBalanceBadge.js\";\nexport { PaymentMethodSelect, type SavedCard } from \"./client/PaymentMethodSelect.js\";\n"],"mappings":"AAAA,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,OAIK;AACP,SAAS,0BAA0B;AACnC,SAAS,2BAA2C;","names":[]}
@@ -0,0 +1,27 @@
1
+ import Stripe from "stripe";
2
+ export type StripeBundle = {
3
+ client: Stripe;
4
+ publishableKey: string;
5
+ apiVersion: string;
6
+ webhookSecret: string;
7
+ };
8
+ export type CreateStripeClientOptions = {
9
+ /** ARN of the bundled `{apiKey, publishableKey, apiVersion}` JSON secret. */
10
+ apiSecretArn?: string;
11
+ /** ARN of the per-endpoint webhook signing secret. */
12
+ webhookSecretArn?: string;
13
+ };
14
+ declare global {
15
+ var __stripeBillingCache: Promise<StripeBundle> | undefined;
16
+ }
17
+ /**
18
+ * Get-or-create the bundled Stripe client + publishable key + webhook secret.
19
+ * Cached for the container lifetime. Pass options or rely on env vars.
20
+ */
21
+ export declare function getStripe(opts?: CreateStripeClientOptions): Promise<StripeBundle>;
22
+ /**
23
+ * Verify a Stripe webhook against the cached signing secret. Returns the
24
+ * decoded Stripe.Event; throws if the signature doesn't match.
25
+ */
26
+ export declare function verifyStripeWebhook(rawBody: string, signature: string, opts?: CreateStripeClientOptions): Promise<Stripe.Event>;
27
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/server/client.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAiB5B,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAQF,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,oBAAoB,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC;CAC7D;AAiDD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,GAAE,yBAA8B,GAAG,OAAO,CAAC,YAAY,CAAC,CAGrF;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,yBAA8B,GACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAGvB"}
@@ -0,0 +1,28 @@
1
+ import type Stripe from "stripe";
2
+ export type StripeCustomerInput = {
3
+ /** Existing customer ID on the user (e.g. User.stripe_customer_id). */
4
+ existingId?: string | null;
5
+ email: string;
6
+ name?: string | null;
7
+ /**
8
+ * Stable identifier baked into the customer's metadata. Typically the
9
+ * stringified User.id; lets dashboard / webhook downstreams correlate.
10
+ */
11
+ appUserId: string;
12
+ };
13
+ export type FindOrCreateResult = {
14
+ /** The live (possibly newly-created) Stripe customer id. */
15
+ customerId: string;
16
+ /** True when a new customer was created on Stripe in this call. */
17
+ created: boolean;
18
+ };
19
+ /**
20
+ * Idempotently find-or-create a Stripe Customer. If `existingId` resolves to
21
+ * a live (non-deleted) customer, returns that id. Otherwise creates a new
22
+ * customer and returns its id with `created: true`.
23
+ *
24
+ * Caller is responsible for persisting `customerId` to its own DB when
25
+ * `created` is true.
26
+ */
27
+ export declare function findOrCreateStripeCustomer(input: StripeCustomerInput, stripe: Stripe): Promise<FindOrCreateResult>;
28
+ //# sourceMappingURL=customer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customer.d.ts","sourceRoot":"","sources":["../../src/server/customer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAajC,MAAM,MAAM,mBAAmB,GAAG;IAChC,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,mBAAmB,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,kBAAkB,CAAC,CAuB7B"}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/server.ts
31
+ var server_exports = {};
32
+ __export(server_exports, {
33
+ findOrCreateStripeCustomer: () => findOrCreateStripeCustomer,
34
+ getStripe: () => getStripe,
35
+ verifyStripeWebhook: () => verifyStripeWebhook
36
+ });
37
+ module.exports = __toCommonJS(server_exports);
38
+
39
+ // src/server/client.ts
40
+ var import_stripe = __toESM(require("stripe"));
41
+ var import_server = require("@augmenting-integrations/aws/server");
42
+ async function loadBundle(opts) {
43
+ if (process.env.STRIPE_API_KEY && process.env.STRIPE_WEBHOOK_SECRET) {
44
+ const apiKey = process.env.STRIPE_API_KEY;
45
+ const publishableKey = process.env.STRIPE_PUBLISHABLE_KEY ?? "";
46
+ const apiVersion = process.env.STRIPE_API_VERSION ?? "2025-09-30.clover";
47
+ const webhookSecret2 = process.env.STRIPE_WEBHOOK_SECRET;
48
+ const client2 = new import_stripe.default(apiKey, {
49
+ apiVersion
50
+ });
51
+ return { client: client2, publishableKey, apiVersion, webhookSecret: webhookSecret2 };
52
+ }
53
+ const apiSecretArn = opts.apiSecretArn ?? process.env.STRIPE_API_SECRET_ARN;
54
+ const webhookSecretArn = opts.webhookSecretArn ?? process.env.STRIPE_WEBHOOK_SECRET_ARN;
55
+ if (!apiSecretArn || !webhookSecretArn) {
56
+ throw new Error(
57
+ `[billing] createStripeClient requires (STRIPE_API_KEY + STRIPE_WEBHOOK_SECRET) for local dev or both apiSecretArn + webhookSecretArn (or matching env vars) for Lambda. Got apiSecretArn=${!!apiSecretArn}, webhookSecretArn=${!!webhookSecretArn}.`
58
+ );
59
+ }
60
+ const [bundleJson, webhookSecret] = await Promise.all([
61
+ (0, import_server.getSecret)(apiSecretArn),
62
+ (0, import_server.getSecret)(webhookSecretArn)
63
+ ]);
64
+ if (!bundleJson || !webhookSecret) {
65
+ throw new Error(
66
+ `[billing] Stripe secrets could not be resolved (apiSecretArn=${apiSecretArn}, webhookSecretArn=${webhookSecretArn})`
67
+ );
68
+ }
69
+ const bundle = JSON.parse(bundleJson);
70
+ if (!bundle.apiKey || !bundle.publishableKey || !bundle.apiVersion) {
71
+ throw new Error(
72
+ `[billing] Stripe secret ${apiSecretArn} missing apiKey/publishableKey/apiVersion`
73
+ );
74
+ }
75
+ const client = new import_stripe.default(bundle.apiKey, {
76
+ apiVersion: bundle.apiVersion
77
+ });
78
+ return {
79
+ client,
80
+ publishableKey: bundle.publishableKey,
81
+ apiVersion: bundle.apiVersion,
82
+ webhookSecret
83
+ };
84
+ }
85
+ function getStripe(opts = {}) {
86
+ globalThis.__stripeBillingCache ??= loadBundle(opts);
87
+ return globalThis.__stripeBillingCache;
88
+ }
89
+ async function verifyStripeWebhook(rawBody, signature, opts = {}) {
90
+ const { client, webhookSecret } = await getStripe(opts);
91
+ return client.webhooks.constructEvent(rawBody, signature, webhookSecret);
92
+ }
93
+
94
+ // src/server/customer.ts
95
+ async function findOrCreateStripeCustomer(input, stripe) {
96
+ if (input.existingId) {
97
+ try {
98
+ const existing = await stripe.customers.retrieve(input.existingId);
99
+ if (!existing.deleted) {
100
+ return { customerId: existing.id, created: false };
101
+ }
102
+ } catch (err) {
103
+ const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
104
+ if (code !== "resource_missing") throw err;
105
+ }
106
+ }
107
+ const created = await stripe.customers.create({
108
+ email: input.email,
109
+ name: input.name ?? void 0,
110
+ metadata: { app_user_id: input.appUserId }
111
+ });
112
+ return { customerId: created.id, created: true };
113
+ }
114
+ // Annotate the CommonJS export names for ESM import in node:
115
+ 0 && (module.exports = {
116
+ findOrCreateStripeCustomer,
117
+ getStripe,
118
+ verifyStripeWebhook
119
+ });
120
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/server/client.ts","../src/server/customer.ts"],"sourcesContent":["export {\n getStripe,\n verifyStripeWebhook,\n type StripeBundle,\n type CreateStripeClientOptions,\n} from \"./server/client.js\";\nexport {\n findOrCreateStripeCustomer,\n type StripeCustomerInput,\n type FindOrCreateResult,\n} from \"./server/customer.js\";\n","import Stripe from \"stripe\";\nimport { getSecret } from \"@augmenting-integrations/aws/server\";\n\n// =============================================================================\n// Stripe client init.\n//\n// Production: API key, publishable key, and API version come bundled in a\n// single SecretsManager JSON blob (created by augint-example-infra) keyed by\n// `STRIPE_API_SECRET_ARN`. Webhook signing secret is a separate per-endpoint\n// secret at `STRIPE_WEBHOOK_SECRET_ARN`.\n//\n// Local dev: STRIPE_API_KEY / STRIPE_PUBLISHABLE_KEY / STRIPE_API_VERSION /\n// STRIPE_WEBHOOK_SECRET in .env are used directly.\n//\n// Cached on globalThis for warm-Lambda reuse + Next dev hot-reload.\n// =============================================================================\n\nexport type StripeBundle = {\n client: Stripe;\n publishableKey: string;\n apiVersion: string;\n webhookSecret: string;\n};\n\nexport type CreateStripeClientOptions = {\n /** ARN of the bundled `{apiKey, publishableKey, apiVersion}` JSON secret. */\n apiSecretArn?: string;\n /** ARN of the per-endpoint webhook signing secret. */\n webhookSecretArn?: string;\n};\n\ntype StripeApiBundle = {\n apiKey: string;\n publishableKey: string;\n apiVersion: string;\n};\n\ndeclare global {\n var __stripeBillingCache: Promise<StripeBundle> | undefined;\n}\n\nasync function loadBundle(opts: CreateStripeClientOptions): Promise<StripeBundle> {\n // Local dev short-circuit\n if (process.env.STRIPE_API_KEY && process.env.STRIPE_WEBHOOK_SECRET) {\n const apiKey = process.env.STRIPE_API_KEY;\n const publishableKey = process.env.STRIPE_PUBLISHABLE_KEY ?? \"\";\n const apiVersion = process.env.STRIPE_API_VERSION ?? \"2025-09-30.clover\";\n const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;\n const client = new Stripe(apiKey, {\n apiVersion: apiVersion as Stripe.LatestApiVersion,\n });\n return { client, publishableKey, apiVersion, webhookSecret };\n }\n\n const apiSecretArn = opts.apiSecretArn ?? process.env.STRIPE_API_SECRET_ARN;\n const webhookSecretArn = opts.webhookSecretArn ?? process.env.STRIPE_WEBHOOK_SECRET_ARN;\n if (!apiSecretArn || !webhookSecretArn) {\n throw new Error(\n `[billing] createStripeClient requires (STRIPE_API_KEY + STRIPE_WEBHOOK_SECRET) for local dev or both apiSecretArn + webhookSecretArn (or matching env vars) for Lambda. Got apiSecretArn=${!!apiSecretArn}, webhookSecretArn=${!!webhookSecretArn}.`,\n );\n }\n\n const [bundleJson, webhookSecret] = await Promise.all([\n getSecret(apiSecretArn),\n getSecret(webhookSecretArn),\n ]);\n if (!bundleJson || !webhookSecret) {\n throw new Error(\n `[billing] Stripe secrets could not be resolved (apiSecretArn=${apiSecretArn}, webhookSecretArn=${webhookSecretArn})`,\n );\n }\n const bundle = JSON.parse(bundleJson) as StripeApiBundle;\n if (!bundle.apiKey || !bundle.publishableKey || !bundle.apiVersion) {\n throw new Error(\n `[billing] Stripe secret ${apiSecretArn} missing apiKey/publishableKey/apiVersion`,\n );\n }\n const client = new Stripe(bundle.apiKey, {\n apiVersion: bundle.apiVersion as Stripe.LatestApiVersion,\n });\n return {\n client,\n publishableKey: bundle.publishableKey,\n apiVersion: bundle.apiVersion,\n webhookSecret,\n };\n}\n\n/**\n * Get-or-create the bundled Stripe client + publishable key + webhook secret.\n * Cached for the container lifetime. Pass options or rely on env vars.\n */\nexport function getStripe(opts: CreateStripeClientOptions = {}): Promise<StripeBundle> {\n globalThis.__stripeBillingCache ??= loadBundle(opts);\n return globalThis.__stripeBillingCache;\n}\n\n/**\n * Verify a Stripe webhook against the cached signing secret. Returns the\n * decoded Stripe.Event; throws if the signature doesn't match.\n */\nexport async function verifyStripeWebhook(\n rawBody: string,\n signature: string,\n opts: CreateStripeClientOptions = {},\n): Promise<Stripe.Event> {\n const { client, webhookSecret } = await getStripe(opts);\n return client.webhooks.constructEvent(rawBody, signature, webhookSecret);\n}\n","import type Stripe from \"stripe\";\n\n// =============================================================================\n// Stripe customer find-or-create.\n//\n// This is the library's pure-Stripe helper: it takes the customer-id the\n// caller has on file (typically User.stripe_customer_id), checks Stripe for\n// liveness, and creates a new one if missing or deleted. The caller is\n// responsible for persisting the (possibly new) id back to its User row.\n//\n// Pure of Prisma so it works with any DB layer.\n// =============================================================================\n\nexport type StripeCustomerInput = {\n /** Existing customer ID on the user (e.g. User.stripe_customer_id). */\n existingId?: string | null;\n email: string;\n name?: string | null;\n /**\n * Stable identifier baked into the customer's metadata. Typically the\n * stringified User.id; lets dashboard / webhook downstreams correlate.\n */\n appUserId: string;\n};\n\nexport type FindOrCreateResult = {\n /** The live (possibly newly-created) Stripe customer id. */\n customerId: string;\n /** True when a new customer was created on Stripe in this call. */\n created: boolean;\n};\n\n/**\n * Idempotently find-or-create a Stripe Customer. If `existingId` resolves to\n * a live (non-deleted) customer, returns that id. Otherwise creates a new\n * customer and returns its id with `created: true`.\n *\n * Caller is responsible for persisting `customerId` to its own DB when\n * `created` is true.\n */\nexport async function findOrCreateStripeCustomer(\n input: StripeCustomerInput,\n stripe: Stripe,\n): Promise<FindOrCreateResult> {\n if (input.existingId) {\n try {\n const existing = await stripe.customers.retrieve(input.existingId);\n if (!existing.deleted) {\n return { customerId: existing.id, created: false };\n }\n } catch (err: unknown) {\n const code =\n err && typeof err === \"object\" && \"code\" in err\n ? (err as { code: string }).code\n : undefined;\n if (code !== \"resource_missing\") throw err;\n // resource_missing -> fall through to recreate\n }\n }\n\n const created = await stripe.customers.create({\n email: input.email,\n name: input.name ?? undefined,\n metadata: { app_user_id: input.appUserId },\n });\n return { customerId: created.id, created: true };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAmB;AACnB,oBAA0B;AAwC1B,eAAe,WAAW,MAAwD;AAEhF,MAAI,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,uBAAuB;AACnE,UAAM,SAAS,QAAQ,IAAI;AAC3B,UAAM,iBAAiB,QAAQ,IAAI,0BAA0B;AAC7D,UAAM,aAAa,QAAQ,IAAI,sBAAsB;AACrD,UAAMA,iBAAgB,QAAQ,IAAI;AAClC,UAAMC,UAAS,IAAI,cAAAC,QAAO,QAAQ;AAAA,MAChC;AAAA,IACF,CAAC;AACD,WAAO,EAAE,QAAAD,SAAQ,gBAAgB,YAAY,eAAAD,eAAc;AAAA,EAC7D;AAEA,QAAM,eAAe,KAAK,gBAAgB,QAAQ,IAAI;AACtD,QAAM,mBAAmB,KAAK,oBAAoB,QAAQ,IAAI;AAC9D,MAAI,CAAC,gBAAgB,CAAC,kBAAkB;AACtC,UAAM,IAAI;AAAA,MACR,4LAA4L,CAAC,CAAC,YAAY,sBAAsB,CAAC,CAAC,gBAAgB;AAAA,IACpP;AAAA,EACF;AAEA,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,QACpD,yBAAU,YAAY;AAAA,QACtB,yBAAU,gBAAgB;AAAA,EAC5B,CAAC;AACD,MAAI,CAAC,cAAc,CAAC,eAAe;AACjC,UAAM,IAAI;AAAA,MACR,gEAAgE,YAAY,sBAAsB,gBAAgB;AAAA,IACpH;AAAA,EACF;AACA,QAAM,SAAS,KAAK,MAAM,UAAU;AACpC,MAAI,CAAC,OAAO,UAAU,CAAC,OAAO,kBAAkB,CAAC,OAAO,YAAY;AAClE,UAAM,IAAI;AAAA,MACR,2BAA2B,YAAY;AAAA,IACzC;AAAA,EACF;AACA,QAAM,SAAS,IAAI,cAAAE,QAAO,OAAO,QAAQ;AAAA,IACvC,YAAY,OAAO;AAAA,EACrB,CAAC;AACD,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,OAAO;AAAA,IACvB,YAAY,OAAO;AAAA,IACnB;AAAA,EACF;AACF;AAMO,SAAS,UAAU,OAAkC,CAAC,GAA0B;AACrF,aAAW,yBAAyB,WAAW,IAAI;AACnD,SAAO,WAAW;AACpB;AAMA,eAAsB,oBACpB,SACA,WACA,OAAkC,CAAC,GACZ;AACvB,QAAM,EAAE,QAAQ,cAAc,IAAI,MAAM,UAAU,IAAI;AACtD,SAAO,OAAO,SAAS,eAAe,SAAS,WAAW,aAAa;AACzE;;;ACpEA,eAAsB,2BACpB,OACA,QAC6B;AAC7B,MAAI,MAAM,YAAY;AACpB,QAAI;AACF,YAAM,WAAW,MAAM,OAAO,UAAU,SAAS,MAAM,UAAU;AACjE,UAAI,CAAC,SAAS,SAAS;AACrB,eAAO,EAAE,YAAY,SAAS,IAAI,SAAS,MAAM;AAAA,MACnD;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,OACJ,OAAO,OAAO,QAAQ,YAAY,UAAU,MACvC,IAAyB,OAC1B;AACN,UAAI,SAAS,mBAAoB,OAAM;AAAA,IAEzC;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,OAAO,UAAU,OAAO;AAAA,IAC5C,OAAO,MAAM;AAAA,IACb,MAAM,MAAM,QAAQ;AAAA,IACpB,UAAU,EAAE,aAAa,MAAM,UAAU;AAAA,EAC3C,CAAC;AACD,SAAO,EAAE,YAAY,QAAQ,IAAI,SAAS,KAAK;AACjD;","names":["webhookSecret","client","Stripe"]}
@@ -0,0 +1,3 @@
1
+ export { getStripe, verifyStripeWebhook, type StripeBundle, type CreateStripeClientOptions, } from "./server/client.js";
2
+ export { findOrCreateStripeCustomer, type StripeCustomerInput, type FindOrCreateResult, } from "./server/customer.js";
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,yBAAyB,GAC/B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,0BAA0B,EAC1B,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,sBAAsB,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,81 @@
1
+ // src/server/client.ts
2
+ import Stripe from "stripe";
3
+ import { getSecret } from "@augmenting-integrations/aws/server";
4
+ async function loadBundle(opts) {
5
+ if (process.env.STRIPE_API_KEY && process.env.STRIPE_WEBHOOK_SECRET) {
6
+ const apiKey = process.env.STRIPE_API_KEY;
7
+ const publishableKey = process.env.STRIPE_PUBLISHABLE_KEY ?? "";
8
+ const apiVersion = process.env.STRIPE_API_VERSION ?? "2025-09-30.clover";
9
+ const webhookSecret2 = process.env.STRIPE_WEBHOOK_SECRET;
10
+ const client2 = new Stripe(apiKey, {
11
+ apiVersion
12
+ });
13
+ return { client: client2, publishableKey, apiVersion, webhookSecret: webhookSecret2 };
14
+ }
15
+ const apiSecretArn = opts.apiSecretArn ?? process.env.STRIPE_API_SECRET_ARN;
16
+ const webhookSecretArn = opts.webhookSecretArn ?? process.env.STRIPE_WEBHOOK_SECRET_ARN;
17
+ if (!apiSecretArn || !webhookSecretArn) {
18
+ throw new Error(
19
+ `[billing] createStripeClient requires (STRIPE_API_KEY + STRIPE_WEBHOOK_SECRET) for local dev or both apiSecretArn + webhookSecretArn (or matching env vars) for Lambda. Got apiSecretArn=${!!apiSecretArn}, webhookSecretArn=${!!webhookSecretArn}.`
20
+ );
21
+ }
22
+ const [bundleJson, webhookSecret] = await Promise.all([
23
+ getSecret(apiSecretArn),
24
+ getSecret(webhookSecretArn)
25
+ ]);
26
+ if (!bundleJson || !webhookSecret) {
27
+ throw new Error(
28
+ `[billing] Stripe secrets could not be resolved (apiSecretArn=${apiSecretArn}, webhookSecretArn=${webhookSecretArn})`
29
+ );
30
+ }
31
+ const bundle = JSON.parse(bundleJson);
32
+ if (!bundle.apiKey || !bundle.publishableKey || !bundle.apiVersion) {
33
+ throw new Error(
34
+ `[billing] Stripe secret ${apiSecretArn} missing apiKey/publishableKey/apiVersion`
35
+ );
36
+ }
37
+ const client = new Stripe(bundle.apiKey, {
38
+ apiVersion: bundle.apiVersion
39
+ });
40
+ return {
41
+ client,
42
+ publishableKey: bundle.publishableKey,
43
+ apiVersion: bundle.apiVersion,
44
+ webhookSecret
45
+ };
46
+ }
47
+ function getStripe(opts = {}) {
48
+ globalThis.__stripeBillingCache ??= loadBundle(opts);
49
+ return globalThis.__stripeBillingCache;
50
+ }
51
+ async function verifyStripeWebhook(rawBody, signature, opts = {}) {
52
+ const { client, webhookSecret } = await getStripe(opts);
53
+ return client.webhooks.constructEvent(rawBody, signature, webhookSecret);
54
+ }
55
+
56
+ // src/server/customer.ts
57
+ async function findOrCreateStripeCustomer(input, stripe) {
58
+ if (input.existingId) {
59
+ try {
60
+ const existing = await stripe.customers.retrieve(input.existingId);
61
+ if (!existing.deleted) {
62
+ return { customerId: existing.id, created: false };
63
+ }
64
+ } catch (err) {
65
+ const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
66
+ if (code !== "resource_missing") throw err;
67
+ }
68
+ }
69
+ const created = await stripe.customers.create({
70
+ email: input.email,
71
+ name: input.name ?? void 0,
72
+ metadata: { app_user_id: input.appUserId }
73
+ });
74
+ return { customerId: created.id, created: true };
75
+ }
76
+ export {
77
+ findOrCreateStripeCustomer,
78
+ getStripe,
79
+ verifyStripeWebhook
80
+ };
81
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/client.ts","../src/server/customer.ts"],"sourcesContent":["import Stripe from \"stripe\";\nimport { getSecret } from \"@augmenting-integrations/aws/server\";\n\n// =============================================================================\n// Stripe client init.\n//\n// Production: API key, publishable key, and API version come bundled in a\n// single SecretsManager JSON blob (created by augint-example-infra) keyed by\n// `STRIPE_API_SECRET_ARN`. Webhook signing secret is a separate per-endpoint\n// secret at `STRIPE_WEBHOOK_SECRET_ARN`.\n//\n// Local dev: STRIPE_API_KEY / STRIPE_PUBLISHABLE_KEY / STRIPE_API_VERSION /\n// STRIPE_WEBHOOK_SECRET in .env are used directly.\n//\n// Cached on globalThis for warm-Lambda reuse + Next dev hot-reload.\n// =============================================================================\n\nexport type StripeBundle = {\n client: Stripe;\n publishableKey: string;\n apiVersion: string;\n webhookSecret: string;\n};\n\nexport type CreateStripeClientOptions = {\n /** ARN of the bundled `{apiKey, publishableKey, apiVersion}` JSON secret. */\n apiSecretArn?: string;\n /** ARN of the per-endpoint webhook signing secret. */\n webhookSecretArn?: string;\n};\n\ntype StripeApiBundle = {\n apiKey: string;\n publishableKey: string;\n apiVersion: string;\n};\n\ndeclare global {\n var __stripeBillingCache: Promise<StripeBundle> | undefined;\n}\n\nasync function loadBundle(opts: CreateStripeClientOptions): Promise<StripeBundle> {\n // Local dev short-circuit\n if (process.env.STRIPE_API_KEY && process.env.STRIPE_WEBHOOK_SECRET) {\n const apiKey = process.env.STRIPE_API_KEY;\n const publishableKey = process.env.STRIPE_PUBLISHABLE_KEY ?? \"\";\n const apiVersion = process.env.STRIPE_API_VERSION ?? \"2025-09-30.clover\";\n const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;\n const client = new Stripe(apiKey, {\n apiVersion: apiVersion as Stripe.LatestApiVersion,\n });\n return { client, publishableKey, apiVersion, webhookSecret };\n }\n\n const apiSecretArn = opts.apiSecretArn ?? process.env.STRIPE_API_SECRET_ARN;\n const webhookSecretArn = opts.webhookSecretArn ?? process.env.STRIPE_WEBHOOK_SECRET_ARN;\n if (!apiSecretArn || !webhookSecretArn) {\n throw new Error(\n `[billing] createStripeClient requires (STRIPE_API_KEY + STRIPE_WEBHOOK_SECRET) for local dev or both apiSecretArn + webhookSecretArn (or matching env vars) for Lambda. Got apiSecretArn=${!!apiSecretArn}, webhookSecretArn=${!!webhookSecretArn}.`,\n );\n }\n\n const [bundleJson, webhookSecret] = await Promise.all([\n getSecret(apiSecretArn),\n getSecret(webhookSecretArn),\n ]);\n if (!bundleJson || !webhookSecret) {\n throw new Error(\n `[billing] Stripe secrets could not be resolved (apiSecretArn=${apiSecretArn}, webhookSecretArn=${webhookSecretArn})`,\n );\n }\n const bundle = JSON.parse(bundleJson) as StripeApiBundle;\n if (!bundle.apiKey || !bundle.publishableKey || !bundle.apiVersion) {\n throw new Error(\n `[billing] Stripe secret ${apiSecretArn} missing apiKey/publishableKey/apiVersion`,\n );\n }\n const client = new Stripe(bundle.apiKey, {\n apiVersion: bundle.apiVersion as Stripe.LatestApiVersion,\n });\n return {\n client,\n publishableKey: bundle.publishableKey,\n apiVersion: bundle.apiVersion,\n webhookSecret,\n };\n}\n\n/**\n * Get-or-create the bundled Stripe client + publishable key + webhook secret.\n * Cached for the container lifetime. Pass options or rely on env vars.\n */\nexport function getStripe(opts: CreateStripeClientOptions = {}): Promise<StripeBundle> {\n globalThis.__stripeBillingCache ??= loadBundle(opts);\n return globalThis.__stripeBillingCache;\n}\n\n/**\n * Verify a Stripe webhook against the cached signing secret. Returns the\n * decoded Stripe.Event; throws if the signature doesn't match.\n */\nexport async function verifyStripeWebhook(\n rawBody: string,\n signature: string,\n opts: CreateStripeClientOptions = {},\n): Promise<Stripe.Event> {\n const { client, webhookSecret } = await getStripe(opts);\n return client.webhooks.constructEvent(rawBody, signature, webhookSecret);\n}\n","import type Stripe from \"stripe\";\n\n// =============================================================================\n// Stripe customer find-or-create.\n//\n// This is the library's pure-Stripe helper: it takes the customer-id the\n// caller has on file (typically User.stripe_customer_id), checks Stripe for\n// liveness, and creates a new one if missing or deleted. The caller is\n// responsible for persisting the (possibly new) id back to its User row.\n//\n// Pure of Prisma so it works with any DB layer.\n// =============================================================================\n\nexport type StripeCustomerInput = {\n /** Existing customer ID on the user (e.g. User.stripe_customer_id). */\n existingId?: string | null;\n email: string;\n name?: string | null;\n /**\n * Stable identifier baked into the customer's metadata. Typically the\n * stringified User.id; lets dashboard / webhook downstreams correlate.\n */\n appUserId: string;\n};\n\nexport type FindOrCreateResult = {\n /** The live (possibly newly-created) Stripe customer id. */\n customerId: string;\n /** True when a new customer was created on Stripe in this call. */\n created: boolean;\n};\n\n/**\n * Idempotently find-or-create a Stripe Customer. If `existingId` resolves to\n * a live (non-deleted) customer, returns that id. Otherwise creates a new\n * customer and returns its id with `created: true`.\n *\n * Caller is responsible for persisting `customerId` to its own DB when\n * `created` is true.\n */\nexport async function findOrCreateStripeCustomer(\n input: StripeCustomerInput,\n stripe: Stripe,\n): Promise<FindOrCreateResult> {\n if (input.existingId) {\n try {\n const existing = await stripe.customers.retrieve(input.existingId);\n if (!existing.deleted) {\n return { customerId: existing.id, created: false };\n }\n } catch (err: unknown) {\n const code =\n err && typeof err === \"object\" && \"code\" in err\n ? (err as { code: string }).code\n : undefined;\n if (code !== \"resource_missing\") throw err;\n // resource_missing -> fall through to recreate\n }\n }\n\n const created = await stripe.customers.create({\n email: input.email,\n name: input.name ?? undefined,\n metadata: { app_user_id: input.appUserId },\n });\n return { customerId: created.id, created: true };\n}\n"],"mappings":";AAAA,OAAO,YAAY;AACnB,SAAS,iBAAiB;AAwC1B,eAAe,WAAW,MAAwD;AAEhF,MAAI,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,uBAAuB;AACnE,UAAM,SAAS,QAAQ,IAAI;AAC3B,UAAM,iBAAiB,QAAQ,IAAI,0BAA0B;AAC7D,UAAM,aAAa,QAAQ,IAAI,sBAAsB;AACrD,UAAMA,iBAAgB,QAAQ,IAAI;AAClC,UAAMC,UAAS,IAAI,OAAO,QAAQ;AAAA,MAChC;AAAA,IACF,CAAC;AACD,WAAO,EAAE,QAAAA,SAAQ,gBAAgB,YAAY,eAAAD,eAAc;AAAA,EAC7D;AAEA,QAAM,eAAe,KAAK,gBAAgB,QAAQ,IAAI;AACtD,QAAM,mBAAmB,KAAK,oBAAoB,QAAQ,IAAI;AAC9D,MAAI,CAAC,gBAAgB,CAAC,kBAAkB;AACtC,UAAM,IAAI;AAAA,MACR,4LAA4L,CAAC,CAAC,YAAY,sBAAsB,CAAC,CAAC,gBAAgB;AAAA,IACpP;AAAA,EACF;AAEA,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,IACpD,UAAU,YAAY;AAAA,IACtB,UAAU,gBAAgB;AAAA,EAC5B,CAAC;AACD,MAAI,CAAC,cAAc,CAAC,eAAe;AACjC,UAAM,IAAI;AAAA,MACR,gEAAgE,YAAY,sBAAsB,gBAAgB;AAAA,IACpH;AAAA,EACF;AACA,QAAM,SAAS,KAAK,MAAM,UAAU;AACpC,MAAI,CAAC,OAAO,UAAU,CAAC,OAAO,kBAAkB,CAAC,OAAO,YAAY;AAClE,UAAM,IAAI;AAAA,MACR,2BAA2B,YAAY;AAAA,IACzC;AAAA,EACF;AACA,QAAM,SAAS,IAAI,OAAO,OAAO,QAAQ;AAAA,IACvC,YAAY,OAAO;AAAA,EACrB,CAAC;AACD,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,OAAO;AAAA,IACvB,YAAY,OAAO;AAAA,IACnB;AAAA,EACF;AACF;AAMO,SAAS,UAAU,OAAkC,CAAC,GAA0B;AACrF,aAAW,yBAAyB,WAAW,IAAI;AACnD,SAAO,WAAW;AACpB;AAMA,eAAsB,oBACpB,SACA,WACA,OAAkC,CAAC,GACZ;AACvB,QAAM,EAAE,QAAQ,cAAc,IAAI,MAAM,UAAU,IAAI;AACtD,SAAO,OAAO,SAAS,eAAe,SAAS,WAAW,aAAa;AACzE;;;ACpEA,eAAsB,2BACpB,OACA,QAC6B;AAC7B,MAAI,MAAM,YAAY;AACpB,QAAI;AACF,YAAM,WAAW,MAAM,OAAO,UAAU,SAAS,MAAM,UAAU;AACjE,UAAI,CAAC,SAAS,SAAS;AACrB,eAAO,EAAE,YAAY,SAAS,IAAI,SAAS,MAAM;AAAA,MACnD;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,OACJ,OAAO,OAAO,QAAQ,YAAY,UAAU,MACvC,IAAyB,OAC1B;AACN,UAAI,SAAS,mBAAoB,OAAM;AAAA,IAEzC;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,OAAO,UAAU,OAAO;AAAA,IAC5C,OAAO,MAAM;AAAA,IACb,MAAM,MAAM,QAAQ;AAAA,IACpB,UAAU,EAAE,aAAa,MAAM,UAAU;AAAA,EAC3C,CAAC;AACD,SAAO,EAAE,YAAY,QAAQ,IAAI,SAAS,KAAK;AACjD;","names":["webhookSecret","client"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@augmenting-integrations/billing",
3
+ "version": "0.0.1",
4
+ "description": "Stripe primitives for augint product apps: secret-bundled client init, idempotent customer creation, webhook signature verification, plus client-side React widgets (Elements provider, PaymentMethodSelect, CreditBalanceBadge, useCart with localStorage persistence).",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "sideEffects": false,
10
+ "main": "./dist/server.cjs",
11
+ "module": "./dist/server.js",
12
+ "types": "./dist/server.d.ts",
13
+ "exports": {
14
+ "./server": {
15
+ "types": "./dist/server.d.ts",
16
+ "import": "./dist/server.js",
17
+ "require": "./dist/server.cjs"
18
+ },
19
+ "./client": {
20
+ "types": "./dist/client.d.ts",
21
+ "import": "./dist/client.js",
22
+ "require": "./dist/client.cjs"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "clean": "rm -rf dist",
32
+ "test": "vitest run --passWithNoTests"
33
+ },
34
+ "dependencies": {
35
+ "stripe": "^17.0.0"
36
+ },
37
+ "peerDependencies": {
38
+ "@augmenting-integrations/aws": "workspace:*",
39
+ "@augmenting-integrations/ui": "workspace:*",
40
+ "@stripe/react-stripe-js": "^3.0.0",
41
+ "@stripe/stripe-js": "^4.0.0",
42
+ "react": "^19.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@augmenting-integrations/aws": "workspace:*",
46
+ "@augmenting-integrations/ui": "workspace:*",
47
+ "@stripe/react-stripe-js": "^3.0.0",
48
+ "@stripe/stripe-js": "^4.0.0",
49
+ "@types/react": "^19.0.0",
50
+ "react": "^19.0.0",
51
+ "tsup": "^8.3.5",
52
+ "typescript": "^5.7.2",
53
+ "vitest": "^4.1.5"
54
+ }
55
+ }