@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 +29 -0
- package/dist/client/CreditBalanceBadge.d.ts +7 -0
- package/dist/client/CreditBalanceBadge.d.ts.map +1 -0
- package/dist/client/PaymentMethodSelect.d.ts +17 -0
- package/dist/client/PaymentMethodSelect.d.ts.map +1 -0
- package/dist/client/StripeProvider.d.ts +8 -0
- package/dist/client/StripeProvider.d.ts.map +1 -0
- package/dist/client/stripe-loader.d.ts +3 -0
- package/dist/client/stripe-loader.d.ts.map +1 -0
- package/dist/client/useCart.d.ts +27 -0
- package/dist/client/useCart.d.ts.map +1 -0
- package/dist/client.cjs +41 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +15 -0
- package/dist/client.js.map +1 -0
- package/dist/server/client.d.ts +27 -0
- package/dist/server/client.d.ts.map +1 -0
- package/dist/server/customer.d.ts +28 -0
- package/dist/server/customer.d.ts.map +1 -0
- package/dist/server.cjs +120 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +81 -0
- package/dist/server.js.map +1 -0
- package/package.json +55 -0
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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/client.cjs
ADDED
|
@@ -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":[]}
|
package/dist/client.d.ts
ADDED
|
@@ -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"}
|
package/dist/server.cjs
ADDED
|
@@ -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"]}
|
package/dist/server.d.ts
ADDED
|
@@ -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
|
+
}
|