@moneymq/x402 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 MoneyMQ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # @moneymq/x402
2
+
3
+ x402 payment protocol utilities for MoneyMQ. Enables HTTP 402 Payment Required flows for micropayments.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @moneymq/x402
9
+ # or
10
+ pnpm add @moneymq/x402
11
+ # or
12
+ yarn add @moneymq/x402
13
+ ```
14
+
15
+ ## What is x402?
16
+
17
+ The x402 protocol enables native micropayments over HTTP using the `402 Payment Required` status code. When a server returns 402, it includes payment requirements that clients can fulfill to access the resource.
18
+
19
+ ## Usage
20
+
21
+ ### Client-side: Automatic Payment Handling
22
+
23
+ Use `payUpon402` to wrap HTTP calls with automatic payment handling:
24
+
25
+ ```typescript
26
+ import { payUpon402 } from '@moneymq/x402';
27
+
28
+ // Basic usage - will handle 402 with mock payment (for testing)
29
+ const data = await payUpon402(() =>
30
+ fetch('https://api.example.com/premium-data')
31
+ );
32
+
33
+ // With a wallet client for real payments
34
+ import { createSigner } from '@moneymq/x402';
35
+
36
+ const signer = createSigner(privateKey);
37
+ const data = await payUpon402(
38
+ () => fetch('https://api.example.com/premium-data'),
39
+ signer,
40
+ );
41
+
42
+ // With custom max value (default: 0.1 USDC)
43
+ const data = await payUpon402(
44
+ () => fetch('https://api.example.com/expensive-data'),
45
+ signer,
46
+ BigInt(1_000_000), // 1 USDC max
47
+ );
48
+ ```
49
+
50
+ ### Server-side: Express Middleware
51
+
52
+ Protect your API endpoints with payment requirements:
53
+
54
+ ```typescript
55
+ import express from 'express';
56
+ import { createX402Handler, requirePayment } from '@moneymq/x402';
57
+
58
+ const app = express();
59
+
60
+ // Full configuration
61
+ app.use('/api/premium', createX402Handler({
62
+ price: 100, // Amount in smallest units (0.0001 USDC)
63
+ currency: 'USDC',
64
+ recipient: 'YourWalletAddress...',
65
+ network: 'solana', // Optional, defaults to 'solana'
66
+ onPayment: async (payment) => {
67
+ // Called when payment is received
68
+ console.log(`Payment from ${payment.payer}: ${payment.amount}`);
69
+ console.log(`Transaction: ${payment.signature}`);
70
+
71
+ // Track usage, update database, etc.
72
+ await trackPayment(payment);
73
+ },
74
+ }));
75
+
76
+ app.get('/api/premium/data', (req, res) => {
77
+ // Only reached after payment is verified
78
+ res.json({ secret: 'Premium content!' });
79
+ });
80
+
81
+ // Shorthand for simple routes
82
+ app.get('/api/data',
83
+ requirePayment({
84
+ amount: 50,
85
+ currency: 'USDC',
86
+ recipient: 'YourWallet...',
87
+ }),
88
+ (req, res) => {
89
+ res.json({ data: 'Paid content' });
90
+ }
91
+ );
92
+ ```
93
+
94
+ ### Stripe Integration
95
+
96
+ Use x402 with Stripe's SDK for a familiar payment experience:
97
+
98
+ ```typescript
99
+ import { createStripeClient } from '@moneymq/x402';
100
+ import { createSigner } from '@moneymq/x402';
101
+
102
+ // Create signer from private key
103
+ const signer = createSigner(process.env.WALLET_PRIVATE_KEY);
104
+
105
+ // Create Stripe client with x402 payment handling
106
+ const stripe = createStripeClient(
107
+ process.env.STRIPE_API_KEY,
108
+ signer,
109
+ );
110
+
111
+ // Now any Stripe API call that returns 402 will be
112
+ // automatically handled with x402 payments
113
+ const paymentIntent = await stripe.paymentIntents.create({
114
+ amount: 1000,
115
+ currency: 'usd',
116
+ });
117
+ ```
118
+
119
+ ## API Reference
120
+
121
+ ### payUpon402
122
+
123
+ Wraps an HTTP call with automatic 402 payment handling.
124
+
125
+ ```typescript
126
+ function payUpon402<T>(
127
+ promiseOrFn: Promise<T> | (() => Promise<T>),
128
+ walletClient?: Signer | MultiNetworkSigner,
129
+ maxValue?: bigint,
130
+ config?: X402Config,
131
+ ): Promise<T>
132
+ ```
133
+
134
+ **Parameters:**
135
+
136
+ - `promiseOrFn` - Either a Promise or a function returning a Promise. Using a function allows retrying after payment.
137
+ - `walletClient` - Optional wallet signer for creating payments. Without this, mock payments are used.
138
+ - `maxValue` - Maximum payment amount allowed (default: 0.1 USDC = 100000)
139
+ - `config` - Optional x402 configuration
140
+
141
+ **Example:**
142
+
143
+ ```typescript
144
+ // Use function syntax for retry support
145
+ const data = await payUpon402(() => fetch('/api/data'), signer);
146
+
147
+ // Promise syntax (no retry on 402)
148
+ const data = await payUpon402(fetch('/api/data'));
149
+ ```
150
+
151
+ ### createX402Handler
152
+
153
+ Creates Express middleware for handling x402 payments.
154
+
155
+ ```typescript
156
+ function createX402Handler(config: X402HandlerConfig): RequestHandler
157
+ ```
158
+
159
+ **Config options:**
160
+
161
+ ```typescript
162
+ interface X402HandlerConfig {
163
+ price: number; // Amount in smallest units
164
+ currency: string; // e.g., 'USDC'
165
+ recipient: string; // Recipient wallet address
166
+ network?: string; // Network (default: 'solana')
167
+ onPayment?: (payment: {
168
+ amount: number;
169
+ payer: string;
170
+ signature?: string;
171
+ }) => void | Promise<void>;
172
+ }
173
+ ```
174
+
175
+ ### requirePayment
176
+
177
+ Shorthand for creating x402 middleware.
178
+
179
+ ```typescript
180
+ function requirePayment(options: {
181
+ amount: number;
182
+ currency: string;
183
+ recipient: string;
184
+ network?: string;
185
+ }): RequestHandler
186
+ ```
187
+
188
+ ### createStripeClient
189
+
190
+ Creates a Stripe client with x402 payment interception.
191
+
192
+ ```typescript
193
+ function createStripeClient(
194
+ apiKey: string,
195
+ walletClient?: Signer | MultiNetworkSigner,
196
+ config?: Stripe.StripeConfig,
197
+ x402Config?: X402Config,
198
+ ): Stripe
199
+ ```
200
+
201
+ ### createSigner
202
+
203
+ Re-exported from `x402-fetch` for convenience.
204
+
205
+ ```typescript
206
+ import { createSigner } from '@moneymq/x402';
207
+
208
+ const signer = createSigner(privateKeyBytes);
209
+ ```
210
+
211
+ ## Types
212
+
213
+ All x402 types are re-exported:
214
+
215
+ ```typescript
216
+ import type {
217
+ PaymentRequirements,
218
+ Signer,
219
+ MultiNetworkSigner,
220
+ X402Config,
221
+ } from '@moneymq/x402';
222
+ ```
223
+
224
+ ## How it Works
225
+
226
+ 1. Client makes request to protected endpoint
227
+ 2. Server returns `402 Payment Required` with payment requirements in headers/body
228
+ 3. Client parses requirements and creates payment transaction
229
+ 4. Client signs transaction with wallet
230
+ 5. Client retries request with `X-Payment` header containing signed transaction
231
+ 6. Server verifies payment and grants access
232
+
233
+ ## Supported Networks
234
+
235
+ - Solana (mainnet, devnet, testnet)
236
+ - More coming soon
237
+
238
+ ## License
239
+
240
+ MIT
@@ -0,0 +1,117 @@
1
+ import { Signer, MultiNetworkSigner, X402Config } from 'x402/types';
2
+ export { MultiNetworkSigner, PaymentRequirements, Signer, X402Config } from 'x402/types';
3
+ import Stripe from 'stripe';
4
+ export { createSigner } from 'x402-fetch';
5
+ export { createPaymentHeader, selectPaymentRequirements } from 'x402/client';
6
+
7
+ type PromiseOrFn<T> = Promise<T> | (() => Promise<T>);
8
+ /**
9
+ * Wrap an HTTP call with automatic 402 Payment Required handling
10
+ *
11
+ * This utility works with any HTTP API that returns 402 status codes for
12
+ * payment-required scenarios. It uses the Coinbase x402 protocol to
13
+ * automatically handle payment flows and retry the request.
14
+ *
15
+ * Works with any API client (fetch, axios, Stripe SDK, custom clients, etc.)
16
+ * that throws errors with a statusCode property.
17
+ *
18
+ * @example
19
+ * // With a wallet client for automatic payment:
20
+ * await payUpon402(() => fetch('/api/endpoint', { method: 'POST' }), walletClient)
21
+ *
22
+ * // Without wallet client (will fail on 402):
23
+ * await payUpon402(() => apiClient.post('/resource'))
24
+ */
25
+ declare function payUpon402<T>(promiseOrFn: PromiseOrFn<T>, walletClient?: Signer | MultiNetworkSigner, maxValue?: bigint, config?: X402Config): Promise<T>;
26
+
27
+ /**
28
+ * Creates a Stripe client with automatic X402 payment handling
29
+ *
30
+ * @param apiKey - Stripe API key
31
+ * @param walletClient - Optional wallet client for creating payments
32
+ * @param config - Stripe configuration options
33
+ * @param x402Config - Optional X402 configuration
34
+ * @returns A Stripe client instance with payment middleware
35
+ */
36
+ declare function createStripeClient(apiKey: string, walletClient?: Signer | MultiNetworkSigner, config?: Stripe.StripeConfig, x402Config?: X402Config): Stripe;
37
+
38
+ /**
39
+ * Configuration for x402 handler
40
+ */
41
+ interface X402HandlerConfig {
42
+ /** Price in smallest unit (e.g., 100 = 0.0001 USDC) */
43
+ price: number;
44
+ /** Currency code */
45
+ currency: string;
46
+ /** Recipient wallet address */
47
+ recipient: string;
48
+ /** Network (defaults to 'solana-mainnet') */
49
+ network?: string;
50
+ /** Callback when payment is received */
51
+ onPayment?: (payment: {
52
+ amount: number;
53
+ payer: string;
54
+ signature?: string;
55
+ }) => void | Promise<void>;
56
+ }
57
+ /**
58
+ * Express-compatible request type
59
+ */
60
+ interface Request {
61
+ headers: Record<string, string | string[] | undefined>;
62
+ method?: string;
63
+ path?: string;
64
+ }
65
+ /**
66
+ * Express-compatible response type
67
+ */
68
+ interface Response {
69
+ status: (code: number) => Response;
70
+ json: (body: unknown) => void;
71
+ setHeader: (name: string, value: string) => void;
72
+ }
73
+ /**
74
+ * Express-compatible next function
75
+ */
76
+ type NextFunction = (err?: unknown) => void;
77
+ /**
78
+ * Create an Express middleware handler for x402 payments
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { createX402Handler } from '@moneymq/x402';
83
+ *
84
+ * app.use('/api/protected', createX402Handler({
85
+ * price: 100, // 0.0001 USDC
86
+ * currency: 'USDC',
87
+ * recipient: 'YourWalletAddress...',
88
+ * onPayment: async (payment) => {
89
+ * console.log(`Received payment from ${payment.payer}`);
90
+ * },
91
+ * }));
92
+ * ```
93
+ */
94
+ declare function createX402Handler(config: X402HandlerConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
95
+ /**
96
+ * Shorthand middleware for requiring payment on a route
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * import { requirePayment } from '@moneymq/x402';
101
+ *
102
+ * app.get('/api/premium',
103
+ * requirePayment({ amount: 50, currency: 'USDC' }),
104
+ * (req, res) => {
105
+ * res.json({ data: 'Premium content' });
106
+ * }
107
+ * );
108
+ * ```
109
+ */
110
+ declare function requirePayment(options: {
111
+ amount: number;
112
+ currency: string;
113
+ recipient?: string;
114
+ network?: string;
115
+ }): (req: Request, res: Response, next: NextFunction) => Promise<void>;
116
+
117
+ export { createStripeClient, createX402Handler, payUpon402, requirePayment };
@@ -0,0 +1,117 @@
1
+ import { Signer, MultiNetworkSigner, X402Config } from 'x402/types';
2
+ export { MultiNetworkSigner, PaymentRequirements, Signer, X402Config } from 'x402/types';
3
+ import Stripe from 'stripe';
4
+ export { createSigner } from 'x402-fetch';
5
+ export { createPaymentHeader, selectPaymentRequirements } from 'x402/client';
6
+
7
+ type PromiseOrFn<T> = Promise<T> | (() => Promise<T>);
8
+ /**
9
+ * Wrap an HTTP call with automatic 402 Payment Required handling
10
+ *
11
+ * This utility works with any HTTP API that returns 402 status codes for
12
+ * payment-required scenarios. It uses the Coinbase x402 protocol to
13
+ * automatically handle payment flows and retry the request.
14
+ *
15
+ * Works with any API client (fetch, axios, Stripe SDK, custom clients, etc.)
16
+ * that throws errors with a statusCode property.
17
+ *
18
+ * @example
19
+ * // With a wallet client for automatic payment:
20
+ * await payUpon402(() => fetch('/api/endpoint', { method: 'POST' }), walletClient)
21
+ *
22
+ * // Without wallet client (will fail on 402):
23
+ * await payUpon402(() => apiClient.post('/resource'))
24
+ */
25
+ declare function payUpon402<T>(promiseOrFn: PromiseOrFn<T>, walletClient?: Signer | MultiNetworkSigner, maxValue?: bigint, config?: X402Config): Promise<T>;
26
+
27
+ /**
28
+ * Creates a Stripe client with automatic X402 payment handling
29
+ *
30
+ * @param apiKey - Stripe API key
31
+ * @param walletClient - Optional wallet client for creating payments
32
+ * @param config - Stripe configuration options
33
+ * @param x402Config - Optional X402 configuration
34
+ * @returns A Stripe client instance with payment middleware
35
+ */
36
+ declare function createStripeClient(apiKey: string, walletClient?: Signer | MultiNetworkSigner, config?: Stripe.StripeConfig, x402Config?: X402Config): Stripe;
37
+
38
+ /**
39
+ * Configuration for x402 handler
40
+ */
41
+ interface X402HandlerConfig {
42
+ /** Price in smallest unit (e.g., 100 = 0.0001 USDC) */
43
+ price: number;
44
+ /** Currency code */
45
+ currency: string;
46
+ /** Recipient wallet address */
47
+ recipient: string;
48
+ /** Network (defaults to 'solana-mainnet') */
49
+ network?: string;
50
+ /** Callback when payment is received */
51
+ onPayment?: (payment: {
52
+ amount: number;
53
+ payer: string;
54
+ signature?: string;
55
+ }) => void | Promise<void>;
56
+ }
57
+ /**
58
+ * Express-compatible request type
59
+ */
60
+ interface Request {
61
+ headers: Record<string, string | string[] | undefined>;
62
+ method?: string;
63
+ path?: string;
64
+ }
65
+ /**
66
+ * Express-compatible response type
67
+ */
68
+ interface Response {
69
+ status: (code: number) => Response;
70
+ json: (body: unknown) => void;
71
+ setHeader: (name: string, value: string) => void;
72
+ }
73
+ /**
74
+ * Express-compatible next function
75
+ */
76
+ type NextFunction = (err?: unknown) => void;
77
+ /**
78
+ * Create an Express middleware handler for x402 payments
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { createX402Handler } from '@moneymq/x402';
83
+ *
84
+ * app.use('/api/protected', createX402Handler({
85
+ * price: 100, // 0.0001 USDC
86
+ * currency: 'USDC',
87
+ * recipient: 'YourWalletAddress...',
88
+ * onPayment: async (payment) => {
89
+ * console.log(`Received payment from ${payment.payer}`);
90
+ * },
91
+ * }));
92
+ * ```
93
+ */
94
+ declare function createX402Handler(config: X402HandlerConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
95
+ /**
96
+ * Shorthand middleware for requiring payment on a route
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * import { requirePayment } from '@moneymq/x402';
101
+ *
102
+ * app.get('/api/premium',
103
+ * requirePayment({ amount: 50, currency: 'USDC' }),
104
+ * (req, res) => {
105
+ * res.json({ data: 'Premium content' });
106
+ * }
107
+ * );
108
+ * ```
109
+ */
110
+ declare function requirePayment(options: {
111
+ amount: number;
112
+ currency: string;
113
+ recipient?: string;
114
+ network?: string;
115
+ }): (req: Request, res: Response, next: NextFunction) => Promise<void>;
116
+
117
+ export { createStripeClient, createX402Handler, payUpon402, requirePayment };
package/dist/index.js ADDED
@@ -0,0 +1,334 @@
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/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ createPaymentHeader: () => import_client3.createPaymentHeader,
34
+ createSigner: () => import_x402_fetch.createSigner,
35
+ createStripeClient: () => createStripeClient,
36
+ createX402Handler: () => createX402Handler,
37
+ payUpon402: () => payUpon402,
38
+ requirePayment: () => requirePayment,
39
+ selectPaymentRequirements: () => import_client3.selectPaymentRequirements
40
+ });
41
+ module.exports = __toCommonJS(index_exports);
42
+
43
+ // src/payUpon402.ts
44
+ var import_client = require("x402/client");
45
+ async function payUpon402(promiseOrFn, walletClient, maxValue = BigInt(0.1 * 10 ** 6), config) {
46
+ const isFunction = typeof promiseOrFn === "function";
47
+ const execute = async (_paymentHeader) => {
48
+ return isFunction ? await promiseOrFn() : await promiseOrFn;
49
+ };
50
+ try {
51
+ return await execute();
52
+ } catch (error) {
53
+ const is402 = error?.statusCode === 402 || error?.status === 402 || error?.raw?.statusCode === 402;
54
+ if (!is402) throw error;
55
+ console.log("\u{1F4B3} 402 Payment Required - processing payment...");
56
+ if (!isFunction) {
57
+ console.warn(
58
+ "\u26A0\uFE0F Cannot retry - promise already executed. Use () => syntax for retry support."
59
+ );
60
+ throw error;
61
+ }
62
+ if (!walletClient) {
63
+ console.warn("\u26A0\uFE0F No wallet client provided. Using mock payment (will not actually pay).");
64
+ }
65
+ let x402Response;
66
+ try {
67
+ if (error?.raw?.payment_requirements !== void 0) {
68
+ console.log("\u2713 Found payment requirements in Stripe error format");
69
+ x402Response = {
70
+ x402Version: 1,
71
+ // Default to version 1
72
+ accepts: error.raw.payment_requirements,
73
+ error: {
74
+ code: error.raw.code || "payment_required",
75
+ message: error.raw.message || "Payment required",
76
+ type: error.raw.type || "invalid_request_error"
77
+ }
78
+ };
79
+ } else if (error?.raw?.body) {
80
+ console.log("Attempting to parse from error.raw.body");
81
+ x402Response = typeof error.raw.body === "string" ? JSON.parse(error.raw.body) : error.raw.body;
82
+ } else if (error?.response?.data) {
83
+ console.log("Attempting to parse from error.response.data");
84
+ x402Response = error.response.data;
85
+ } else if (error?.error) {
86
+ console.log("Attempting to parse from error.error");
87
+ x402Response = error.error;
88
+ } else {
89
+ throw new Error("Cannot parse 402 response");
90
+ }
91
+ } catch (parseError) {
92
+ console.warn("\u26A0\uFE0F Failed to parse payment requirements from 402 response");
93
+ console.error(parseError);
94
+ throw error;
95
+ }
96
+ const { x402Version, accepts } = x402Response;
97
+ if (!accepts || accepts.length === 0) {
98
+ console.warn("\u26A0\uFE0F No payment requirements found in 402 response");
99
+ throw error;
100
+ }
101
+ try {
102
+ let paymentHeaderValue;
103
+ if (!walletClient) {
104
+ console.log("\u{1F4B0} Creating mock payment header...");
105
+ const mockPayload = {
106
+ x402Version,
107
+ scheme: "exact",
108
+ network: "solana-surfnet",
109
+ payload: {
110
+ transaction: "mock_base58_encoded_transaction"
111
+ }
112
+ };
113
+ paymentHeaderValue = Buffer.from(JSON.stringify(mockPayload)).toString("base64");
114
+ } else {
115
+ const selectedPaymentRequirement = (0, import_client.selectPaymentRequirements)(
116
+ accepts,
117
+ void 0,
118
+ // Let the selector determine network from wallet
119
+ "exact"
120
+ );
121
+ if (BigInt(selectedPaymentRequirement.maxAmountRequired) > maxValue) {
122
+ throw new Error(
123
+ `Payment amount ${selectedPaymentRequirement.maxAmountRequired} exceeds maximum allowed ${maxValue}`
124
+ );
125
+ }
126
+ console.log(`\u{1F4B0} Creating payment for ${selectedPaymentRequirement.network}...`);
127
+ paymentHeaderValue = await (0, import_client.createPaymentHeader)(
128
+ walletClient,
129
+ x402Version,
130
+ selectedPaymentRequirement,
131
+ config
132
+ );
133
+ }
134
+ console.log("\u2705 Payment header created, retrying request...");
135
+ return await execute(paymentHeaderValue);
136
+ } catch (paymentError) {
137
+ console.warn("\u26A0\uFE0F Payment creation failed");
138
+ console.error(paymentError);
139
+ throw error;
140
+ }
141
+ }
142
+ }
143
+
144
+ // src/createStripeClient.ts
145
+ var import_stripe = __toESM(require("stripe"));
146
+ var import_client2 = require("x402/client");
147
+ function createStripeClient(apiKey, walletClient, config, x402Config) {
148
+ const customHttpClient = import_stripe.default.createFetchHttpClient();
149
+ const originalMakeRequest = customHttpClient.makeRequest.bind(customHttpClient);
150
+ customHttpClient.makeRequest = async (host, port, path, method, headers, requestData, protocol, timeout) => {
151
+ console.log(`\u{1F50D} Making request to ${method} ${host}:${port}${path}`);
152
+ const response = await originalMakeRequest(
153
+ host,
154
+ port,
155
+ path,
156
+ method,
157
+ headers,
158
+ requestData,
159
+ protocol,
160
+ timeout
161
+ );
162
+ const statusCode = response.getStatusCode();
163
+ console.log(`\u{1F4E5} Response status: ${statusCode}`);
164
+ if (statusCode === 402) {
165
+ console.log("\u{1F4B3} 402 Payment Required - processing payment...");
166
+ const responseBody = await response.toJSON();
167
+ console.log("\u{1F4C4} Response body:", JSON.stringify(responseBody, null, 2));
168
+ const paymentRequirements = responseBody?.payment_requirements || responseBody?.error?.payment_requirements || [];
169
+ if (paymentRequirements.length === 0) {
170
+ console.warn("\u26A0\uFE0F No payment requirements found in 402 response");
171
+ return response;
172
+ }
173
+ let paymentHeaderValue;
174
+ if (!walletClient) {
175
+ console.warn("\u26A0\uFE0F No wallet client provided. Using mock payment (will not actually pay).");
176
+ const mockPayload = {
177
+ x402Version: 1,
178
+ scheme: "exact",
179
+ network: "solana-surfnet",
180
+ payload: {
181
+ transaction: "mock_base58_encoded_transaction"
182
+ }
183
+ };
184
+ paymentHeaderValue = Buffer.from(JSON.stringify(mockPayload)).toString("base64");
185
+ } else {
186
+ const selectedPaymentRequirement = (0, import_client2.selectPaymentRequirements)(
187
+ paymentRequirements,
188
+ void 0,
189
+ "exact"
190
+ );
191
+ console.log(`\u{1F4B0} Creating payment for ${selectedPaymentRequirement.network}...`);
192
+ let signer;
193
+ if ("svm" in walletClient) {
194
+ signer = walletClient.svm;
195
+ } else {
196
+ signer = walletClient;
197
+ }
198
+ const effectiveX402Config = {
199
+ ...x402Config,
200
+ svmConfig: {
201
+ rpcUrl: "http://localhost:8899"
202
+ }
203
+ };
204
+ console.log(effectiveX402Config);
205
+ paymentHeaderValue = await (0, import_client2.createPaymentHeader)(
206
+ signer,
207
+ 1,
208
+ // x402Version
209
+ selectedPaymentRequirement,
210
+ effectiveX402Config
211
+ );
212
+ }
213
+ console.log("\u2705 Payment header created, retrying request...");
214
+ const headersWithPayment = {
215
+ ...headers,
216
+ "X-Payment": paymentHeaderValue
217
+ };
218
+ console.log("\u{1F4E4} Retrying with X-Payment header");
219
+ return await originalMakeRequest(
220
+ host,
221
+ port,
222
+ path,
223
+ method,
224
+ headersWithPayment,
225
+ requestData,
226
+ protocol,
227
+ timeout
228
+ );
229
+ }
230
+ return response;
231
+ };
232
+ return new import_stripe.default(apiKey, {
233
+ ...config,
234
+ httpClient: customHttpClient,
235
+ maxNetworkRetries: 0
236
+ // Disable Stripe's automatic retries so we can handle 402
237
+ });
238
+ }
239
+
240
+ // src/middleware.ts
241
+ function createPaymentRequirements(config) {
242
+ return {
243
+ scheme: "exact",
244
+ network: config.network ?? "solana",
245
+ maxAmountRequired: String(config.price),
246
+ resource: config.recipient,
247
+ description: `Payment of ${config.price} ${config.currency}`,
248
+ mimeType: "application/json",
249
+ payTo: config.recipient,
250
+ maxTimeoutSeconds: 60,
251
+ asset: config.currency,
252
+ extra: {}
253
+ };
254
+ }
255
+ async function verifyPaymentHeader(header, _config) {
256
+ try {
257
+ const decoded = JSON.parse(Buffer.from(header, "base64").toString("utf-8"));
258
+ if (!decoded.payload?.transaction) {
259
+ return { valid: false };
260
+ }
261
+ return {
262
+ valid: true,
263
+ payer: decoded.payload?.payer ?? "unknown",
264
+ signature: decoded.payload?.transaction
265
+ };
266
+ } catch {
267
+ return { valid: false };
268
+ }
269
+ }
270
+ function createX402Handler(config) {
271
+ return async (req, res, next) => {
272
+ const paymentHeader = req.headers["x-payment"];
273
+ if (!paymentHeader) {
274
+ const requirements = createPaymentRequirements(config);
275
+ res.status(402);
276
+ res.setHeader("X-Payment-Requirements", JSON.stringify([requirements]));
277
+ res.json({
278
+ error: {
279
+ code: "payment_required",
280
+ message: "Payment required to access this resource",
281
+ type: "invalid_request_error"
282
+ },
283
+ payment_requirements: [requirements]
284
+ });
285
+ return;
286
+ }
287
+ const verification = await verifyPaymentHeader(paymentHeader, config);
288
+ if (!verification.valid) {
289
+ res.status(402);
290
+ res.json({
291
+ error: {
292
+ code: "invalid_payment",
293
+ message: "Invalid payment header",
294
+ type: "invalid_request_error"
295
+ }
296
+ });
297
+ return;
298
+ }
299
+ if (config.onPayment) {
300
+ await config.onPayment({
301
+ amount: config.price,
302
+ payer: verification.payer,
303
+ signature: verification.signature
304
+ });
305
+ }
306
+ next();
307
+ };
308
+ }
309
+ function requirePayment(options) {
310
+ if (!options.recipient) {
311
+ throw new Error("requirePayment: recipient wallet address is required");
312
+ }
313
+ return createX402Handler({
314
+ price: options.amount,
315
+ currency: options.currency,
316
+ recipient: options.recipient,
317
+ network: options.network
318
+ });
319
+ }
320
+
321
+ // src/index.ts
322
+ var import_x402_fetch = require("x402-fetch");
323
+ var import_client3 = require("x402/client");
324
+ // Annotate the CommonJS export names for ESM import in node:
325
+ 0 && (module.exports = {
326
+ createPaymentHeader,
327
+ createSigner,
328
+ createStripeClient,
329
+ createX402Handler,
330
+ payUpon402,
331
+ requirePayment,
332
+ selectPaymentRequirements
333
+ });
334
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/payUpon402.ts","../src/createStripeClient.ts","../src/middleware.ts"],"sourcesContent":["// Main exports\nexport { payUpon402 } from './payUpon402';\nexport { createStripeClient } from './createStripeClient';\nexport { createX402Handler, requirePayment } from './middleware';\n\n// Re-export x402 utilities for convenience\nexport { createSigner } from 'x402-fetch';\nexport { createPaymentHeader, selectPaymentRequirements } from 'x402/client';\nexport type { PaymentRequirements, Signer, MultiNetworkSigner, X402Config } from 'x402/types';\n","import { createPaymentHeader, selectPaymentRequirements } from 'x402/client';\nimport type { Signer, MultiNetworkSigner, X402Config, PaymentRequirements } from 'x402/types';\n\ntype PromiseOrFn<T> = Promise<T> | (() => Promise<T>);\n\ninterface X402Response {\n x402Version: number;\n accepts: PaymentRequirements[];\n error?: {\n code: string;\n message: string;\n type: string;\n };\n}\n\n/**\n * Wrap an HTTP call with automatic 402 Payment Required handling\n *\n * This utility works with any HTTP API that returns 402 status codes for\n * payment-required scenarios. It uses the Coinbase x402 protocol to\n * automatically handle payment flows and retry the request.\n *\n * Works with any API client (fetch, axios, Stripe SDK, custom clients, etc.)\n * that throws errors with a statusCode property.\n *\n * @example\n * // With a wallet client for automatic payment:\n * await payUpon402(() => fetch('/api/endpoint', { method: 'POST' }), walletClient)\n *\n * // Without wallet client (will fail on 402):\n * await payUpon402(() => apiClient.post('/resource'))\n */\nexport async function payUpon402<T>(\n promiseOrFn: PromiseOrFn<T>,\n walletClient?: Signer | MultiNetworkSigner,\n maxValue: bigint = BigInt(0.1 * 10 ** 6),\n config?: X402Config,\n): Promise<T> {\n const isFunction = typeof promiseOrFn === 'function';\n\n const execute = async (_paymentHeader?: string) => {\n return isFunction ? await (promiseOrFn as () => Promise<T>)() : await promiseOrFn;\n };\n\n try {\n return await execute();\n } catch (error: any) {\n const is402 =\n error?.statusCode === 402 || error?.status === 402 || error?.raw?.statusCode === 402;\n\n if (!is402) throw error;\n\n console.log('💳 402 Payment Required - processing payment...');\n\n if (!isFunction) {\n console.warn(\n '⚠️ Cannot retry - promise already executed. Use () => syntax for retry support.',\n );\n throw error;\n }\n\n // TODO: For now, use mock wallet client if none provided\n if (!walletClient) {\n console.warn('⚠️ No wallet client provided. Using mock payment (will not actually pay).');\n // Don't throw, continue with mock payment header generation\n }\n\n // Extract x402 response from error\n let x402Response: X402Response;\n try {\n // For Stripe SDK errors, the payment requirements are directly in error.raw\n if (error?.raw?.payment_requirements !== undefined) {\n console.log('✓ Found payment requirements in Stripe error format');\n x402Response = {\n x402Version: 1, // Default to version 1\n accepts: error.raw.payment_requirements,\n error: {\n code: error.raw.code || 'payment_required',\n message: error.raw.message || 'Payment required',\n type: error.raw.type || 'invalid_request_error',\n },\n };\n } else if (error?.raw?.body) {\n console.log('Attempting to parse from error.raw.body');\n x402Response =\n typeof error.raw.body === 'string' ? JSON.parse(error.raw.body) : error.raw.body;\n } else if (error?.response?.data) {\n console.log('Attempting to parse from error.response.data');\n x402Response = error.response.data;\n } else if (error?.error) {\n console.log('Attempting to parse from error.error');\n x402Response = error.error;\n } else {\n throw new Error('Cannot parse 402 response');\n }\n } catch (parseError) {\n console.warn('⚠️ Failed to parse payment requirements from 402 response');\n console.error(parseError);\n throw error;\n }\n\n const { x402Version, accepts } = x402Response;\n\n if (!accepts || accepts.length === 0) {\n console.warn('⚠️ No payment requirements found in 402 response');\n throw error;\n }\n\n try {\n let paymentHeaderValue: string;\n\n if (!walletClient) {\n // Create a mock payment header for testing\n console.log('💰 Creating mock payment header...');\n const mockPayload = {\n x402Version,\n scheme: 'exact',\n network: 'solana-surfnet',\n payload: {\n transaction: 'mock_base58_encoded_transaction',\n },\n };\n paymentHeaderValue = Buffer.from(JSON.stringify(mockPayload)).toString('base64');\n } else {\n // Select appropriate payment requirement\n const selectedPaymentRequirement = selectPaymentRequirements(\n accepts,\n undefined, // Let the selector determine network from wallet\n 'exact',\n );\n\n // Check if payment amount exceeds maximum\n if (BigInt(selectedPaymentRequirement.maxAmountRequired) > maxValue) {\n throw new Error(\n `Payment amount ${selectedPaymentRequirement.maxAmountRequired} exceeds maximum allowed ${maxValue}`,\n );\n }\n\n console.log(`💰 Creating payment for ${selectedPaymentRequirement.network}...`);\n\n // Create payment header using Coinbase x402 library\n paymentHeaderValue = await createPaymentHeader(\n walletClient,\n x402Version,\n selectedPaymentRequirement,\n config,\n );\n }\n\n console.log('✅ Payment header created, retrying request...');\n\n // Retry the request - this is simplified, real implementation would\n // need to inject the X-PAYMENT header into the original request\n return await execute(paymentHeaderValue);\n } catch (paymentError) {\n console.warn('⚠️ Payment creation failed');\n console.error(paymentError);\n throw error;\n }\n }\n}\n","import Stripe from 'stripe';\nimport { createPaymentHeader, selectPaymentRequirements } from 'x402/client';\nimport type { Signer, MultiNetworkSigner, X402Config, PaymentRequirements } from 'x402/types';\n\n/**\n * Creates a Stripe client with automatic X402 payment handling\n *\n * @param apiKey - Stripe API key\n * @param walletClient - Optional wallet client for creating payments\n * @param config - Stripe configuration options\n * @param x402Config - Optional X402 configuration\n * @returns A Stripe client instance with payment middleware\n */\nexport function createStripeClient(\n apiKey: string,\n walletClient?: Signer | MultiNetworkSigner,\n config?: Stripe.StripeConfig,\n x402Config?: X402Config,\n): Stripe {\n // Create a custom HTTP client that wraps fetch\n const customHttpClient = Stripe.createFetchHttpClient();\n\n // Wrap the makeRequest method to add X-Payment header on retry\n const originalMakeRequest = customHttpClient.makeRequest.bind(customHttpClient);\n\n customHttpClient.makeRequest = async (\n host: string,\n port: string | number,\n path: string,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n headers: object,\n requestData: string | null,\n protocol: Stripe.HttpProtocol,\n timeout: number,\n ) => {\n console.log(`🔍 Making request to ${method} ${host}:${port}${path}`);\n\n // Make the initial request\n const response = await originalMakeRequest(\n host,\n port,\n path,\n method,\n headers,\n requestData,\n protocol,\n timeout,\n );\n\n const statusCode = response.getStatusCode();\n console.log(`📥 Response status: ${statusCode}`);\n\n // Check if this is a 402 Payment Required response\n if (statusCode === 402) {\n console.log('💳 402 Payment Required - processing payment...');\n\n // Parse the response body to get payment requirements\n const responseBody = (await response.toJSON()) as any;\n console.log('📄 Response body:', JSON.stringify(responseBody, null, 2));\n\n const paymentRequirements: PaymentRequirements[] =\n responseBody?.payment_requirements || responseBody?.error?.payment_requirements || [];\n\n if (paymentRequirements.length === 0) {\n console.warn('⚠️ No payment requirements found in 402 response');\n return response;\n }\n\n let paymentHeaderValue: string;\n\n if (!walletClient) {\n // Create mock payment header\n console.warn('⚠️ No wallet client provided. Using mock payment (will not actually pay).');\n const mockPayload = {\n x402Version: 1,\n scheme: 'exact',\n network: 'solana-surfnet',\n payload: {\n transaction: 'mock_base58_encoded_transaction',\n },\n };\n paymentHeaderValue = Buffer.from(JSON.stringify(mockPayload)).toString('base64');\n } else {\n // Select appropriate payment requirement\n const selectedPaymentRequirement = selectPaymentRequirements(\n paymentRequirements,\n undefined,\n 'exact',\n );\n\n console.log(`💰 Creating payment for ${selectedPaymentRequirement.network}...`);\n\n // Extract the appropriate signer for the network\n let signer: Signer;\n if ('svm' in walletClient) {\n // MultiNetworkSigner - extract the svm signer\n signer = walletClient.svm;\n } else {\n // Already a Signer\n signer = walletClient;\n }\n\n // Create payment header using Coinbase x402 library\n // Add svmConfig with local RPC URL for local testing\n const effectiveX402Config = {\n ...x402Config,\n svmConfig: {\n rpcUrl: 'http://localhost:8899',\n },\n };\n console.log(effectiveX402Config);\n\n paymentHeaderValue = await createPaymentHeader(\n signer,\n 1, // x402Version\n selectedPaymentRequirement,\n effectiveX402Config,\n );\n }\n\n console.log('✅ Payment header created, retrying request...');\n\n // Retry with X-Payment header\n const headersWithPayment = {\n ...(headers as Record<string, string>),\n 'X-Payment': paymentHeaderValue,\n };\n\n console.log('📤 Retrying with X-Payment header');\n\n return await originalMakeRequest(\n host,\n port,\n path,\n method,\n headersWithPayment,\n requestData,\n protocol,\n timeout,\n );\n }\n\n return response;\n };\n\n return new Stripe(apiKey, {\n ...config,\n httpClient: customHttpClient,\n maxNetworkRetries: 0, // Disable Stripe's automatic retries so we can handle 402\n });\n}\n","import type { PaymentRequirements } from 'x402/types';\n\n/**\n * Configuration for x402 handler\n */\nexport interface X402HandlerConfig {\n /** Price in smallest unit (e.g., 100 = 0.0001 USDC) */\n price: number;\n /** Currency code */\n currency: string;\n /** Recipient wallet address */\n recipient: string;\n /** Network (defaults to 'solana-mainnet') */\n network?: string;\n /** Callback when payment is received */\n onPayment?: (payment: {\n amount: number;\n payer: string;\n signature?: string;\n }) => void | Promise<void>;\n}\n\n/**\n * Express-compatible request type\n */\ninterface Request {\n headers: Record<string, string | string[] | undefined>;\n method?: string;\n path?: string;\n}\n\n/**\n * Express-compatible response type\n */\ninterface Response {\n status: (code: number) => Response;\n json: (body: unknown) => void;\n setHeader: (name: string, value: string) => void;\n}\n\n/**\n * Express-compatible next function\n */\ntype NextFunction = (err?: unknown) => void;\n\n/**\n * Create payment requirements for 402 response\n */\nfunction createPaymentRequirements(config: X402HandlerConfig): PaymentRequirements {\n return {\n scheme: 'exact',\n network: (config.network ?? 'solana') as PaymentRequirements['network'],\n maxAmountRequired: String(config.price),\n resource: config.recipient,\n description: `Payment of ${config.price} ${config.currency}`,\n mimeType: 'application/json',\n payTo: config.recipient,\n maxTimeoutSeconds: 60,\n asset: config.currency,\n extra: {},\n };\n}\n\n/**\n * Verify x402 payment header\n */\nasync function verifyPaymentHeader(\n header: string,\n _config: X402HandlerConfig,\n): Promise<{ valid: boolean; payer?: string; signature?: string }> {\n try {\n const decoded = JSON.parse(Buffer.from(header, 'base64').toString('utf-8'));\n\n // Basic validation - in production, verify the actual transaction\n if (!decoded.payload?.transaction) {\n return { valid: false };\n }\n\n // TODO: Implement actual transaction verification\n // For now, accept any properly formatted header\n return {\n valid: true,\n payer: decoded.payload?.payer ?? 'unknown',\n signature: decoded.payload?.transaction,\n };\n } catch {\n return { valid: false };\n }\n}\n\n/**\n * Create an Express middleware handler for x402 payments\n *\n * @example\n * ```typescript\n * import { createX402Handler } from '@moneymq/x402';\n *\n * app.use('/api/protected', createX402Handler({\n * price: 100, // 0.0001 USDC\n * currency: 'USDC',\n * recipient: 'YourWalletAddress...',\n * onPayment: async (payment) => {\n * console.log(`Received payment from ${payment.payer}`);\n * },\n * }));\n * ```\n */\nexport function createX402Handler(config: X402HandlerConfig) {\n return async (req: Request, res: Response, next: NextFunction) => {\n const paymentHeader = req.headers['x-payment'] as string | undefined;\n\n if (!paymentHeader) {\n // Return 402 Payment Required\n const requirements = createPaymentRequirements(config);\n\n res.status(402);\n res.setHeader('X-Payment-Requirements', JSON.stringify([requirements]));\n res.json({\n error: {\n code: 'payment_required',\n message: 'Payment required to access this resource',\n type: 'invalid_request_error',\n },\n payment_requirements: [requirements],\n });\n return;\n }\n\n // Verify payment\n const verification = await verifyPaymentHeader(paymentHeader, config);\n\n if (!verification.valid) {\n res.status(402);\n res.json({\n error: {\n code: 'invalid_payment',\n message: 'Invalid payment header',\n type: 'invalid_request_error',\n },\n });\n return;\n }\n\n // Payment verified - call onPayment callback if provided\n if (config.onPayment) {\n await config.onPayment({\n amount: config.price,\n payer: verification.payer!,\n signature: verification.signature,\n });\n }\n\n // Continue to next handler\n next();\n };\n}\n\n/**\n * Shorthand middleware for requiring payment on a route\n *\n * @example\n * ```typescript\n * import { requirePayment } from '@moneymq/x402';\n *\n * app.get('/api/premium',\n * requirePayment({ amount: 50, currency: 'USDC' }),\n * (req, res) => {\n * res.json({ data: 'Premium content' });\n * }\n * );\n * ```\n */\nexport function requirePayment(options: {\n amount: number;\n currency: string;\n recipient?: string;\n network?: string;\n}) {\n if (!options.recipient) {\n throw new Error('requirePayment: recipient wallet address is required');\n }\n\n return createX402Handler({\n price: options.amount,\n currency: options.currency,\n recipient: options.recipient,\n network: options.network,\n });\n}\n"],"mappings":"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","names":["import_client","Stripe","import_client"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,291 @@
1
+ // src/payUpon402.ts
2
+ import { createPaymentHeader, selectPaymentRequirements } from "x402/client";
3
+ async function payUpon402(promiseOrFn, walletClient, maxValue = BigInt(0.1 * 10 ** 6), config) {
4
+ const isFunction = typeof promiseOrFn === "function";
5
+ const execute = async (_paymentHeader) => {
6
+ return isFunction ? await promiseOrFn() : await promiseOrFn;
7
+ };
8
+ try {
9
+ return await execute();
10
+ } catch (error) {
11
+ const is402 = error?.statusCode === 402 || error?.status === 402 || error?.raw?.statusCode === 402;
12
+ if (!is402) throw error;
13
+ console.log("\u{1F4B3} 402 Payment Required - processing payment...");
14
+ if (!isFunction) {
15
+ console.warn(
16
+ "\u26A0\uFE0F Cannot retry - promise already executed. Use () => syntax for retry support."
17
+ );
18
+ throw error;
19
+ }
20
+ if (!walletClient) {
21
+ console.warn("\u26A0\uFE0F No wallet client provided. Using mock payment (will not actually pay).");
22
+ }
23
+ let x402Response;
24
+ try {
25
+ if (error?.raw?.payment_requirements !== void 0) {
26
+ console.log("\u2713 Found payment requirements in Stripe error format");
27
+ x402Response = {
28
+ x402Version: 1,
29
+ // Default to version 1
30
+ accepts: error.raw.payment_requirements,
31
+ error: {
32
+ code: error.raw.code || "payment_required",
33
+ message: error.raw.message || "Payment required",
34
+ type: error.raw.type || "invalid_request_error"
35
+ }
36
+ };
37
+ } else if (error?.raw?.body) {
38
+ console.log("Attempting to parse from error.raw.body");
39
+ x402Response = typeof error.raw.body === "string" ? JSON.parse(error.raw.body) : error.raw.body;
40
+ } else if (error?.response?.data) {
41
+ console.log("Attempting to parse from error.response.data");
42
+ x402Response = error.response.data;
43
+ } else if (error?.error) {
44
+ console.log("Attempting to parse from error.error");
45
+ x402Response = error.error;
46
+ } else {
47
+ throw new Error("Cannot parse 402 response");
48
+ }
49
+ } catch (parseError) {
50
+ console.warn("\u26A0\uFE0F Failed to parse payment requirements from 402 response");
51
+ console.error(parseError);
52
+ throw error;
53
+ }
54
+ const { x402Version, accepts } = x402Response;
55
+ if (!accepts || accepts.length === 0) {
56
+ console.warn("\u26A0\uFE0F No payment requirements found in 402 response");
57
+ throw error;
58
+ }
59
+ try {
60
+ let paymentHeaderValue;
61
+ if (!walletClient) {
62
+ console.log("\u{1F4B0} Creating mock payment header...");
63
+ const mockPayload = {
64
+ x402Version,
65
+ scheme: "exact",
66
+ network: "solana-surfnet",
67
+ payload: {
68
+ transaction: "mock_base58_encoded_transaction"
69
+ }
70
+ };
71
+ paymentHeaderValue = Buffer.from(JSON.stringify(mockPayload)).toString("base64");
72
+ } else {
73
+ const selectedPaymentRequirement = selectPaymentRequirements(
74
+ accepts,
75
+ void 0,
76
+ // Let the selector determine network from wallet
77
+ "exact"
78
+ );
79
+ if (BigInt(selectedPaymentRequirement.maxAmountRequired) > maxValue) {
80
+ throw new Error(
81
+ `Payment amount ${selectedPaymentRequirement.maxAmountRequired} exceeds maximum allowed ${maxValue}`
82
+ );
83
+ }
84
+ console.log(`\u{1F4B0} Creating payment for ${selectedPaymentRequirement.network}...`);
85
+ paymentHeaderValue = await createPaymentHeader(
86
+ walletClient,
87
+ x402Version,
88
+ selectedPaymentRequirement,
89
+ config
90
+ );
91
+ }
92
+ console.log("\u2705 Payment header created, retrying request...");
93
+ return await execute(paymentHeaderValue);
94
+ } catch (paymentError) {
95
+ console.warn("\u26A0\uFE0F Payment creation failed");
96
+ console.error(paymentError);
97
+ throw error;
98
+ }
99
+ }
100
+ }
101
+
102
+ // src/createStripeClient.ts
103
+ import Stripe from "stripe";
104
+ import { createPaymentHeader as createPaymentHeader2, selectPaymentRequirements as selectPaymentRequirements2 } from "x402/client";
105
+ function createStripeClient(apiKey, walletClient, config, x402Config) {
106
+ const customHttpClient = Stripe.createFetchHttpClient();
107
+ const originalMakeRequest = customHttpClient.makeRequest.bind(customHttpClient);
108
+ customHttpClient.makeRequest = async (host, port, path, method, headers, requestData, protocol, timeout) => {
109
+ console.log(`\u{1F50D} Making request to ${method} ${host}:${port}${path}`);
110
+ const response = await originalMakeRequest(
111
+ host,
112
+ port,
113
+ path,
114
+ method,
115
+ headers,
116
+ requestData,
117
+ protocol,
118
+ timeout
119
+ );
120
+ const statusCode = response.getStatusCode();
121
+ console.log(`\u{1F4E5} Response status: ${statusCode}`);
122
+ if (statusCode === 402) {
123
+ console.log("\u{1F4B3} 402 Payment Required - processing payment...");
124
+ const responseBody = await response.toJSON();
125
+ console.log("\u{1F4C4} Response body:", JSON.stringify(responseBody, null, 2));
126
+ const paymentRequirements = responseBody?.payment_requirements || responseBody?.error?.payment_requirements || [];
127
+ if (paymentRequirements.length === 0) {
128
+ console.warn("\u26A0\uFE0F No payment requirements found in 402 response");
129
+ return response;
130
+ }
131
+ let paymentHeaderValue;
132
+ if (!walletClient) {
133
+ console.warn("\u26A0\uFE0F No wallet client provided. Using mock payment (will not actually pay).");
134
+ const mockPayload = {
135
+ x402Version: 1,
136
+ scheme: "exact",
137
+ network: "solana-surfnet",
138
+ payload: {
139
+ transaction: "mock_base58_encoded_transaction"
140
+ }
141
+ };
142
+ paymentHeaderValue = Buffer.from(JSON.stringify(mockPayload)).toString("base64");
143
+ } else {
144
+ const selectedPaymentRequirement = selectPaymentRequirements2(
145
+ paymentRequirements,
146
+ void 0,
147
+ "exact"
148
+ );
149
+ console.log(`\u{1F4B0} Creating payment for ${selectedPaymentRequirement.network}...`);
150
+ let signer;
151
+ if ("svm" in walletClient) {
152
+ signer = walletClient.svm;
153
+ } else {
154
+ signer = walletClient;
155
+ }
156
+ const effectiveX402Config = {
157
+ ...x402Config,
158
+ svmConfig: {
159
+ rpcUrl: "http://localhost:8899"
160
+ }
161
+ };
162
+ console.log(effectiveX402Config);
163
+ paymentHeaderValue = await createPaymentHeader2(
164
+ signer,
165
+ 1,
166
+ // x402Version
167
+ selectedPaymentRequirement,
168
+ effectiveX402Config
169
+ );
170
+ }
171
+ console.log("\u2705 Payment header created, retrying request...");
172
+ const headersWithPayment = {
173
+ ...headers,
174
+ "X-Payment": paymentHeaderValue
175
+ };
176
+ console.log("\u{1F4E4} Retrying with X-Payment header");
177
+ return await originalMakeRequest(
178
+ host,
179
+ port,
180
+ path,
181
+ method,
182
+ headersWithPayment,
183
+ requestData,
184
+ protocol,
185
+ timeout
186
+ );
187
+ }
188
+ return response;
189
+ };
190
+ return new Stripe(apiKey, {
191
+ ...config,
192
+ httpClient: customHttpClient,
193
+ maxNetworkRetries: 0
194
+ // Disable Stripe's automatic retries so we can handle 402
195
+ });
196
+ }
197
+
198
+ // src/middleware.ts
199
+ function createPaymentRequirements(config) {
200
+ return {
201
+ scheme: "exact",
202
+ network: config.network ?? "solana",
203
+ maxAmountRequired: String(config.price),
204
+ resource: config.recipient,
205
+ description: `Payment of ${config.price} ${config.currency}`,
206
+ mimeType: "application/json",
207
+ payTo: config.recipient,
208
+ maxTimeoutSeconds: 60,
209
+ asset: config.currency,
210
+ extra: {}
211
+ };
212
+ }
213
+ async function verifyPaymentHeader(header, _config) {
214
+ try {
215
+ const decoded = JSON.parse(Buffer.from(header, "base64").toString("utf-8"));
216
+ if (!decoded.payload?.transaction) {
217
+ return { valid: false };
218
+ }
219
+ return {
220
+ valid: true,
221
+ payer: decoded.payload?.payer ?? "unknown",
222
+ signature: decoded.payload?.transaction
223
+ };
224
+ } catch {
225
+ return { valid: false };
226
+ }
227
+ }
228
+ function createX402Handler(config) {
229
+ return async (req, res, next) => {
230
+ const paymentHeader = req.headers["x-payment"];
231
+ if (!paymentHeader) {
232
+ const requirements = createPaymentRequirements(config);
233
+ res.status(402);
234
+ res.setHeader("X-Payment-Requirements", JSON.stringify([requirements]));
235
+ res.json({
236
+ error: {
237
+ code: "payment_required",
238
+ message: "Payment required to access this resource",
239
+ type: "invalid_request_error"
240
+ },
241
+ payment_requirements: [requirements]
242
+ });
243
+ return;
244
+ }
245
+ const verification = await verifyPaymentHeader(paymentHeader, config);
246
+ if (!verification.valid) {
247
+ res.status(402);
248
+ res.json({
249
+ error: {
250
+ code: "invalid_payment",
251
+ message: "Invalid payment header",
252
+ type: "invalid_request_error"
253
+ }
254
+ });
255
+ return;
256
+ }
257
+ if (config.onPayment) {
258
+ await config.onPayment({
259
+ amount: config.price,
260
+ payer: verification.payer,
261
+ signature: verification.signature
262
+ });
263
+ }
264
+ next();
265
+ };
266
+ }
267
+ function requirePayment(options) {
268
+ if (!options.recipient) {
269
+ throw new Error("requirePayment: recipient wallet address is required");
270
+ }
271
+ return createX402Handler({
272
+ price: options.amount,
273
+ currency: options.currency,
274
+ recipient: options.recipient,
275
+ network: options.network
276
+ });
277
+ }
278
+
279
+ // src/index.ts
280
+ import { createSigner } from "x402-fetch";
281
+ import { createPaymentHeader as createPaymentHeader3, selectPaymentRequirements as selectPaymentRequirements3 } from "x402/client";
282
+ export {
283
+ createPaymentHeader3 as createPaymentHeader,
284
+ createSigner,
285
+ createStripeClient,
286
+ createX402Handler,
287
+ payUpon402,
288
+ requirePayment,
289
+ selectPaymentRequirements3 as selectPaymentRequirements
290
+ };
291
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/payUpon402.ts","../src/createStripeClient.ts","../src/middleware.ts","../src/index.ts"],"sourcesContent":["import { createPaymentHeader, selectPaymentRequirements } from 'x402/client';\nimport type { Signer, MultiNetworkSigner, X402Config, PaymentRequirements } from 'x402/types';\n\ntype PromiseOrFn<T> = Promise<T> | (() => Promise<T>);\n\ninterface X402Response {\n x402Version: number;\n accepts: PaymentRequirements[];\n error?: {\n code: string;\n message: string;\n type: string;\n };\n}\n\n/**\n * Wrap an HTTP call with automatic 402 Payment Required handling\n *\n * This utility works with any HTTP API that returns 402 status codes for\n * payment-required scenarios. It uses the Coinbase x402 protocol to\n * automatically handle payment flows and retry the request.\n *\n * Works with any API client (fetch, axios, Stripe SDK, custom clients, etc.)\n * that throws errors with a statusCode property.\n *\n * @example\n * // With a wallet client for automatic payment:\n * await payUpon402(() => fetch('/api/endpoint', { method: 'POST' }), walletClient)\n *\n * // Without wallet client (will fail on 402):\n * await payUpon402(() => apiClient.post('/resource'))\n */\nexport async function payUpon402<T>(\n promiseOrFn: PromiseOrFn<T>,\n walletClient?: Signer | MultiNetworkSigner,\n maxValue: bigint = BigInt(0.1 * 10 ** 6),\n config?: X402Config,\n): Promise<T> {\n const isFunction = typeof promiseOrFn === 'function';\n\n const execute = async (_paymentHeader?: string) => {\n return isFunction ? await (promiseOrFn as () => Promise<T>)() : await promiseOrFn;\n };\n\n try {\n return await execute();\n } catch (error: any) {\n const is402 =\n error?.statusCode === 402 || error?.status === 402 || error?.raw?.statusCode === 402;\n\n if (!is402) throw error;\n\n console.log('💳 402 Payment Required - processing payment...');\n\n if (!isFunction) {\n console.warn(\n '⚠️ Cannot retry - promise already executed. Use () => syntax for retry support.',\n );\n throw error;\n }\n\n // TODO: For now, use mock wallet client if none provided\n if (!walletClient) {\n console.warn('⚠️ No wallet client provided. Using mock payment (will not actually pay).');\n // Don't throw, continue with mock payment header generation\n }\n\n // Extract x402 response from error\n let x402Response: X402Response;\n try {\n // For Stripe SDK errors, the payment requirements are directly in error.raw\n if (error?.raw?.payment_requirements !== undefined) {\n console.log('✓ Found payment requirements in Stripe error format');\n x402Response = {\n x402Version: 1, // Default to version 1\n accepts: error.raw.payment_requirements,\n error: {\n code: error.raw.code || 'payment_required',\n message: error.raw.message || 'Payment required',\n type: error.raw.type || 'invalid_request_error',\n },\n };\n } else if (error?.raw?.body) {\n console.log('Attempting to parse from error.raw.body');\n x402Response =\n typeof error.raw.body === 'string' ? JSON.parse(error.raw.body) : error.raw.body;\n } else if (error?.response?.data) {\n console.log('Attempting to parse from error.response.data');\n x402Response = error.response.data;\n } else if (error?.error) {\n console.log('Attempting to parse from error.error');\n x402Response = error.error;\n } else {\n throw new Error('Cannot parse 402 response');\n }\n } catch (parseError) {\n console.warn('⚠️ Failed to parse payment requirements from 402 response');\n console.error(parseError);\n throw error;\n }\n\n const { x402Version, accepts } = x402Response;\n\n if (!accepts || accepts.length === 0) {\n console.warn('⚠️ No payment requirements found in 402 response');\n throw error;\n }\n\n try {\n let paymentHeaderValue: string;\n\n if (!walletClient) {\n // Create a mock payment header for testing\n console.log('💰 Creating mock payment header...');\n const mockPayload = {\n x402Version,\n scheme: 'exact',\n network: 'solana-surfnet',\n payload: {\n transaction: 'mock_base58_encoded_transaction',\n },\n };\n paymentHeaderValue = Buffer.from(JSON.stringify(mockPayload)).toString('base64');\n } else {\n // Select appropriate payment requirement\n const selectedPaymentRequirement = selectPaymentRequirements(\n accepts,\n undefined, // Let the selector determine network from wallet\n 'exact',\n );\n\n // Check if payment amount exceeds maximum\n if (BigInt(selectedPaymentRequirement.maxAmountRequired) > maxValue) {\n throw new Error(\n `Payment amount ${selectedPaymentRequirement.maxAmountRequired} exceeds maximum allowed ${maxValue}`,\n );\n }\n\n console.log(`💰 Creating payment for ${selectedPaymentRequirement.network}...`);\n\n // Create payment header using Coinbase x402 library\n paymentHeaderValue = await createPaymentHeader(\n walletClient,\n x402Version,\n selectedPaymentRequirement,\n config,\n );\n }\n\n console.log('✅ Payment header created, retrying request...');\n\n // Retry the request - this is simplified, real implementation would\n // need to inject the X-PAYMENT header into the original request\n return await execute(paymentHeaderValue);\n } catch (paymentError) {\n console.warn('⚠️ Payment creation failed');\n console.error(paymentError);\n throw error;\n }\n }\n}\n","import Stripe from 'stripe';\nimport { createPaymentHeader, selectPaymentRequirements } from 'x402/client';\nimport type { Signer, MultiNetworkSigner, X402Config, PaymentRequirements } from 'x402/types';\n\n/**\n * Creates a Stripe client with automatic X402 payment handling\n *\n * @param apiKey - Stripe API key\n * @param walletClient - Optional wallet client for creating payments\n * @param config - Stripe configuration options\n * @param x402Config - Optional X402 configuration\n * @returns A Stripe client instance with payment middleware\n */\nexport function createStripeClient(\n apiKey: string,\n walletClient?: Signer | MultiNetworkSigner,\n config?: Stripe.StripeConfig,\n x402Config?: X402Config,\n): Stripe {\n // Create a custom HTTP client that wraps fetch\n const customHttpClient = Stripe.createFetchHttpClient();\n\n // Wrap the makeRequest method to add X-Payment header on retry\n const originalMakeRequest = customHttpClient.makeRequest.bind(customHttpClient);\n\n customHttpClient.makeRequest = async (\n host: string,\n port: string | number,\n path: string,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n headers: object,\n requestData: string | null,\n protocol: Stripe.HttpProtocol,\n timeout: number,\n ) => {\n console.log(`🔍 Making request to ${method} ${host}:${port}${path}`);\n\n // Make the initial request\n const response = await originalMakeRequest(\n host,\n port,\n path,\n method,\n headers,\n requestData,\n protocol,\n timeout,\n );\n\n const statusCode = response.getStatusCode();\n console.log(`📥 Response status: ${statusCode}`);\n\n // Check if this is a 402 Payment Required response\n if (statusCode === 402) {\n console.log('💳 402 Payment Required - processing payment...');\n\n // Parse the response body to get payment requirements\n const responseBody = (await response.toJSON()) as any;\n console.log('📄 Response body:', JSON.stringify(responseBody, null, 2));\n\n const paymentRequirements: PaymentRequirements[] =\n responseBody?.payment_requirements || responseBody?.error?.payment_requirements || [];\n\n if (paymentRequirements.length === 0) {\n console.warn('⚠️ No payment requirements found in 402 response');\n return response;\n }\n\n let paymentHeaderValue: string;\n\n if (!walletClient) {\n // Create mock payment header\n console.warn('⚠️ No wallet client provided. Using mock payment (will not actually pay).');\n const mockPayload = {\n x402Version: 1,\n scheme: 'exact',\n network: 'solana-surfnet',\n payload: {\n transaction: 'mock_base58_encoded_transaction',\n },\n };\n paymentHeaderValue = Buffer.from(JSON.stringify(mockPayload)).toString('base64');\n } else {\n // Select appropriate payment requirement\n const selectedPaymentRequirement = selectPaymentRequirements(\n paymentRequirements,\n undefined,\n 'exact',\n );\n\n console.log(`💰 Creating payment for ${selectedPaymentRequirement.network}...`);\n\n // Extract the appropriate signer for the network\n let signer: Signer;\n if ('svm' in walletClient) {\n // MultiNetworkSigner - extract the svm signer\n signer = walletClient.svm;\n } else {\n // Already a Signer\n signer = walletClient;\n }\n\n // Create payment header using Coinbase x402 library\n // Add svmConfig with local RPC URL for local testing\n const effectiveX402Config = {\n ...x402Config,\n svmConfig: {\n rpcUrl: 'http://localhost:8899',\n },\n };\n console.log(effectiveX402Config);\n\n paymentHeaderValue = await createPaymentHeader(\n signer,\n 1, // x402Version\n selectedPaymentRequirement,\n effectiveX402Config,\n );\n }\n\n console.log('✅ Payment header created, retrying request...');\n\n // Retry with X-Payment header\n const headersWithPayment = {\n ...(headers as Record<string, string>),\n 'X-Payment': paymentHeaderValue,\n };\n\n console.log('📤 Retrying with X-Payment header');\n\n return await originalMakeRequest(\n host,\n port,\n path,\n method,\n headersWithPayment,\n requestData,\n protocol,\n timeout,\n );\n }\n\n return response;\n };\n\n return new Stripe(apiKey, {\n ...config,\n httpClient: customHttpClient,\n maxNetworkRetries: 0, // Disable Stripe's automatic retries so we can handle 402\n });\n}\n","import type { PaymentRequirements } from 'x402/types';\n\n/**\n * Configuration for x402 handler\n */\nexport interface X402HandlerConfig {\n /** Price in smallest unit (e.g., 100 = 0.0001 USDC) */\n price: number;\n /** Currency code */\n currency: string;\n /** Recipient wallet address */\n recipient: string;\n /** Network (defaults to 'solana-mainnet') */\n network?: string;\n /** Callback when payment is received */\n onPayment?: (payment: {\n amount: number;\n payer: string;\n signature?: string;\n }) => void | Promise<void>;\n}\n\n/**\n * Express-compatible request type\n */\ninterface Request {\n headers: Record<string, string | string[] | undefined>;\n method?: string;\n path?: string;\n}\n\n/**\n * Express-compatible response type\n */\ninterface Response {\n status: (code: number) => Response;\n json: (body: unknown) => void;\n setHeader: (name: string, value: string) => void;\n}\n\n/**\n * Express-compatible next function\n */\ntype NextFunction = (err?: unknown) => void;\n\n/**\n * Create payment requirements for 402 response\n */\nfunction createPaymentRequirements(config: X402HandlerConfig): PaymentRequirements {\n return {\n scheme: 'exact',\n network: (config.network ?? 'solana') as PaymentRequirements['network'],\n maxAmountRequired: String(config.price),\n resource: config.recipient,\n description: `Payment of ${config.price} ${config.currency}`,\n mimeType: 'application/json',\n payTo: config.recipient,\n maxTimeoutSeconds: 60,\n asset: config.currency,\n extra: {},\n };\n}\n\n/**\n * Verify x402 payment header\n */\nasync function verifyPaymentHeader(\n header: string,\n _config: X402HandlerConfig,\n): Promise<{ valid: boolean; payer?: string; signature?: string }> {\n try {\n const decoded = JSON.parse(Buffer.from(header, 'base64').toString('utf-8'));\n\n // Basic validation - in production, verify the actual transaction\n if (!decoded.payload?.transaction) {\n return { valid: false };\n }\n\n // TODO: Implement actual transaction verification\n // For now, accept any properly formatted header\n return {\n valid: true,\n payer: decoded.payload?.payer ?? 'unknown',\n signature: decoded.payload?.transaction,\n };\n } catch {\n return { valid: false };\n }\n}\n\n/**\n * Create an Express middleware handler for x402 payments\n *\n * @example\n * ```typescript\n * import { createX402Handler } from '@moneymq/x402';\n *\n * app.use('/api/protected', createX402Handler({\n * price: 100, // 0.0001 USDC\n * currency: 'USDC',\n * recipient: 'YourWalletAddress...',\n * onPayment: async (payment) => {\n * console.log(`Received payment from ${payment.payer}`);\n * },\n * }));\n * ```\n */\nexport function createX402Handler(config: X402HandlerConfig) {\n return async (req: Request, res: Response, next: NextFunction) => {\n const paymentHeader = req.headers['x-payment'] as string | undefined;\n\n if (!paymentHeader) {\n // Return 402 Payment Required\n const requirements = createPaymentRequirements(config);\n\n res.status(402);\n res.setHeader('X-Payment-Requirements', JSON.stringify([requirements]));\n res.json({\n error: {\n code: 'payment_required',\n message: 'Payment required to access this resource',\n type: 'invalid_request_error',\n },\n payment_requirements: [requirements],\n });\n return;\n }\n\n // Verify payment\n const verification = await verifyPaymentHeader(paymentHeader, config);\n\n if (!verification.valid) {\n res.status(402);\n res.json({\n error: {\n code: 'invalid_payment',\n message: 'Invalid payment header',\n type: 'invalid_request_error',\n },\n });\n return;\n }\n\n // Payment verified - call onPayment callback if provided\n if (config.onPayment) {\n await config.onPayment({\n amount: config.price,\n payer: verification.payer!,\n signature: verification.signature,\n });\n }\n\n // Continue to next handler\n next();\n };\n}\n\n/**\n * Shorthand middleware for requiring payment on a route\n *\n * @example\n * ```typescript\n * import { requirePayment } from '@moneymq/x402';\n *\n * app.get('/api/premium',\n * requirePayment({ amount: 50, currency: 'USDC' }),\n * (req, res) => {\n * res.json({ data: 'Premium content' });\n * }\n * );\n * ```\n */\nexport function requirePayment(options: {\n amount: number;\n currency: string;\n recipient?: string;\n network?: string;\n}) {\n if (!options.recipient) {\n throw new Error('requirePayment: recipient wallet address is required');\n }\n\n return createX402Handler({\n price: options.amount,\n currency: options.currency,\n recipient: options.recipient,\n network: options.network,\n });\n}\n","// Main exports\nexport { payUpon402 } from './payUpon402';\nexport { createStripeClient } from './createStripeClient';\nexport { createX402Handler, requirePayment } from './middleware';\n\n// Re-export x402 utilities for convenience\nexport { createSigner } from 'x402-fetch';\nexport { createPaymentHeader, selectPaymentRequirements } from 'x402/client';\nexport type { PaymentRequirements, Signer, MultiNetworkSigner, X402Config } from 'x402/types';\n"],"mappings":"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","names":["createPaymentHeader","selectPaymentRequirements","createPaymentHeader","selectPaymentRequirements"]}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@moneymq/x402",
3
+ "version": "0.1.0",
4
+ "description": "x402 protocol integration for MoneyMQ",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/txtx/moneymq-js.git",
21
+ "directory": "packages/x402"
22
+ },
23
+ "keywords": [
24
+ "moneymq",
25
+ "x402",
26
+ "payments",
27
+ "micropayments",
28
+ "stablecoin"
29
+ ],
30
+ "author": "MoneyMQ",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "x402": "^0.7.1",
34
+ "x402-fetch": "^0.7.0"
35
+ },
36
+ "peerDependencies": {
37
+ "stripe": "^19.0.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "stripe": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20",
46
+ "stripe": "^19.2.0",
47
+ "tsup": "^8.0.0",
48
+ "typescript": "^5"
49
+ },
50
+ "scripts": {
51
+ "build": "tsup",
52
+ "dev": "tsup --watch",
53
+ "typecheck": "tsc --noEmit",
54
+ "clean": "rm -rf dist .turbo node_modules"
55
+ }
56
+ }