@spaire/express 1.0.1 → 2.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,175 @@
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
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Checkout: () => Checkout,
24
+ CustomerPortal: () => CustomerPortal,
25
+ EntitlementStrategy: () => import_adapter_utils2.EntitlementStrategy,
26
+ Entitlements: () => import_adapter_utils2.Entitlements,
27
+ Webhooks: () => Webhooks
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/checkout/checkout.ts
32
+ var import_sdk = require("@spaire/sdk");
33
+ var Checkout = ({
34
+ accessToken,
35
+ successUrl,
36
+ returnUrl,
37
+ server,
38
+ theme,
39
+ includeCheckoutId = true
40
+ }) => {
41
+ const spaire = new import_sdk.Spaire({
42
+ accessToken: accessToken ?? process.env["SPAIRE_ACCESS_TOKEN"],
43
+ server
44
+ });
45
+ return async (req, res) => {
46
+ const url = new URL(
47
+ `${req.protocol}://${req.get("host")}${req.originalUrl}`
48
+ );
49
+ const products = url.searchParams.getAll("products");
50
+ if (products.length === 0) {
51
+ res.status(400).json({
52
+ error: "Missing products in query params"
53
+ });
54
+ return;
55
+ }
56
+ const success = successUrl ? new URL(successUrl) : void 0;
57
+ if (success && includeCheckoutId) {
58
+ success.searchParams.set("checkoutId", "{CHECKOUT_ID}");
59
+ }
60
+ const retUrl = returnUrl ? new URL(returnUrl) : void 0;
61
+ try {
62
+ const result = await spaire.checkouts.create({
63
+ products,
64
+ successUrl: success ? decodeURI(success.toString()) : void 0,
65
+ customerId: url.searchParams.get("customerId") ?? void 0,
66
+ externalCustomerId: url.searchParams.get("customerExternalId") ?? void 0,
67
+ customerEmail: url.searchParams.get("customerEmail") ?? void 0,
68
+ customerName: url.searchParams.get("customerName") ?? void 0,
69
+ customerBillingAddress: url.searchParams.has("customerBillingAddress") ? JSON.parse(url.searchParams.get("customerBillingAddress") ?? "{}") : void 0,
70
+ customerTaxId: url.searchParams.get("customerTaxId") ?? void 0,
71
+ customerIpAddress: url.searchParams.get("customerIpAddress") ?? void 0,
72
+ customerMetadata: url.searchParams.has("customerMetadata") ? JSON.parse(url.searchParams.get("customerMetadata") ?? "{}") : void 0,
73
+ allowDiscountCodes: url.searchParams.has("allowDiscountCodes") ? url.searchParams.get("allowDiscountCodes") === "true" : void 0,
74
+ discountId: url.searchParams.get("discountId") ?? void 0,
75
+ metadata: url.searchParams.has("metadata") ? JSON.parse(url.searchParams.get("metadata") ?? "{}") : void 0,
76
+ seats: url.searchParams.has("seats") ? Number.parseInt(url.searchParams.get("seats") ?? "1", 10) : void 0,
77
+ returnUrl: retUrl ? decodeURI(retUrl.toString()) : void 0
78
+ });
79
+ const redirectUrl = new URL(result.url);
80
+ if (theme) {
81
+ redirectUrl.searchParams.set("theme", theme);
82
+ }
83
+ res.redirect(redirectUrl.toString());
84
+ } catch (error) {
85
+ console.error(error);
86
+ res.status(500).json({ error: "Internal server error" });
87
+ }
88
+ };
89
+ };
90
+
91
+ // src/customerPortal/customerPortal.ts
92
+ var import_sdk2 = require("@spaire/sdk");
93
+ var CustomerPortal = ({
94
+ accessToken,
95
+ server,
96
+ getCustomerId,
97
+ returnUrl
98
+ }) => {
99
+ const spaire = new import_sdk2.Spaire({
100
+ accessToken: accessToken ?? process.env["SPAIRE_ACCESS_TOKEN"],
101
+ server
102
+ });
103
+ return async (req, res) => {
104
+ const retUrl = returnUrl ? new URL(returnUrl) : void 0;
105
+ const customerId = await getCustomerId(req);
106
+ if (!customerId) {
107
+ res.status(400).json({ error: "customerId not defined" });
108
+ return;
109
+ }
110
+ try {
111
+ const result = await spaire.customerSessions.create({
112
+ customerId,
113
+ returnUrl: retUrl ? decodeURI(retUrl.toString()) : void 0
114
+ });
115
+ res.redirect(result.customerPortalUrl);
116
+ return;
117
+ } catch (error) {
118
+ console.error(error);
119
+ res.status(500).json({ error: "Internal server error" });
120
+ return;
121
+ }
122
+ };
123
+ };
124
+
125
+ // src/webhooks/webhooks.ts
126
+ var import_adapter_utils = require("@spaire/adapter-utils");
127
+ var import_webhooks = require("@spaire/sdk/webhooks");
128
+ var import_adapter_utils2 = require("@spaire/adapter-utils");
129
+ var Webhooks = ({
130
+ webhookSecret,
131
+ onPayload,
132
+ entitlements,
133
+ ...eventHandlers
134
+ }) => {
135
+ return async (req, res) => {
136
+ const requestBody = JSON.stringify(req.body);
137
+ const webhookHeaders = {
138
+ "webhook-id": req.headers["webhook-id"],
139
+ "webhook-timestamp": req.headers["webhook-timestamp"],
140
+ "webhook-signature": req.headers["webhook-signature"]
141
+ };
142
+ let webhookPayload;
143
+ try {
144
+ webhookPayload = (0, import_webhooks.validateEvent)(
145
+ requestBody,
146
+ webhookHeaders,
147
+ webhookSecret
148
+ );
149
+ } catch (error) {
150
+ console.log(error);
151
+ if (error instanceof import_webhooks.WebhookVerificationError) {
152
+ res.status(403).json({ received: false });
153
+ return;
154
+ }
155
+ res.status(500).json({ error: "Internal server error" });
156
+ return;
157
+ }
158
+ await (0, import_adapter_utils.handleWebhookPayload)(webhookPayload, {
159
+ webhookSecret,
160
+ entitlements,
161
+ onPayload,
162
+ ...eventHandlers
163
+ });
164
+ res.json({ received: true });
165
+ };
166
+ };
167
+ // Annotate the CommonJS export names for ESM import in node:
168
+ 0 && (module.exports = {
169
+ Checkout,
170
+ CustomerPortal,
171
+ EntitlementStrategy,
172
+ Entitlements,
173
+ Webhooks
174
+ });
175
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/checkout/checkout.ts","../src/customerPortal/customerPortal.ts","../src/webhooks/webhooks.ts"],"sourcesContent":["export * from \"./checkout/checkout\";\nexport * from \"./customerPortal/customerPortal\";\nexport * from \"./webhooks/webhooks\";\n","import { Spaire } from \"@spaire/sdk\";\nimport type { Request, Response } from \"express\";\n\nexport interface CheckoutConfig {\n\taccessToken?: string;\n\tsuccessUrl?: string;\n\treturnUrl?: string;\n\tincludeCheckoutId?: boolean;\n\tserver?: \"sandbox\" | \"production\";\n\ttheme?: \"light\" | \"dark\";\n}\n\nexport const Checkout = ({\n\taccessToken,\n\tsuccessUrl,\n\treturnUrl,\n\tserver,\n\ttheme,\n\tincludeCheckoutId = true,\n}: CheckoutConfig) => {\n\tconst spaire = new Spaire({\n\t\taccessToken: accessToken ?? process.env[\"SPAIRE_ACCESS_TOKEN\"],\n\t\tserver,\n\t});\n\n\treturn async (req: Request, res: Response) => {\n\t\tconst url = new URL(\n\t\t\t`${req.protocol}://${req.get(\"host\")}${req.originalUrl}`,\n\t\t);\n\t\tconst products = url.searchParams.getAll(\"products\");\n\n\t\tif (products.length === 0) {\n\t\t\tres.status(400).json({\n\t\t\t\terror: \"Missing products in query params\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst success = successUrl ? new URL(successUrl) : undefined;\n\n\t\tif (success && includeCheckoutId) {\n\t\t\tsuccess.searchParams.set(\"checkoutId\", \"{CHECKOUT_ID}\");\n\t\t}\n\n\t\tconst retUrl = returnUrl ? new URL(returnUrl) : undefined;\n\n\t\ttry {\n\t\t\tconst result = await spaire.checkouts.create({\n\t\t\t\tproducts,\n\t\t\t\tsuccessUrl: success ? decodeURI(success.toString()) : undefined,\n\t\t\t\tcustomerId: url.searchParams.get(\"customerId\") ?? undefined,\n\t\t\t\texternalCustomerId:\n\t\t\t\t\turl.searchParams.get(\"customerExternalId\") ?? undefined,\n\t\t\t\tcustomerEmail: url.searchParams.get(\"customerEmail\") ?? undefined,\n\t\t\t\tcustomerName: url.searchParams.get(\"customerName\") ?? undefined,\n\t\t\t\tcustomerBillingAddress: url.searchParams.has(\"customerBillingAddress\")\n\t\t\t\t\t? JSON.parse(url.searchParams.get(\"customerBillingAddress\") ?? \"{}\")\n\t\t\t\t\t: undefined,\n\t\t\t\tcustomerTaxId: url.searchParams.get(\"customerTaxId\") ?? undefined,\n\t\t\t\tcustomerIpAddress:\n\t\t\t\t\turl.searchParams.get(\"customerIpAddress\") ?? undefined,\n\t\t\t\tcustomerMetadata: url.searchParams.has(\"customerMetadata\")\n\t\t\t\t\t? JSON.parse(url.searchParams.get(\"customerMetadata\") ?? \"{}\")\n\t\t\t\t\t: undefined,\n\t\t\t\tallowDiscountCodes: url.searchParams.has(\"allowDiscountCodes\")\n\t\t\t\t\t? url.searchParams.get(\"allowDiscountCodes\") === \"true\"\n\t\t\t\t\t: undefined,\n\t\t\t\tdiscountId: url.searchParams.get(\"discountId\") ?? undefined,\n\t\t\t\tmetadata: url.searchParams.has(\"metadata\")\n\t\t\t\t\t? JSON.parse(url.searchParams.get(\"metadata\") ?? \"{}\")\n\t\t\t\t\t: undefined,\n\t\t\t\tseats: url.searchParams.has(\"seats\")\n\t\t\t\t\t? Number.parseInt(url.searchParams.get(\"seats\") ?? \"1\", 10)\n\t\t\t\t\t: undefined,\n\t\t\t\treturnUrl: retUrl ? decodeURI(retUrl.toString()) : undefined,\n\t\t\t});\n\n\t\t\tconst redirectUrl = new URL(result.url);\n\n\t\t\tif (theme) {\n\t\t\t\tredirectUrl.searchParams.set(\"theme\", theme);\n\t\t\t}\n\n\t\t\tres.redirect(redirectUrl.toString());\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\tres.status(500).json({ error: \"Internal server error\" });\n\t\t}\n\t};\n};\n","import { Spaire } from \"@spaire/sdk\";\nimport type { Request, Response } from \"express\";\n\nexport interface CustomerPortalConfig {\n\taccessToken?: string;\n\tgetCustomerId: (req: Request) => Promise<string>;\n\tserver?: \"sandbox\" | \"production\";\n\treturnUrl?: string;\n}\n\nexport const CustomerPortal = ({\n\taccessToken,\n\tserver,\n\tgetCustomerId,\n\treturnUrl,\n}: CustomerPortalConfig) => {\n\tconst spaire = new Spaire({\n\t\taccessToken: accessToken ?? process.env[\"SPAIRE_ACCESS_TOKEN\"],\n\t\tserver,\n\t});\n\n\treturn async (req: Request, res: Response) => {\n\t\tconst retUrl = returnUrl ? new URL(returnUrl) : undefined;\n\n\t\tconst customerId = await getCustomerId(req);\n\n\t\tif (!customerId) {\n\t\t\tres.status(400).json({ error: \"customerId not defined\" });\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await spaire.customerSessions.create({\n\t\t\t\tcustomerId,\n\t\t\t\treturnUrl: retUrl ? decodeURI(retUrl.toString()) : undefined,\n\t\t\t});\n\n\t\t\tres.redirect(result.customerPortalUrl);\n\t\t\treturn;\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\tres.status(500).json({ error: \"Internal server error\" });\n\t\t\treturn;\n\t\t}\n\t};\n};\n","import {\n\ttype WebhooksConfig,\n\thandleWebhookPayload,\n} from \"@spaire/adapter-utils\";\nimport { WebhookVerificationError, validateEvent } from \"@spaire/sdk/webhooks\";\nimport type { Request, RequestHandler, Response } from \"express\";\n\nexport {\n\ttype EntitlementContext,\n\ttype EntitlementHandler,\n\ttype EntitlementProperties,\n\tEntitlementStrategy,\n\tEntitlements,\n} from \"@spaire/adapter-utils\";\n\nexport const Webhooks = ({\n\twebhookSecret,\n\tonPayload,\n\tentitlements,\n\t...eventHandlers\n}: WebhooksConfig): RequestHandler => {\n\treturn async (req: Request, res: Response) => {\n\t\tconst requestBody = JSON.stringify(req.body);\n\n\t\tconst webhookHeaders: Record<string, string> = {\n\t\t\t\"webhook-id\": req.headers[\"webhook-id\"] as string,\n\t\t\t\"webhook-timestamp\": req.headers[\"webhook-timestamp\"] as string,\n\t\t\t\"webhook-signature\": req.headers[\"webhook-signature\"] as string,\n\t\t};\n\n\t\tlet webhookPayload: ReturnType<typeof validateEvent>;\n\t\ttry {\n\t\t\twebhookPayload = validateEvent(\n\t\t\t\trequestBody,\n\t\t\t\twebhookHeaders,\n\t\t\t\twebhookSecret,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.log(error);\n\t\t\tif (error instanceof WebhookVerificationError) {\n\t\t\t\tres.status(403).json({ received: false });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.status(500).json({ error: \"Internal server error\" });\n\t\t\treturn;\n\t\t}\n\n\t\tawait handleWebhookPayload(webhookPayload, {\n\t\t\twebhookSecret,\n\t\t\tentitlements,\n\t\t\tonPayload,\n\t\t\t...eventHandlers,\n\t\t});\n\n\t\tres.json({ received: true });\n\t};\n};\n"],"mappings":"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","names":["import_sdk","import_adapter_utils"]}
@@ -0,0 +1,25 @@
1
+ import { Request, Response, RequestHandler } from 'express';
2
+ import { WebhooksConfig } from '@spaire/adapter-utils';
3
+ export { EntitlementContext, EntitlementHandler, EntitlementProperties, EntitlementStrategy, Entitlements } from '@spaire/adapter-utils';
4
+
5
+ interface CheckoutConfig {
6
+ accessToken?: string;
7
+ successUrl?: string;
8
+ returnUrl?: string;
9
+ includeCheckoutId?: boolean;
10
+ server?: "sandbox" | "production";
11
+ theme?: "light" | "dark";
12
+ }
13
+ declare const Checkout: ({ accessToken, successUrl, returnUrl, server, theme, includeCheckoutId, }: CheckoutConfig) => (req: Request, res: Response) => Promise<void>;
14
+
15
+ interface CustomerPortalConfig {
16
+ accessToken?: string;
17
+ getCustomerId: (req: Request) => Promise<string>;
18
+ server?: "sandbox" | "production";
19
+ returnUrl?: string;
20
+ }
21
+ declare const CustomerPortal: ({ accessToken, server, getCustomerId, returnUrl, }: CustomerPortalConfig) => (req: Request, res: Response) => Promise<void>;
22
+
23
+ declare const Webhooks: ({ webhookSecret, onPayload, entitlements, ...eventHandlers }: WebhooksConfig) => RequestHandler;
24
+
25
+ export { Checkout, type CheckoutConfig, CustomerPortal, type CustomerPortalConfig, Webhooks };
@@ -0,0 +1,25 @@
1
+ import { Request, Response, RequestHandler } from 'express';
2
+ import { WebhooksConfig } from '@spaire/adapter-utils';
3
+ export { EntitlementContext, EntitlementHandler, EntitlementProperties, EntitlementStrategy, Entitlements } from '@spaire/adapter-utils';
4
+
5
+ interface CheckoutConfig {
6
+ accessToken?: string;
7
+ successUrl?: string;
8
+ returnUrl?: string;
9
+ includeCheckoutId?: boolean;
10
+ server?: "sandbox" | "production";
11
+ theme?: "light" | "dark";
12
+ }
13
+ declare const Checkout: ({ accessToken, successUrl, returnUrl, server, theme, includeCheckoutId, }: CheckoutConfig) => (req: Request, res: Response) => Promise<void>;
14
+
15
+ interface CustomerPortalConfig {
16
+ accessToken?: string;
17
+ getCustomerId: (req: Request) => Promise<string>;
18
+ server?: "sandbox" | "production";
19
+ returnUrl?: string;
20
+ }
21
+ declare const CustomerPortal: ({ accessToken, server, getCustomerId, returnUrl, }: CustomerPortalConfig) => (req: Request, res: Response) => Promise<void>;
22
+
23
+ declare const Webhooks: ({ webhookSecret, onPayload, entitlements, ...eventHandlers }: WebhooksConfig) => RequestHandler;
24
+
25
+ export { Checkout, type CheckoutConfig, CustomerPortal, type CustomerPortalConfig, Webhooks };
package/dist/index.js ADDED
@@ -0,0 +1,149 @@
1
+ // src/checkout/checkout.ts
2
+ import { Spaire } from "@spaire/sdk";
3
+ var Checkout = ({
4
+ accessToken,
5
+ successUrl,
6
+ returnUrl,
7
+ server,
8
+ theme,
9
+ includeCheckoutId = true
10
+ }) => {
11
+ const spaire = new Spaire({
12
+ accessToken: accessToken ?? process.env["SPAIRE_ACCESS_TOKEN"],
13
+ server
14
+ });
15
+ return async (req, res) => {
16
+ const url = new URL(
17
+ `${req.protocol}://${req.get("host")}${req.originalUrl}`
18
+ );
19
+ const products = url.searchParams.getAll("products");
20
+ if (products.length === 0) {
21
+ res.status(400).json({
22
+ error: "Missing products in query params"
23
+ });
24
+ return;
25
+ }
26
+ const success = successUrl ? new URL(successUrl) : void 0;
27
+ if (success && includeCheckoutId) {
28
+ success.searchParams.set("checkoutId", "{CHECKOUT_ID}");
29
+ }
30
+ const retUrl = returnUrl ? new URL(returnUrl) : void 0;
31
+ try {
32
+ const result = await spaire.checkouts.create({
33
+ products,
34
+ successUrl: success ? decodeURI(success.toString()) : void 0,
35
+ customerId: url.searchParams.get("customerId") ?? void 0,
36
+ externalCustomerId: url.searchParams.get("customerExternalId") ?? void 0,
37
+ customerEmail: url.searchParams.get("customerEmail") ?? void 0,
38
+ customerName: url.searchParams.get("customerName") ?? void 0,
39
+ customerBillingAddress: url.searchParams.has("customerBillingAddress") ? JSON.parse(url.searchParams.get("customerBillingAddress") ?? "{}") : void 0,
40
+ customerTaxId: url.searchParams.get("customerTaxId") ?? void 0,
41
+ customerIpAddress: url.searchParams.get("customerIpAddress") ?? void 0,
42
+ customerMetadata: url.searchParams.has("customerMetadata") ? JSON.parse(url.searchParams.get("customerMetadata") ?? "{}") : void 0,
43
+ allowDiscountCodes: url.searchParams.has("allowDiscountCodes") ? url.searchParams.get("allowDiscountCodes") === "true" : void 0,
44
+ discountId: url.searchParams.get("discountId") ?? void 0,
45
+ metadata: url.searchParams.has("metadata") ? JSON.parse(url.searchParams.get("metadata") ?? "{}") : void 0,
46
+ seats: url.searchParams.has("seats") ? Number.parseInt(url.searchParams.get("seats") ?? "1", 10) : void 0,
47
+ returnUrl: retUrl ? decodeURI(retUrl.toString()) : void 0
48
+ });
49
+ const redirectUrl = new URL(result.url);
50
+ if (theme) {
51
+ redirectUrl.searchParams.set("theme", theme);
52
+ }
53
+ res.redirect(redirectUrl.toString());
54
+ } catch (error) {
55
+ console.error(error);
56
+ res.status(500).json({ error: "Internal server error" });
57
+ }
58
+ };
59
+ };
60
+
61
+ // src/customerPortal/customerPortal.ts
62
+ import { Spaire as Spaire2 } from "@spaire/sdk";
63
+ var CustomerPortal = ({
64
+ accessToken,
65
+ server,
66
+ getCustomerId,
67
+ returnUrl
68
+ }) => {
69
+ const spaire = new Spaire2({
70
+ accessToken: accessToken ?? process.env["SPAIRE_ACCESS_TOKEN"],
71
+ server
72
+ });
73
+ return async (req, res) => {
74
+ const retUrl = returnUrl ? new URL(returnUrl) : void 0;
75
+ const customerId = await getCustomerId(req);
76
+ if (!customerId) {
77
+ res.status(400).json({ error: "customerId not defined" });
78
+ return;
79
+ }
80
+ try {
81
+ const result = await spaire.customerSessions.create({
82
+ customerId,
83
+ returnUrl: retUrl ? decodeURI(retUrl.toString()) : void 0
84
+ });
85
+ res.redirect(result.customerPortalUrl);
86
+ return;
87
+ } catch (error) {
88
+ console.error(error);
89
+ res.status(500).json({ error: "Internal server error" });
90
+ return;
91
+ }
92
+ };
93
+ };
94
+
95
+ // src/webhooks/webhooks.ts
96
+ import {
97
+ handleWebhookPayload
98
+ } from "@spaire/adapter-utils";
99
+ import { WebhookVerificationError, validateEvent } from "@spaire/sdk/webhooks";
100
+ import {
101
+ EntitlementStrategy,
102
+ Entitlements
103
+ } from "@spaire/adapter-utils";
104
+ var Webhooks = ({
105
+ webhookSecret,
106
+ onPayload,
107
+ entitlements,
108
+ ...eventHandlers
109
+ }) => {
110
+ return async (req, res) => {
111
+ const requestBody = JSON.stringify(req.body);
112
+ const webhookHeaders = {
113
+ "webhook-id": req.headers["webhook-id"],
114
+ "webhook-timestamp": req.headers["webhook-timestamp"],
115
+ "webhook-signature": req.headers["webhook-signature"]
116
+ };
117
+ let webhookPayload;
118
+ try {
119
+ webhookPayload = validateEvent(
120
+ requestBody,
121
+ webhookHeaders,
122
+ webhookSecret
123
+ );
124
+ } catch (error) {
125
+ console.log(error);
126
+ if (error instanceof WebhookVerificationError) {
127
+ res.status(403).json({ received: false });
128
+ return;
129
+ }
130
+ res.status(500).json({ error: "Internal server error" });
131
+ return;
132
+ }
133
+ await handleWebhookPayload(webhookPayload, {
134
+ webhookSecret,
135
+ entitlements,
136
+ onPayload,
137
+ ...eventHandlers
138
+ });
139
+ res.json({ received: true });
140
+ };
141
+ };
142
+ export {
143
+ Checkout,
144
+ CustomerPortal,
145
+ EntitlementStrategy,
146
+ Entitlements,
147
+ Webhooks
148
+ };
149
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/checkout/checkout.ts","../src/customerPortal/customerPortal.ts","../src/webhooks/webhooks.ts"],"sourcesContent":["import { Spaire } from \"@spaire/sdk\";\nimport type { Request, Response } from \"express\";\n\nexport interface CheckoutConfig {\n\taccessToken?: string;\n\tsuccessUrl?: string;\n\treturnUrl?: string;\n\tincludeCheckoutId?: boolean;\n\tserver?: \"sandbox\" | \"production\";\n\ttheme?: \"light\" | \"dark\";\n}\n\nexport const Checkout = ({\n\taccessToken,\n\tsuccessUrl,\n\treturnUrl,\n\tserver,\n\ttheme,\n\tincludeCheckoutId = true,\n}: CheckoutConfig) => {\n\tconst spaire = new Spaire({\n\t\taccessToken: accessToken ?? process.env[\"SPAIRE_ACCESS_TOKEN\"],\n\t\tserver,\n\t});\n\n\treturn async (req: Request, res: Response) => {\n\t\tconst url = new URL(\n\t\t\t`${req.protocol}://${req.get(\"host\")}${req.originalUrl}`,\n\t\t);\n\t\tconst products = url.searchParams.getAll(\"products\");\n\n\t\tif (products.length === 0) {\n\t\t\tres.status(400).json({\n\t\t\t\terror: \"Missing products in query params\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst success = successUrl ? new URL(successUrl) : undefined;\n\n\t\tif (success && includeCheckoutId) {\n\t\t\tsuccess.searchParams.set(\"checkoutId\", \"{CHECKOUT_ID}\");\n\t\t}\n\n\t\tconst retUrl = returnUrl ? new URL(returnUrl) : undefined;\n\n\t\ttry {\n\t\t\tconst result = await spaire.checkouts.create({\n\t\t\t\tproducts,\n\t\t\t\tsuccessUrl: success ? decodeURI(success.toString()) : undefined,\n\t\t\t\tcustomerId: url.searchParams.get(\"customerId\") ?? undefined,\n\t\t\t\texternalCustomerId:\n\t\t\t\t\turl.searchParams.get(\"customerExternalId\") ?? undefined,\n\t\t\t\tcustomerEmail: url.searchParams.get(\"customerEmail\") ?? undefined,\n\t\t\t\tcustomerName: url.searchParams.get(\"customerName\") ?? undefined,\n\t\t\t\tcustomerBillingAddress: url.searchParams.has(\"customerBillingAddress\")\n\t\t\t\t\t? JSON.parse(url.searchParams.get(\"customerBillingAddress\") ?? \"{}\")\n\t\t\t\t\t: undefined,\n\t\t\t\tcustomerTaxId: url.searchParams.get(\"customerTaxId\") ?? undefined,\n\t\t\t\tcustomerIpAddress:\n\t\t\t\t\turl.searchParams.get(\"customerIpAddress\") ?? undefined,\n\t\t\t\tcustomerMetadata: url.searchParams.has(\"customerMetadata\")\n\t\t\t\t\t? JSON.parse(url.searchParams.get(\"customerMetadata\") ?? \"{}\")\n\t\t\t\t\t: undefined,\n\t\t\t\tallowDiscountCodes: url.searchParams.has(\"allowDiscountCodes\")\n\t\t\t\t\t? url.searchParams.get(\"allowDiscountCodes\") === \"true\"\n\t\t\t\t\t: undefined,\n\t\t\t\tdiscountId: url.searchParams.get(\"discountId\") ?? undefined,\n\t\t\t\tmetadata: url.searchParams.has(\"metadata\")\n\t\t\t\t\t? JSON.parse(url.searchParams.get(\"metadata\") ?? \"{}\")\n\t\t\t\t\t: undefined,\n\t\t\t\tseats: url.searchParams.has(\"seats\")\n\t\t\t\t\t? Number.parseInt(url.searchParams.get(\"seats\") ?? \"1\", 10)\n\t\t\t\t\t: undefined,\n\t\t\t\treturnUrl: retUrl ? decodeURI(retUrl.toString()) : undefined,\n\t\t\t});\n\n\t\t\tconst redirectUrl = new URL(result.url);\n\n\t\t\tif (theme) {\n\t\t\t\tredirectUrl.searchParams.set(\"theme\", theme);\n\t\t\t}\n\n\t\t\tres.redirect(redirectUrl.toString());\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\tres.status(500).json({ error: \"Internal server error\" });\n\t\t}\n\t};\n};\n","import { Spaire } from \"@spaire/sdk\";\nimport type { Request, Response } from \"express\";\n\nexport interface CustomerPortalConfig {\n\taccessToken?: string;\n\tgetCustomerId: (req: Request) => Promise<string>;\n\tserver?: \"sandbox\" | \"production\";\n\treturnUrl?: string;\n}\n\nexport const CustomerPortal = ({\n\taccessToken,\n\tserver,\n\tgetCustomerId,\n\treturnUrl,\n}: CustomerPortalConfig) => {\n\tconst spaire = new Spaire({\n\t\taccessToken: accessToken ?? process.env[\"SPAIRE_ACCESS_TOKEN\"],\n\t\tserver,\n\t});\n\n\treturn async (req: Request, res: Response) => {\n\t\tconst retUrl = returnUrl ? new URL(returnUrl) : undefined;\n\n\t\tconst customerId = await getCustomerId(req);\n\n\t\tif (!customerId) {\n\t\t\tres.status(400).json({ error: \"customerId not defined\" });\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await spaire.customerSessions.create({\n\t\t\t\tcustomerId,\n\t\t\t\treturnUrl: retUrl ? decodeURI(retUrl.toString()) : undefined,\n\t\t\t});\n\n\t\t\tres.redirect(result.customerPortalUrl);\n\t\t\treturn;\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\tres.status(500).json({ error: \"Internal server error\" });\n\t\t\treturn;\n\t\t}\n\t};\n};\n","import {\n\ttype WebhooksConfig,\n\thandleWebhookPayload,\n} from \"@spaire/adapter-utils\";\nimport { WebhookVerificationError, validateEvent } from \"@spaire/sdk/webhooks\";\nimport type { Request, RequestHandler, Response } from \"express\";\n\nexport {\n\ttype EntitlementContext,\n\ttype EntitlementHandler,\n\ttype EntitlementProperties,\n\tEntitlementStrategy,\n\tEntitlements,\n} from \"@spaire/adapter-utils\";\n\nexport const Webhooks = ({\n\twebhookSecret,\n\tonPayload,\n\tentitlements,\n\t...eventHandlers\n}: WebhooksConfig): RequestHandler => {\n\treturn async (req: Request, res: Response) => {\n\t\tconst requestBody = JSON.stringify(req.body);\n\n\t\tconst webhookHeaders: Record<string, string> = {\n\t\t\t\"webhook-id\": req.headers[\"webhook-id\"] as string,\n\t\t\t\"webhook-timestamp\": req.headers[\"webhook-timestamp\"] as string,\n\t\t\t\"webhook-signature\": req.headers[\"webhook-signature\"] as string,\n\t\t};\n\n\t\tlet webhookPayload: ReturnType<typeof validateEvent>;\n\t\ttry {\n\t\t\twebhookPayload = validateEvent(\n\t\t\t\trequestBody,\n\t\t\t\twebhookHeaders,\n\t\t\t\twebhookSecret,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.log(error);\n\t\t\tif (error instanceof WebhookVerificationError) {\n\t\t\t\tres.status(403).json({ received: false });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.status(500).json({ error: \"Internal server error\" });\n\t\t\treturn;\n\t\t}\n\n\t\tawait handleWebhookPayload(webhookPayload, {\n\t\t\twebhookSecret,\n\t\t\tentitlements,\n\t\t\tonPayload,\n\t\t\t...eventHandlers,\n\t\t});\n\n\t\tres.json({ received: true });\n\t};\n};\n"],"mappings":"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","names":["Spaire"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaire/express",
3
- "version": "1.0.1",
3
+ "version": "2.0.0",
4
4
  "description": "Spaire integration for Express",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -50,7 +50,10 @@
50
50
  "vitest": "^2.1.8"
51
51
  },
52
52
  "dependencies": {
53
- "@spaire/adapter-utils": "workspace:*",
53
+ "@spaire/adapter-utils": "^2.0.0",
54
54
  "@spaire/sdk": "^0.45.1"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
55
58
  }
56
59
  }