@mixrpay/merchant-sdk 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/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # @mixrpay/merchant-sdk
2
+
3
+ Add x402 payments to your API in minutes. Let AI agents pay for your services automatically with USDC - no signups, no API keys, no invoices.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @mixrpay/merchant-sdk
9
+ # or
10
+ yarn add @mixrpay/merchant-sdk
11
+ # or
12
+ pnpm add @mixrpay/merchant-sdk
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ### 1. Set Your Wallet Address
18
+
19
+ ```bash
20
+ # In your .env file
21
+ MIXRPAY_MERCHANT_ADDRESS=0xYourWalletAddress
22
+ ```
23
+
24
+ ### 2. Add to Your Routes
25
+
26
+ #### Express
27
+
28
+ ```typescript
29
+ import express from 'express';
30
+ import { x402 } from '@mixrpay/merchant-sdk/express';
31
+
32
+ const app = express();
33
+
34
+ // Fixed price - $0.05 per request
35
+ app.post('/api/query', x402({ price: 0.05 }), (req, res) => {
36
+ // This only runs after payment is verified
37
+ const { payer, amount, txHash } = req.x402Payment!;
38
+
39
+ console.log(`Payment received: $${amount} from ${payer}`);
40
+
41
+ const result = processQuery(req.body);
42
+ res.json(result);
43
+ });
44
+
45
+ // Dynamic pricing based on request
46
+ app.post('/api/generate', x402({
47
+ getPrice: (ctx) => ctx.body?.premium ? 0.10 : 0.05
48
+ }), handler);
49
+ ```
50
+
51
+ #### Next.js (App Router)
52
+
53
+ ```typescript
54
+ // app/api/query/route.ts
55
+ import { withX402 } from '@mixrpay/merchant-sdk/nextjs';
56
+ import { NextRequest, NextResponse } from 'next/server';
57
+
58
+ async function handler(req: NextRequest, payment: X402PaymentResult) {
59
+ // payment contains: { payer, amount, txHash, settledAt }
60
+ console.log(`Paid by ${payment.payer}: $${payment.amount}`);
61
+
62
+ return NextResponse.json({ result: 'success' });
63
+ }
64
+
65
+ export const POST = withX402({ price: 0.05 }, handler);
66
+ ```
67
+
68
+ #### Fastify
69
+
70
+ ```typescript
71
+ import Fastify from 'fastify';
72
+ import { x402Plugin, x402 } from '@mixrpay/merchant-sdk/fastify';
73
+
74
+ const app = Fastify();
75
+
76
+ // Register plugin with defaults
77
+ app.register(x402Plugin, {
78
+ recipient: process.env.MIXRPAY_MERCHANT_ADDRESS,
79
+ });
80
+
81
+ // Use on routes
82
+ app.post('/api/query', {
83
+ preHandler: x402({ price: 0.05 })
84
+ }, async (req, reply) => {
85
+ return { result: 'success', payer: req.x402Payment?.payer };
86
+ });
87
+ ```
88
+
89
+ ## How It Works
90
+
91
+ 1. **Client makes request** without payment header
92
+ 2. **Server returns 402** with payment requirements (amount, recipient, etc.)
93
+ 3. **Client signs USDC transfer** using their session key
94
+ 4. **Client retries** with `X-PAYMENT` header containing signed authorization
95
+ 5. **Server verifies signature** and submits to facilitator for settlement
96
+ 6. **Server processes request** after payment confirmation
97
+
98
+ ```
99
+ Client Server Facilitator
100
+ | | |
101
+ |-- POST /api/query ------------>| |
102
+ |<-- 402 Payment Required -------| |
103
+ | | |
104
+ |-- POST /api/query ------------>| |
105
+ | X-PAYMENT: <signed auth> | |
106
+ | |-- verify & settle ---------->|
107
+ | |<-- tx hash -----------------|
108
+ |<-- 200 OK --------------------| |
109
+ ```
110
+
111
+ ## Configuration Options
112
+
113
+ ### X402Options
114
+
115
+ ```typescript
116
+ interface X402Options {
117
+ /** Price in USD (e.g., 0.05 for 5 cents) */
118
+ price: number;
119
+
120
+ /** Your wallet address (defaults to MIXRPAY_MERCHANT_ADDRESS env var) */
121
+ recipient?: string;
122
+
123
+ /** Chain ID (default: 8453 for Base) */
124
+ chainId?: number;
125
+
126
+ /** Facilitator URL (default: https://x402.org/facilitator) */
127
+ facilitator?: string;
128
+
129
+ /** Description shown to payer */
130
+ description?: string;
131
+
132
+ /** Dynamic pricing function */
133
+ getPrice?: (context: PriceContext) => number | Promise<number>;
134
+
135
+ /** Callback after successful payment */
136
+ onPayment?: (payment: X402PaymentResult) => void | Promise<void>;
137
+
138
+ /** Skip payment for certain requests */
139
+ skip?: (context: SkipContext) => boolean | Promise<boolean>;
140
+
141
+ /** Test mode - skips on-chain settlement */
142
+ testMode?: boolean;
143
+ }
144
+ ```
145
+
146
+ ### Payment Result
147
+
148
+ ```typescript
149
+ interface X402PaymentResult {
150
+ /** Whether payment is valid */
151
+ valid: boolean;
152
+
153
+ /** Payer's wallet address */
154
+ payer?: string;
155
+
156
+ /** Amount paid in USD */
157
+ amount?: number;
158
+
159
+ /** Settlement transaction hash */
160
+ txHash?: string;
161
+
162
+ /** When payment was settled */
163
+ settledAt?: Date;
164
+
165
+ /** Error message if invalid */
166
+ error?: string;
167
+ }
168
+ ```
169
+
170
+ ## Advanced Usage
171
+
172
+ ### Dynamic Pricing
173
+
174
+ ```typescript
175
+ app.post('/api/ai', x402({
176
+ getPrice: async (ctx) => {
177
+ const { model, tokens } = ctx.body;
178
+
179
+ // Price based on model and usage
180
+ const rates = { 'gpt-4': 0.03, 'gpt-3.5': 0.002 };
181
+ const rate = rates[model] || 0.01;
182
+
183
+ return rate * (tokens / 1000);
184
+ }
185
+ }), handler);
186
+ ```
187
+
188
+ ### Skip Payment for Certain Requests
189
+
190
+ ```typescript
191
+ app.post('/api/query', x402({
192
+ price: 0.05,
193
+ skip: (ctx) => {
194
+ // Free for authenticated premium users
195
+ return ctx.headers['x-premium-token'] === 'valid';
196
+ }
197
+ }), handler);
198
+ ```
199
+
200
+ ### Payment Callbacks
201
+
202
+ ```typescript
203
+ app.post('/api/query', x402({
204
+ price: 0.05,
205
+ onPayment: async (payment) => {
206
+ // Log to your analytics
207
+ await analytics.track('payment_received', {
208
+ payer: payment.payer,
209
+ amount: payment.amount,
210
+ txHash: payment.txHash,
211
+ });
212
+
213
+ // Store in database
214
+ await db.payments.create({
215
+ data: {
216
+ payer: payment.payer,
217
+ amount: payment.amount,
218
+ txHash: payment.txHash,
219
+ }
220
+ });
221
+ }
222
+ }), handler);
223
+ ```
224
+
225
+ ### Custom Verification
226
+
227
+ ```typescript
228
+ import { verifyX402Payment, usdToMinor } from '@mixrpay/merchant-sdk';
229
+
230
+ // Manual verification without middleware
231
+ const result = await verifyX402Payment(paymentHeader, {
232
+ expectedAmount: usdToMinor(0.05),
233
+ expectedRecipient: '0x...',
234
+ chainId: 8453,
235
+ });
236
+
237
+ if (result.valid) {
238
+ console.log(`Valid payment from ${result.payer}`);
239
+ }
240
+ ```
241
+
242
+ ## Testing
243
+
244
+ Enable test mode to skip on-chain settlement during development:
245
+
246
+ ```typescript
247
+ app.post('/api/query', x402({
248
+ price: 0.05,
249
+ testMode: process.env.NODE_ENV !== 'production',
250
+ }), handler);
251
+ ```
252
+
253
+ ## Environment Variables
254
+
255
+ | Variable | Description | Required |
256
+ |----------|-------------|----------|
257
+ | `MIXRPAY_MERCHANT_ADDRESS` | Your wallet address to receive payments | Yes (or pass `recipient` option) |
258
+
259
+ ## Supported Chains
260
+
261
+ | Chain | Chain ID | USDC Contract |
262
+ |-------|----------|---------------|
263
+ | Base Mainnet | 8453 | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
264
+ | Base Sepolia | 84532 | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` |
265
+
266
+ ## Error Handling
267
+
268
+ The middleware returns appropriate HTTP status codes:
269
+
270
+ - `402 Payment Required` - No payment or invalid payment
271
+ - `500 Internal Server Error` - Configuration issues
272
+
273
+ Error responses include details:
274
+
275
+ ```json
276
+ {
277
+ "error": "Invalid payment",
278
+ "reason": "Insufficient payment: expected 50000, got 10000"
279
+ }
280
+ ```
281
+
282
+ ## TypeScript Support
283
+
284
+ Full TypeScript support with type definitions included.
285
+
286
+ ```typescript
287
+ import type { X402Options, X402PaymentResult } from '@mixrpay/merchant-sdk';
288
+ ```
289
+
290
+ ## License
291
+
292
+ MIT
293
+
@@ -0,0 +1,291 @@
1
+ import { V as VerifyOptions, X as X402PaymentResult, a as VerifyReceiptOptions, P as PaymentReceipt } from './types-BJNy6Hhb.mjs';
2
+ export { E as EIP712Domain, e as PriceContext, R as ReceiptMode, S as SkipContext, T as TransferWithAuthorizationMessage, b as X402Options, d as X402PaymentPayload, c as X402PaymentRequired } from './types-BJNy6Hhb.mjs';
3
+ export { default as expressX402 } from './middleware/express.mjs';
4
+ export { createPaymentRequired, withX402 } from './middleware/nextjs.mjs';
5
+ export { x402 as fastifyX402, x402Plugin } from './middleware/fastify.mjs';
6
+ import 'express';
7
+ import 'next/server';
8
+ import 'fastify';
9
+
10
+ /**
11
+ * MixrPay Merchant SDK - Payment Verification
12
+ */
13
+
14
+ /**
15
+ * Verify an X-PAYMENT header and optionally settle the payment.
16
+ *
17
+ * @param paymentHeader - The X-PAYMENT header value (base64 encoded JSON)
18
+ * @param options - Verification options
19
+ * @returns Payment verification result
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const result = await verifyX402Payment(req.headers['x-payment'], {
24
+ * expectedAmount: 50000n, // 0.05 USDC
25
+ * expectedRecipient: '0x...',
26
+ * });
27
+ *
28
+ * if (result.valid) {
29
+ * console.log(`Payment from ${result.payer}: $${result.amount}`);
30
+ * }
31
+ * ```
32
+ */
33
+ declare function verifyX402Payment(paymentHeader: string, options: VerifyOptions): Promise<X402PaymentResult>;
34
+ /**
35
+ * Parse and validate an X-PAYMENT header without settlement.
36
+ * Useful for checking if a payment is structurally valid before processing.
37
+ */
38
+ declare function parseX402Payment(paymentHeader: string, chainId?: number): Promise<{
39
+ valid: boolean;
40
+ error?: string;
41
+ payer?: string;
42
+ recipient?: string;
43
+ amount?: number;
44
+ amountMinor?: bigint;
45
+ expiresAt?: Date;
46
+ }>;
47
+
48
+ /**
49
+ * MixrPay Merchant SDK - Payment Receipt Verification
50
+ *
51
+ * Verify JWT payment receipts issued by MixrPay after successful x402 payments.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import { verifyPaymentReceipt } from '@mixrpay/merchant-sdk';
56
+ *
57
+ * const receipt = req.headers['x-payment-receipt'];
58
+ * const payment = await verifyPaymentReceipt(receipt);
59
+ *
60
+ * console.log(`Payment received: $${payment.amountUsd} from ${payment.payer}`);
61
+ * console.log(`Transaction: ${payment.txHash}`);
62
+ * ```
63
+ */
64
+
65
+ /**
66
+ * Verify a JWT payment receipt from MixrPay.
67
+ *
68
+ * This function:
69
+ * 1. Fetches the JWKS from MixrPay (cached for 1 hour)
70
+ * 2. Verifies the JWT signature using RS256
71
+ * 3. Validates standard JWT claims (exp, iat)
72
+ * 4. Returns the typed payment receipt
73
+ *
74
+ * @param receipt - The JWT receipt string from X-Payment-Receipt header
75
+ * @param options - Optional configuration (custom JWKS URL, issuer validation)
76
+ * @returns Verified payment receipt
77
+ * @throws Error if verification fails
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // Basic usage
82
+ * const payment = await verifyPaymentReceipt(receipt);
83
+ *
84
+ * // With custom JWKS URL (for testing or self-hosted)
85
+ * const payment = await verifyPaymentReceipt(receipt, {
86
+ * jwksUrl: 'https://your-mixrpay.com/.well-known/jwks'
87
+ * });
88
+ * ```
89
+ */
90
+ declare function verifyPaymentReceipt(receipt: string, options?: VerifyReceiptOptions): Promise<PaymentReceipt>;
91
+ /**
92
+ * Parse a JWT payment receipt without verification.
93
+ *
94
+ * WARNING: This does NOT verify the signature. Use only for debugging
95
+ * or when you've already verified the receipt elsewhere.
96
+ *
97
+ * @param receipt - The JWT receipt string
98
+ * @returns Decoded payload (unverified)
99
+ */
100
+ declare function parsePaymentReceipt(receipt: string): PaymentReceipt;
101
+ /**
102
+ * Check if a receipt is expired.
103
+ *
104
+ * @param receipt - The JWT receipt string or parsed PaymentReceipt
105
+ * @returns true if expired, false otherwise
106
+ */
107
+ declare function isReceiptExpired(receipt: string | PaymentReceipt): boolean;
108
+ /**
109
+ * Clear the JWKS cache.
110
+ * Useful for testing or when keys have rotated.
111
+ */
112
+ declare function clearJWKSCache(): void;
113
+ /**
114
+ * Get the default JWKS URL.
115
+ */
116
+ declare function getDefaultJWKSUrl(): string;
117
+
118
+ /**
119
+ * MixrPay Merchant SDK - Utilities
120
+ */
121
+
122
+ declare const USDC_CONTRACTS: Record<number, `0x${string}`>;
123
+ declare const DEFAULT_FACILITATOR = "https://x402.org/facilitator";
124
+ /**
125
+ * Convert USD dollars to USDC minor units (6 decimals)
126
+ */
127
+ declare function usdToMinor(usd: number): bigint;
128
+ /**
129
+ * Convert USDC minor units to USD dollars
130
+ */
131
+ declare function minorToUsd(minor: bigint): number;
132
+ /**
133
+ * Generate a random nonce for x402 payments
134
+ */
135
+ declare function generateNonce(): `0x${string}`;
136
+ /**
137
+ * Check if an address is valid
138
+ */
139
+ declare function isValidAddress(address: string): address is `0x${string}`;
140
+ /**
141
+ * Normalize an address to lowercase checksum format
142
+ */
143
+ declare function normalizeAddress(address: string): `0x${string}`;
144
+
145
+ /**
146
+ * MixrPay Charges Module
147
+ *
148
+ * Create and manage charges using session signers.
149
+ * Session signers allow you to charge user wallets without
150
+ * requiring approval for each transaction.
151
+ */
152
+ interface CreateChargeParams {
153
+ /** Session key ID granted by the user */
154
+ sessionId: string;
155
+ /** Amount to charge in USD (e.g., 0.05 for 5 cents) */
156
+ amountUsd: number;
157
+ /** Unique reference for idempotency (e.g., "order_123") */
158
+ reference: string;
159
+ /** Optional metadata */
160
+ metadata?: Record<string, unknown>;
161
+ }
162
+ interface Charge {
163
+ /** Unique charge ID */
164
+ id: string;
165
+ /** Current status */
166
+ status: 'pending' | 'submitted' | 'confirmed' | 'failed';
167
+ /** Amount in USD */
168
+ amountUsd: number;
169
+ /** Formatted USDC amount */
170
+ amountUsdc: string;
171
+ /** Transaction hash (if submitted) */
172
+ txHash?: string;
173
+ /** Block explorer URL */
174
+ explorerUrl?: string;
175
+ /** From wallet address */
176
+ fromAddress: string;
177
+ /** To wallet address */
178
+ toAddress: string;
179
+ /** Your reference */
180
+ reference: string;
181
+ /** When the charge was created */
182
+ createdAt: string;
183
+ /** When the charge was confirmed */
184
+ confirmedAt?: string;
185
+ /** Session name */
186
+ sessionName?: string;
187
+ /** User wallet address */
188
+ userWallet?: string;
189
+ /** Error message if failed */
190
+ error?: string;
191
+ }
192
+ interface CreateChargeResult {
193
+ success: boolean;
194
+ charge?: Charge;
195
+ /** True if this is a replay of an existing charge */
196
+ idempotentReplay?: boolean;
197
+ error?: string;
198
+ details?: string;
199
+ }
200
+ interface SessionGrant {
201
+ /** Session key ID */
202
+ sessionId: string;
203
+ /** User's wallet address */
204
+ userWallet: string;
205
+ /** Your merchant wallet address */
206
+ merchantWallet: string;
207
+ /** Spending limits */
208
+ limits: {
209
+ maxTotalUsd?: number;
210
+ maxPerTxUsd?: number;
211
+ expiresAt: string;
212
+ };
213
+ /** When the session was granted */
214
+ grantedAt: string;
215
+ }
216
+ interface ChargesClientOptions {
217
+ /** Your API key */
218
+ apiKey: string;
219
+ /** MixrPay API base URL */
220
+ baseUrl?: string;
221
+ }
222
+ declare class ChargesClient {
223
+ private apiKey;
224
+ private baseUrl;
225
+ constructor(options: ChargesClientOptions);
226
+ /**
227
+ * Create a new charge.
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * const result = await charges.create({
232
+ * sessionId: 'sk_xxx',
233
+ * amountUsd: 0.05,
234
+ * reference: 'image_gen_123',
235
+ * metadata: { feature: 'image_generation' }
236
+ * });
237
+ *
238
+ * if (result.success) {
239
+ * console.log(`Charged ${result.charge.amountUsdc}`);
240
+ * console.log(`TX: ${result.charge.explorerUrl}`);
241
+ * }
242
+ * ```
243
+ */
244
+ create(params: CreateChargeParams): Promise<CreateChargeResult>;
245
+ /**
246
+ * Get a charge by ID.
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * const charge = await charges.get('chg_xxx');
251
+ * console.log(charge.status); // 'confirmed'
252
+ * ```
253
+ */
254
+ get(chargeId: string): Promise<Charge | null>;
255
+ /**
256
+ * Get a charge by transaction hash.
257
+ */
258
+ getByTxHash(txHash: string): Promise<Charge | null>;
259
+ }
260
+ /**
261
+ * Verify a session.granted webhook signature.
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * const payload = req.body;
266
+ * const signature = req.headers['x-mixrpay-signature'];
267
+ *
268
+ * if (verifySessionWebhook(JSON.stringify(payload), signature, webhookSecret)) {
269
+ * const grant = parseSessionGrant(payload);
270
+ * console.log(`User ${grant.userWallet} granted access`);
271
+ * }
272
+ * ```
273
+ */
274
+ declare function verifySessionWebhook(payload: string, signature: string, secret: string): boolean;
275
+ /**
276
+ * Parse a session.granted webhook payload.
277
+ */
278
+ declare function parseSessionGrant(payload: {
279
+ event: string;
280
+ session_id: string;
281
+ user_wallet: string;
282
+ merchant_wallet: string;
283
+ limits: {
284
+ max_total_usd?: number;
285
+ max_per_tx_usd?: number;
286
+ expires_at: string;
287
+ };
288
+ granted_at: string;
289
+ }): SessionGrant;
290
+
291
+ export { type Charge, ChargesClient, type ChargesClientOptions, type CreateChargeParams, type CreateChargeResult, DEFAULT_FACILITATOR, PaymentReceipt, type SessionGrant, USDC_CONTRACTS, VerifyOptions, VerifyReceiptOptions, X402PaymentResult, clearJWKSCache, generateNonce, getDefaultJWKSUrl, isReceiptExpired, isValidAddress, minorToUsd, normalizeAddress, parsePaymentReceipt, parseSessionGrant, parseX402Payment, usdToMinor, verifyPaymentReceipt, verifySessionWebhook, verifyX402Payment };