@autopayprotocol/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,107 @@
1
+ # @autopayprotocol/sdk
2
+
3
+ Server-side utility package for merchants integrating with [AutoPay Protocol](https://autopay.xyz).
4
+
5
+ This SDK does **not** interact with the blockchain or manage wallets. It provides typed, zero-dependency helpers for the things merchants need on their backend: building checkout URLs, verifying webhook signatures, and working with USDC amounts/intervals.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @autopayprotocol/sdk
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### Build a checkout URL
16
+
17
+ ```typescript
18
+ import { createCheckoutUrl } from '@autopayprotocol/sdk'
19
+
20
+ const url = createCheckoutUrl({
21
+ merchant: '0x2B8b9182c1c3A9bEf4a60951D9B7F49420D12B9B',
22
+ amount: 9.99,
23
+ interval: 'monthly',
24
+ metadataUrl: 'https://mysite.com/plans/pro.json',
25
+ successUrl: 'https://mysite.com/success',
26
+ cancelUrl: 'https://mysite.com/cancel',
27
+ spendingCap: 119.88,
28
+ })
29
+ ```
30
+
31
+ ### Verify webhooks
32
+
33
+ ```typescript
34
+ import { verifyWebhook } from '@autopayprotocol/sdk'
35
+
36
+ const event = verifyWebhook(rawBody, req.headers['x-autopay-signature'], secret)
37
+
38
+ if (event.type === 'charge.succeeded') {
39
+ console.log(event.data.amount) // TypeScript knows this exists
40
+ console.log(event.data.protocolFee)
41
+ console.log(event.data.txHash)
42
+ }
43
+ ```
44
+
45
+ ## API
46
+
47
+ ### Checkout
48
+
49
+ | Function | Description |
50
+ |----------|-------------|
51
+ | `createCheckoutUrl(options)` | Build a checkout URL with validation |
52
+ | `parseSuccessRedirect(queryString)` | Parse `policyId` and `txHash` from success redirect |
53
+ | `resolveInterval(preset \| seconds)` | Convert interval preset to seconds |
54
+
55
+ ### Webhooks
56
+
57
+ | Function | Description |
58
+ |----------|-------------|
59
+ | `verifyWebhook(body, signature, secret)` | Verify + parse webhook (discriminated union) |
60
+ | `verifySignature(payload, signature, secret)` | Verify HMAC-SHA256 signature only |
61
+ | `signPayload(payload, secret)` | Sign a payload (for testing) |
62
+
63
+ ### Amounts
64
+
65
+ | Function | Description |
66
+ |----------|-------------|
67
+ | `formatUSDC(rawAmount)` | `"9990000"` → `"9.99"` |
68
+ | `parseUSDC(amount)` | `9.99` → `"9990000"` |
69
+ | `calculateFeeBreakdown(rawAmount)` | Total, merchant receives, protocol fee |
70
+ | `formatInterval(seconds)` | `2592000` → `"monthly"` |
71
+
72
+ ### Metadata
73
+
74
+ | Function | Description |
75
+ |----------|-------------|
76
+ | `validateMetadata(data)` | Validate JSON against metadata schema |
77
+ | `createMetadata(options)` | Create a valid metadata object |
78
+
79
+ ### Constants
80
+
81
+ | Export | Value |
82
+ |--------|-------|
83
+ | `intervals.monthly` | `2_592_000` |
84
+ | `intervals.weekly` | `604_800` |
85
+ | `intervals.custom(14, 'days')` | `1_209_600` |
86
+ | `PROTOCOL_FEE_BPS` | `250` (2.5%) |
87
+ | `chains` | Chain configs (Polygon Amoy, Arbitrum Sepolia, Arc Testnet) |
88
+
89
+ ### Webhook Event Types
90
+
91
+ All events share `{ type, timestamp, data: { policyId, chainId, payer, merchant } }` plus event-specific fields:
92
+
93
+ | Event | Extra Fields |
94
+ |-------|-------------|
95
+ | `charge.succeeded` | `amount`, `protocolFee`, `txHash` |
96
+ | `charge.failed` | `reason` |
97
+ | `policy.created` | `chargeAmount`, `interval`, `spendingCap`, `metadataUrl` |
98
+ | `policy.revoked` | `endTime` |
99
+ | `policy.cancelled_by_failure` | `consecutiveFailures`, `endTime` |
100
+
101
+ ## Zero Dependencies
102
+
103
+ This package has **zero runtime dependencies**. It only uses Node.js built-in `crypto`.
104
+
105
+ ## License
106
+
107
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,434 @@
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
+ AutoPayCheckoutError: () => AutoPayCheckoutError,
24
+ AutoPayError: () => AutoPayError,
25
+ AutoPayMetadataError: () => AutoPayMetadataError,
26
+ AutoPayWebhookError: () => AutoPayWebhookError,
27
+ DEFAULT_CHECKOUT_BASE_URL: () => DEFAULT_CHECKOUT_BASE_URL,
28
+ MAX_INTERVAL: () => MAX_INTERVAL,
29
+ MAX_RETRIES: () => MAX_RETRIES,
30
+ MIN_INTERVAL: () => MIN_INTERVAL,
31
+ PROTOCOL_FEE_BPS: () => PROTOCOL_FEE_BPS,
32
+ USDC_DECIMALS: () => USDC_DECIMALS,
33
+ calculateFeeBreakdown: () => calculateFeeBreakdown,
34
+ chains: () => chains,
35
+ createCheckoutUrl: () => createCheckoutUrl,
36
+ createMetadata: () => createMetadata,
37
+ formatInterval: () => formatInterval,
38
+ formatUSDC: () => formatUSDC,
39
+ intervals: () => intervals,
40
+ parseSuccessRedirect: () => parseSuccessRedirect,
41
+ parseUSDC: () => parseUSDC,
42
+ resolveInterval: () => resolveInterval,
43
+ signPayload: () => signPayload,
44
+ validateMetadata: () => validateMetadata,
45
+ verifySignature: () => verifySignature,
46
+ verifyWebhook: () => verifyWebhook
47
+ });
48
+ module.exports = __toCommonJS(index_exports);
49
+
50
+ // src/errors.ts
51
+ var AutoPayError = class extends Error {
52
+ constructor(message, code) {
53
+ super(message);
54
+ this.code = code;
55
+ this.name = "AutoPayError";
56
+ }
57
+ };
58
+ var AutoPayWebhookError = class extends AutoPayError {
59
+ constructor(message) {
60
+ super(message, "WEBHOOK_VERIFICATION_FAILED");
61
+ this.name = "AutoPayWebhookError";
62
+ }
63
+ };
64
+ var AutoPayCheckoutError = class extends AutoPayError {
65
+ constructor(message) {
66
+ super(message, "INVALID_CHECKOUT_PARAMS");
67
+ this.name = "AutoPayCheckoutError";
68
+ }
69
+ };
70
+ var AutoPayMetadataError = class extends AutoPayError {
71
+ constructor(message) {
72
+ super(message, "INVALID_METADATA");
73
+ this.name = "AutoPayMetadataError";
74
+ }
75
+ };
76
+
77
+ // src/constants.ts
78
+ var intervals = {
79
+ /** 1 minute — useful for testing */
80
+ minute: 60,
81
+ /** 7 days */
82
+ weekly: 604800,
83
+ /** 14 days */
84
+ biweekly: 1209600,
85
+ /** 30 days */
86
+ monthly: 2592e3,
87
+ /** 90 days */
88
+ quarterly: 7776e3,
89
+ /** 365 days */
90
+ yearly: 31536e3,
91
+ /** Build a custom interval from a count and unit */
92
+ custom(count, unit) {
93
+ const multipliers = {
94
+ minutes: 60,
95
+ hours: 3600,
96
+ days: 86400,
97
+ months: 2592e3,
98
+ // 30 days
99
+ years: 31536e3
100
+ // 365 days
101
+ };
102
+ return count * multipliers[unit];
103
+ }
104
+ };
105
+ var PROTOCOL_FEE_BPS = 250;
106
+ var USDC_DECIMALS = 6;
107
+ var MIN_INTERVAL = 60;
108
+ var MAX_INTERVAL = 31536e3;
109
+ var MAX_RETRIES = 3;
110
+ var chains = {
111
+ polygonAmoy: {
112
+ name: "Polygon Amoy",
113
+ chainId: 80002,
114
+ cctpDomain: 7,
115
+ usdc: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
116
+ explorer: "https://amoy.polygonscan.com"
117
+ },
118
+ arbitrumSepolia: {
119
+ name: "Arbitrum Sepolia",
120
+ chainId: 421614,
121
+ cctpDomain: 3,
122
+ usdc: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
123
+ explorer: "https://sepolia.arbiscan.io"
124
+ },
125
+ arcTestnet: {
126
+ name: "Arc Testnet",
127
+ chainId: 1868,
128
+ cctpDomain: 26,
129
+ usdc: "0x3600000000000000000000000000000000000000",
130
+ explorer: "https://explorer-testnet.arc.gel.network"
131
+ }
132
+ };
133
+ var DEFAULT_CHECKOUT_BASE_URL = "https://app.autopay.xyz";
134
+
135
+ // src/checkout.ts
136
+ var INTERVAL_MAP = {
137
+ weekly: intervals.weekly,
138
+ biweekly: intervals.biweekly,
139
+ monthly: intervals.monthly,
140
+ quarterly: intervals.quarterly,
141
+ yearly: intervals.yearly
142
+ };
143
+ function resolveInterval(interval) {
144
+ if (typeof interval === "number") return interval;
145
+ const seconds = INTERVAL_MAP[interval];
146
+ if (!seconds) {
147
+ throw new AutoPayCheckoutError(
148
+ `Invalid interval preset "${interval}". Use: ${Object.keys(INTERVAL_MAP).join(", ")} or a number of seconds.`
149
+ );
150
+ }
151
+ return seconds;
152
+ }
153
+ function isValidAddress(value) {
154
+ return /^0x[a-fA-F0-9]{40}$/.test(value);
155
+ }
156
+ function isValidUrl(value) {
157
+ try {
158
+ new URL(value);
159
+ return true;
160
+ } catch {
161
+ return false;
162
+ }
163
+ }
164
+ function createCheckoutUrl(options) {
165
+ const { merchant, amount, interval, metadataUrl, successUrl, cancelUrl, spendingCap, baseUrl } = options;
166
+ if (!merchant || !isValidAddress(merchant)) {
167
+ throw new AutoPayCheckoutError(`Invalid merchant address: ${merchant}`);
168
+ }
169
+ if (typeof amount !== "number" || amount <= 0 || !Number.isFinite(amount)) {
170
+ throw new AutoPayCheckoutError(`Invalid amount: ${amount}. Must be a positive number.`);
171
+ }
172
+ const intervalSeconds = resolveInterval(interval);
173
+ if (intervalSeconds < MIN_INTERVAL || intervalSeconds > MAX_INTERVAL) {
174
+ throw new AutoPayCheckoutError(
175
+ `Interval ${intervalSeconds}s out of range. Must be between ${MIN_INTERVAL}s and ${MAX_INTERVAL}s.`
176
+ );
177
+ }
178
+ if (!isValidUrl(metadataUrl)) {
179
+ throw new AutoPayCheckoutError(`Invalid metadata URL: ${metadataUrl}`);
180
+ }
181
+ if (!isValidUrl(successUrl)) {
182
+ throw new AutoPayCheckoutError(`Invalid success URL: ${successUrl}`);
183
+ }
184
+ if (!isValidUrl(cancelUrl)) {
185
+ throw new AutoPayCheckoutError(`Invalid cancel URL: ${cancelUrl}`);
186
+ }
187
+ if (spendingCap !== void 0) {
188
+ if (typeof spendingCap !== "number" || spendingCap <= 0 || !Number.isFinite(spendingCap)) {
189
+ throw new AutoPayCheckoutError(`Invalid spending cap: ${spendingCap}. Must be a positive number.`);
190
+ }
191
+ if (spendingCap < amount) {
192
+ throw new AutoPayCheckoutError(`Spending cap (${spendingCap}) must be >= amount (${amount}).`);
193
+ }
194
+ }
195
+ const base = baseUrl || DEFAULT_CHECKOUT_BASE_URL;
196
+ const url = new URL("/checkout", base);
197
+ url.searchParams.set("merchant", merchant);
198
+ url.searchParams.set("amount", String(amount));
199
+ url.searchParams.set("interval", String(intervalSeconds));
200
+ url.searchParams.set("metadata_url", metadataUrl);
201
+ url.searchParams.set("success_url", successUrl);
202
+ url.searchParams.set("cancel_url", cancelUrl);
203
+ if (spendingCap !== void 0) {
204
+ url.searchParams.set("spending_cap", String(spendingCap));
205
+ }
206
+ return url.toString();
207
+ }
208
+ function parseSuccessRedirect(queryString) {
209
+ const params = new URLSearchParams(queryString);
210
+ const policyId = params.get("policyId");
211
+ const txHash = params.get("txHash");
212
+ if (!policyId) {
213
+ throw new AutoPayCheckoutError("Missing policyId in success redirect URL");
214
+ }
215
+ if (!txHash) {
216
+ throw new AutoPayCheckoutError("Missing txHash in success redirect URL");
217
+ }
218
+ return { policyId, txHash };
219
+ }
220
+
221
+ // src/webhooks.ts
222
+ var import_crypto = require("crypto");
223
+ var VALID_EVENT_TYPES = [
224
+ "charge.succeeded",
225
+ "charge.failed",
226
+ "policy.created",
227
+ "policy.revoked",
228
+ "policy.cancelled_by_failure"
229
+ ];
230
+ function signPayload(payload, secret) {
231
+ return (0, import_crypto.createHmac)("sha256", secret).update(payload).digest("hex");
232
+ }
233
+ function verifySignature(payload, signature, secret) {
234
+ const expected = signPayload(payload, secret);
235
+ return timingSafeEqual(expected, signature);
236
+ }
237
+ function verifyWebhook(rawBody, signature, secret) {
238
+ if (!signature) {
239
+ throw new AutoPayWebhookError("Missing x-autopay-signature header");
240
+ }
241
+ if (!secret) {
242
+ throw new AutoPayWebhookError("Webhook secret is not configured");
243
+ }
244
+ if (!verifySignature(rawBody, signature, secret)) {
245
+ throw new AutoPayWebhookError("Invalid webhook signature");
246
+ }
247
+ let parsed;
248
+ try {
249
+ parsed = JSON.parse(rawBody);
250
+ } catch {
251
+ throw new AutoPayWebhookError("Invalid JSON in webhook body");
252
+ }
253
+ const event = parsed;
254
+ if (!event || typeof event !== "object") {
255
+ throw new AutoPayWebhookError("Webhook body is not an object");
256
+ }
257
+ const eventType = event.event ?? event.type;
258
+ if (!eventType || !VALID_EVENT_TYPES.includes(eventType)) {
259
+ throw new AutoPayWebhookError(`Unknown webhook event type: ${eventType}`);
260
+ }
261
+ return {
262
+ type: eventType,
263
+ timestamp: event.timestamp,
264
+ data: event.data
265
+ };
266
+ }
267
+ function timingSafeEqual(a, b) {
268
+ if (a.length !== b.length) return false;
269
+ let result = 0;
270
+ for (let i = 0; i < a.length; i++) {
271
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
272
+ }
273
+ return result === 0;
274
+ }
275
+
276
+ // src/amounts.ts
277
+ var USDC_FACTOR = 10 ** USDC_DECIMALS;
278
+ function formatUSDC(rawAmount) {
279
+ const num = Number(rawAmount) / USDC_FACTOR;
280
+ const fixed = num.toFixed(6);
281
+ const parts = fixed.split(".");
282
+ let decimals = parts[1].replace(/0+$/, "");
283
+ if (decimals.length < 2) decimals = decimals.padEnd(2, "0");
284
+ return `${parts[0]}.${decimals}`;
285
+ }
286
+ function parseUSDC(amount) {
287
+ const str = amount.toFixed(USDC_DECIMALS);
288
+ const [whole, frac] = str.split(".");
289
+ const padded = (frac || "").padEnd(USDC_DECIMALS, "0").slice(0, USDC_DECIMALS);
290
+ const raw = BigInt(whole) * BigInt(USDC_FACTOR) + BigInt(padded);
291
+ return raw.toString();
292
+ }
293
+ function calculateFeeBreakdown(rawAmount) {
294
+ const totalRaw = BigInt(rawAmount);
295
+ const feeRaw = totalRaw * BigInt(PROTOCOL_FEE_BPS) / 10000n;
296
+ const merchantRaw = totalRaw - feeRaw;
297
+ return {
298
+ total: formatUSDC(totalRaw.toString()),
299
+ merchantReceives: formatUSDC(merchantRaw.toString()),
300
+ protocolFee: formatUSDC(feeRaw.toString()),
301
+ feePercentage: `${PROTOCOL_FEE_BPS / 100}%`
302
+ };
303
+ }
304
+ function formatInterval(seconds) {
305
+ const presets = {
306
+ 604800: "weekly",
307
+ 1209600: "biweekly",
308
+ 2592e3: "monthly",
309
+ 7776e3: "quarterly",
310
+ 31536e3: "yearly"
311
+ };
312
+ if (presets[seconds]) return presets[seconds];
313
+ const days = Math.floor(seconds / 86400);
314
+ const hours = Math.floor(seconds % 86400 / 3600);
315
+ const minutes = Math.floor(seconds % 3600 / 60);
316
+ if (days > 0 && hours > 0) return `${days}d ${hours}h`;
317
+ if (days > 0) return days === 1 ? "1 day" : `${days} days`;
318
+ if (hours > 0) return hours === 1 ? "1 hour" : `${hours} hours`;
319
+ if (minutes > 0) return minutes === 1 ? "1 minute" : `${minutes} minutes`;
320
+ return `${seconds}s`;
321
+ }
322
+
323
+ // src/metadata.ts
324
+ function validateMetadata(data) {
325
+ const errors = [];
326
+ if (!data || typeof data !== "object") {
327
+ return { valid: false, errors: ["Metadata must be an object"] };
328
+ }
329
+ const obj = data;
330
+ if (typeof obj.version !== "string" || !obj.version) {
331
+ errors.push('Missing or invalid "version" (must be a non-empty string)');
332
+ }
333
+ if (!obj.plan || typeof obj.plan !== "object") {
334
+ errors.push('Missing or invalid "plan" (must be an object)');
335
+ } else {
336
+ const plan = obj.plan;
337
+ if (typeof plan.name !== "string" || !plan.name) {
338
+ errors.push('Missing or invalid "plan.name" (must be a non-empty string)');
339
+ }
340
+ if (typeof plan.description !== "string" || !plan.description) {
341
+ errors.push('Missing or invalid "plan.description" (must be a non-empty string)');
342
+ }
343
+ if (plan.tier !== void 0 && typeof plan.tier !== "string") {
344
+ errors.push('"plan.tier" must be a string if provided');
345
+ }
346
+ if (plan.features !== void 0) {
347
+ if (!Array.isArray(plan.features) || !plan.features.every((f) => typeof f === "string")) {
348
+ errors.push('"plan.features" must be an array of strings if provided');
349
+ }
350
+ }
351
+ }
352
+ if (!obj.merchant || typeof obj.merchant !== "object") {
353
+ errors.push('Missing or invalid "merchant" (must be an object)');
354
+ } else {
355
+ const merchant = obj.merchant;
356
+ if (typeof merchant.name !== "string" || !merchant.name) {
357
+ errors.push('Missing or invalid "merchant.name" (must be a non-empty string)');
358
+ }
359
+ if (merchant.logo !== void 0 && typeof merchant.logo !== "string") {
360
+ errors.push('"merchant.logo" must be a string if provided');
361
+ }
362
+ if (merchant.website !== void 0 && typeof merchant.website !== "string") {
363
+ errors.push('"merchant.website" must be a string if provided');
364
+ }
365
+ if (merchant.supportEmail !== void 0 && typeof merchant.supportEmail !== "string") {
366
+ errors.push('"merchant.supportEmail" must be a string if provided');
367
+ }
368
+ }
369
+ if (obj.display !== void 0) {
370
+ if (typeof obj.display !== "object" || obj.display === null) {
371
+ errors.push('"display" must be an object if provided');
372
+ } else {
373
+ const display = obj.display;
374
+ if (display.color !== void 0 && typeof display.color !== "string") {
375
+ errors.push('"display.color" must be a string if provided');
376
+ }
377
+ if (display.badge !== void 0 && typeof display.badge !== "string") {
378
+ errors.push('"display.badge" must be a string if provided');
379
+ }
380
+ }
381
+ }
382
+ return { valid: errors.length === 0, errors };
383
+ }
384
+ function createMetadata(options) {
385
+ const metadata = {
386
+ version: "1.0",
387
+ plan: {
388
+ name: options.planName,
389
+ description: options.planDescription
390
+ },
391
+ merchant: {
392
+ name: options.merchantName
393
+ }
394
+ };
395
+ if (options.tier) metadata.plan.tier = options.tier;
396
+ if (options.features) metadata.plan.features = options.features;
397
+ if (options.logo) metadata.merchant.logo = options.logo;
398
+ if (options.website) metadata.merchant.website = options.website;
399
+ if (options.supportEmail) metadata.merchant.supportEmail = options.supportEmail;
400
+ if (options.color || options.badge) {
401
+ metadata.display = {};
402
+ if (options.color) metadata.display.color = options.color;
403
+ if (options.badge) metadata.display.badge = options.badge;
404
+ }
405
+ return metadata;
406
+ }
407
+ // Annotate the CommonJS export names for ESM import in node:
408
+ 0 && (module.exports = {
409
+ AutoPayCheckoutError,
410
+ AutoPayError,
411
+ AutoPayMetadataError,
412
+ AutoPayWebhookError,
413
+ DEFAULT_CHECKOUT_BASE_URL,
414
+ MAX_INTERVAL,
415
+ MAX_RETRIES,
416
+ MIN_INTERVAL,
417
+ PROTOCOL_FEE_BPS,
418
+ USDC_DECIMALS,
419
+ calculateFeeBreakdown,
420
+ chains,
421
+ createCheckoutUrl,
422
+ createMetadata,
423
+ formatInterval,
424
+ formatUSDC,
425
+ intervals,
426
+ parseSuccessRedirect,
427
+ parseUSDC,
428
+ resolveInterval,
429
+ signPayload,
430
+ validateMetadata,
431
+ verifySignature,
432
+ verifyWebhook
433
+ });
434
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/constants.ts","../src/checkout.ts","../src/webhooks.ts","../src/amounts.ts","../src/metadata.ts"],"sourcesContent":["// Types\nexport type {\n CheckoutOptions,\n SuccessRedirect,\n IntervalPreset,\n WebhookEvent,\n WebhookEventType,\n ChargeSucceededEvent,\n ChargeFailedEvent,\n PolicyCreatedEvent,\n PolicyRevokedEvent,\n PolicyCancelledByFailureEvent,\n CheckoutMetadata,\n FeeBreakdown,\n} from './types'\n\n// Errors\nexport {\n AutoPayError,\n AutoPayWebhookError,\n AutoPayCheckoutError,\n AutoPayMetadataError,\n} from './errors'\n\n// Constants\nexport {\n intervals,\n PROTOCOL_FEE_BPS,\n USDC_DECIMALS,\n MIN_INTERVAL,\n MAX_INTERVAL,\n MAX_RETRIES,\n chains,\n DEFAULT_CHECKOUT_BASE_URL,\n} from './constants'\nexport type { ChainConfig } from './constants'\n\n// Checkout\nexport { createCheckoutUrl, parseSuccessRedirect, resolveInterval } from './checkout'\n\n// Webhooks\nexport { verifyWebhook, verifySignature, signPayload } from './webhooks'\n\n// Amounts\nexport { formatUSDC, parseUSDC, calculateFeeBreakdown, formatInterval } from './amounts'\n\n// Metadata\nexport { validateMetadata, createMetadata } from './metadata'\nexport type { MetadataValidationResult } from './metadata'\n","export class AutoPayError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message)\n this.name = 'AutoPayError'\n }\n}\n\nexport class AutoPayWebhookError extends AutoPayError {\n constructor(message: string) {\n super(message, 'WEBHOOK_VERIFICATION_FAILED')\n this.name = 'AutoPayWebhookError'\n }\n}\n\nexport class AutoPayCheckoutError extends AutoPayError {\n constructor(message: string) {\n super(message, 'INVALID_CHECKOUT_PARAMS')\n this.name = 'AutoPayCheckoutError'\n }\n}\n\nexport class AutoPayMetadataError extends AutoPayError {\n constructor(message: string) {\n super(message, 'INVALID_METADATA')\n this.name = 'AutoPayMetadataError'\n }\n}\n","// ---------------------------------------------------------------------------\n// Intervals (seconds)\n// ---------------------------------------------------------------------------\n\nexport const intervals = {\n /** 1 minute — useful for testing */\n minute: 60,\n /** 7 days */\n weekly: 604_800,\n /** 14 days */\n biweekly: 1_209_600,\n /** 30 days */\n monthly: 2_592_000,\n /** 90 days */\n quarterly: 7_776_000,\n /** 365 days */\n yearly: 31_536_000,\n\n /** Build a custom interval from a count and unit */\n custom(count: number, unit: 'minutes' | 'hours' | 'days' | 'months' | 'years'): number {\n const multipliers: Record<string, number> = {\n minutes: 60,\n hours: 3_600,\n days: 86_400,\n months: 2_592_000, // 30 days\n years: 31_536_000, // 365 days\n }\n return count * multipliers[unit]\n },\n} as const\n\n// ---------------------------------------------------------------------------\n// Protocol\n// ---------------------------------------------------------------------------\n\n/** Protocol fee in basis points (2.5%) */\nexport const PROTOCOL_FEE_BPS = 250\n\n/** USDC uses 6 decimals */\nexport const USDC_DECIMALS = 6\n\n/** Minimum interval (1 minute) */\nexport const MIN_INTERVAL = 60\n\n/** Maximum interval (365 days) */\nexport const MAX_INTERVAL = 31_536_000\n\n/** Max consecutive failures before auto-cancel */\nexport const MAX_RETRIES = 3\n\n// ---------------------------------------------------------------------------\n// Chain configs\n// ---------------------------------------------------------------------------\n\nexport interface ChainConfig {\n name: string\n chainId: number\n cctpDomain: number\n usdc: string\n explorer: string\n}\n\nexport const chains: Record<string, ChainConfig> = {\n polygonAmoy: {\n name: 'Polygon Amoy',\n chainId: 80002,\n cctpDomain: 7,\n usdc: '0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582',\n explorer: 'https://amoy.polygonscan.com',\n },\n arbitrumSepolia: {\n name: 'Arbitrum Sepolia',\n chainId: 421614,\n cctpDomain: 3,\n usdc: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d',\n explorer: 'https://sepolia.arbiscan.io',\n },\n arcTestnet: {\n name: 'Arc Testnet',\n chainId: 1868,\n cctpDomain: 26,\n usdc: '0x3600000000000000000000000000000000000000',\n explorer: 'https://explorer-testnet.arc.gel.network',\n },\n}\n\n/** Default checkout base URL */\nexport const DEFAULT_CHECKOUT_BASE_URL = 'https://app.autopay.xyz'\n","import { AutoPayCheckoutError } from './errors'\nimport { DEFAULT_CHECKOUT_BASE_URL, intervals as presetIntervals, MIN_INTERVAL, MAX_INTERVAL } from './constants'\nimport type { CheckoutOptions, IntervalPreset, SuccessRedirect } from './types'\n\nconst INTERVAL_MAP: Record<IntervalPreset, number> = {\n weekly: presetIntervals.weekly,\n biweekly: presetIntervals.biweekly,\n monthly: presetIntervals.monthly,\n quarterly: presetIntervals.quarterly,\n yearly: presetIntervals.yearly,\n}\n\n/** Resolve an interval preset or number to seconds */\nexport function resolveInterval(interval: IntervalPreset | number): number {\n if (typeof interval === 'number') return interval\n const seconds = INTERVAL_MAP[interval]\n if (!seconds) {\n throw new AutoPayCheckoutError(\n `Invalid interval preset \"${interval}\". Use: ${Object.keys(INTERVAL_MAP).join(', ')} or a number of seconds.`,\n )\n }\n return seconds\n}\n\nfunction isValidAddress(value: string): boolean {\n return /^0x[a-fA-F0-9]{40}$/.test(value)\n}\n\nfunction isValidUrl(value: string): boolean {\n try {\n new URL(value)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Build a checkout URL that a merchant can redirect users to.\n *\n * @example\n * ```ts\n * const url = createCheckoutUrl({\n * merchant: '0x2B8b...',\n * amount: 9.99,\n * interval: 'monthly',\n * metadataUrl: 'https://mysite.com/plans/pro.json',\n * successUrl: 'https://mysite.com/success',\n * cancelUrl: 'https://mysite.com/cancel',\n * })\n * ```\n */\nexport function createCheckoutUrl(options: CheckoutOptions): string {\n const { merchant, amount, interval, metadataUrl, successUrl, cancelUrl, spendingCap, baseUrl } = options\n\n // Validate merchant address\n if (!merchant || !isValidAddress(merchant)) {\n throw new AutoPayCheckoutError(`Invalid merchant address: ${merchant}`)\n }\n\n // Validate amount\n if (typeof amount !== 'number' || amount <= 0 || !Number.isFinite(amount)) {\n throw new AutoPayCheckoutError(`Invalid amount: ${amount}. Must be a positive number.`)\n }\n\n // Resolve and validate interval\n const intervalSeconds = resolveInterval(interval)\n if (intervalSeconds < MIN_INTERVAL || intervalSeconds > MAX_INTERVAL) {\n throw new AutoPayCheckoutError(\n `Interval ${intervalSeconds}s out of range. Must be between ${MIN_INTERVAL}s and ${MAX_INTERVAL}s.`,\n )\n }\n\n // Validate URLs\n if (!isValidUrl(metadataUrl)) {\n throw new AutoPayCheckoutError(`Invalid metadata URL: ${metadataUrl}`)\n }\n if (!isValidUrl(successUrl)) {\n throw new AutoPayCheckoutError(`Invalid success URL: ${successUrl}`)\n }\n if (!isValidUrl(cancelUrl)) {\n throw new AutoPayCheckoutError(`Invalid cancel URL: ${cancelUrl}`)\n }\n\n // Validate spending cap\n if (spendingCap !== undefined) {\n if (typeof spendingCap !== 'number' || spendingCap <= 0 || !Number.isFinite(spendingCap)) {\n throw new AutoPayCheckoutError(`Invalid spending cap: ${spendingCap}. Must be a positive number.`)\n }\n if (spendingCap < amount) {\n throw new AutoPayCheckoutError(`Spending cap (${spendingCap}) must be >= amount (${amount}).`)\n }\n }\n\n const base = baseUrl || DEFAULT_CHECKOUT_BASE_URL\n const url = new URL('/checkout', base)\n\n url.searchParams.set('merchant', merchant)\n url.searchParams.set('amount', String(amount))\n url.searchParams.set('interval', String(intervalSeconds))\n url.searchParams.set('metadata_url', metadataUrl)\n url.searchParams.set('success_url', successUrl)\n url.searchParams.set('cancel_url', cancelUrl)\n\n if (spendingCap !== undefined) {\n url.searchParams.set('spending_cap', String(spendingCap))\n }\n\n return url.toString()\n}\n\n/**\n * Parse the query params from the success redirect URL.\n *\n * After a user subscribes, they are redirected to the merchant's `successUrl`\n * with `?policyId=0x...&txHash=0x...` appended.\n *\n * @example\n * ```ts\n * // On your success page:\n * const { policyId, txHash } = parseSuccessRedirect(window.location.search)\n * ```\n */\nexport function parseSuccessRedirect(queryString: string): SuccessRedirect {\n const params = new URLSearchParams(queryString)\n const policyId = params.get('policyId')\n const txHash = params.get('txHash')\n\n if (!policyId) {\n throw new AutoPayCheckoutError('Missing policyId in success redirect URL')\n }\n if (!txHash) {\n throw new AutoPayCheckoutError('Missing txHash in success redirect URL')\n }\n\n return { policyId, txHash }\n}\n","import { createHmac } from 'crypto'\nimport { AutoPayWebhookError } from './errors'\nimport type { WebhookEvent, WebhookEventType } from './types'\n\nconst VALID_EVENT_TYPES: WebhookEventType[] = [\n 'charge.succeeded',\n 'charge.failed',\n 'policy.created',\n 'policy.revoked',\n 'policy.cancelled_by_failure',\n]\n\n/**\n * Sign a payload string with HMAC-SHA256.\n * Used by the relayer — exposed here so merchants can test locally.\n */\nexport function signPayload(payload: string, secret: string): string {\n return createHmac('sha256', secret).update(payload).digest('hex')\n}\n\n/**\n * Verify an HMAC-SHA256 signature using constant-time comparison.\n */\nexport function verifySignature(payload: string, signature: string, secret: string): boolean {\n const expected = signPayload(payload, secret)\n return timingSafeEqual(expected, signature)\n}\n\n/**\n * Verify and parse a webhook from AutoPay.\n *\n * @param rawBody - The raw request body string (JSON.stringify of the body)\n * @param signature - The `x-autopay-signature` header value\n * @param secret - Your webhook secret\n * @returns A fully-typed {@link WebhookEvent} with discriminated union on `type`\n *\n * @example\n * ```ts\n * const event = verifyWebhook(rawBody, req.headers['x-autopay-signature'], secret)\n * if (event.type === 'charge.succeeded') {\n * console.log(event.data.amount) // TypeScript knows this exists\n * }\n * ```\n */\nexport function verifyWebhook(rawBody: string, signature: string | undefined, secret: string): WebhookEvent {\n if (!signature) {\n throw new AutoPayWebhookError('Missing x-autopay-signature header')\n }\n\n if (!secret) {\n throw new AutoPayWebhookError('Webhook secret is not configured')\n }\n\n if (!verifySignature(rawBody, signature, secret)) {\n throw new AutoPayWebhookError('Invalid webhook signature')\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(rawBody)\n } catch {\n throw new AutoPayWebhookError('Invalid JSON in webhook body')\n }\n\n const event = parsed as Record<string, unknown>\n\n if (!event || typeof event !== 'object') {\n throw new AutoPayWebhookError('Webhook body is not an object')\n }\n\n // The relayer sends { event: \"charge.succeeded\", timestamp: \"...\", data: { ... } }\n // Normalize to our SDK type shape: { type: \"charge.succeeded\", ... }\n const eventType = (event.event ?? event.type) as string\n if (!eventType || !VALID_EVENT_TYPES.includes(eventType as WebhookEventType)) {\n throw new AutoPayWebhookError(`Unknown webhook event type: ${eventType}`)\n }\n\n return {\n type: eventType,\n timestamp: event.timestamp,\n data: event.data,\n } as WebhookEvent\n}\n\n// Constant-time string comparison to prevent timing attacks\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n let result = 0\n for (let i = 0; i < a.length; i++) {\n result |= a.charCodeAt(i) ^ b.charCodeAt(i)\n }\n return result === 0\n}\n","import { USDC_DECIMALS, PROTOCOL_FEE_BPS } from './constants'\nimport type { FeeBreakdown } from './types'\n\nconst USDC_FACTOR = 10 ** USDC_DECIMALS // 1_000_000\n\n/**\n * Format a raw USDC amount (6-decimal string) to a human-readable string.\n *\n * @example\n * ```ts\n * formatUSDC('9990000') // \"9.99\"\n * formatUSDC('100000') // \"0.10\"\n * ```\n */\nexport function formatUSDC(rawAmount: string): string {\n const num = Number(rawAmount) / USDC_FACTOR\n // Use fixed-point to avoid floating-point display issues\n // Trim trailing zeros but keep at least 2 decimal places\n const fixed = num.toFixed(6)\n // Remove trailing zeros, but keep at least \"X.XX\"\n const parts = fixed.split('.')\n let decimals = parts[1].replace(/0+$/, '')\n if (decimals.length < 2) decimals = decimals.padEnd(2, '0')\n return `${parts[0]}.${decimals}`\n}\n\n/**\n * Parse a human-readable USDC amount to a raw 6-decimal string.\n *\n * @example\n * ```ts\n * parseUSDC(9.99) // \"9990000\"\n * parseUSDC(0.10) // \"100000\"\n * ```\n */\nexport function parseUSDC(amount: number): string {\n // Use string math to avoid floating-point errors\n // Multiply by 1_000_000 using integer arithmetic on the string parts\n const str = amount.toFixed(USDC_DECIMALS)\n const [whole, frac] = str.split('.')\n const padded = (frac || '').padEnd(USDC_DECIMALS, '0').slice(0, USDC_DECIMALS)\n const raw = BigInt(whole) * BigInt(USDC_FACTOR) + BigInt(padded)\n return raw.toString()\n}\n\n/**\n * Calculate the fee breakdown for a charge amount.\n *\n * @param rawAmount - Raw USDC amount (6-decimal string) OR human-readable number\n *\n * @example\n * ```ts\n * calculateFeeBreakdown('9990000')\n * // { total: \"9.99\", merchantReceives: \"9.74\", protocolFee: \"0.25\", feePercentage: \"2.5%\" }\n * ```\n */\nexport function calculateFeeBreakdown(rawAmount: string): FeeBreakdown {\n const totalRaw = BigInt(rawAmount)\n const feeRaw = (totalRaw * BigInt(PROTOCOL_FEE_BPS)) / 10_000n\n const merchantRaw = totalRaw - feeRaw\n\n return {\n total: formatUSDC(totalRaw.toString()),\n merchantReceives: formatUSDC(merchantRaw.toString()),\n protocolFee: formatUSDC(feeRaw.toString()),\n feePercentage: `${PROTOCOL_FEE_BPS / 100}%`,\n }\n}\n\n/**\n * Format an interval in seconds to a human-readable label.\n *\n * @example\n * ```ts\n * formatInterval(2592000) // \"monthly\"\n * formatInterval(604800) // \"weekly\"\n * formatInterval(86400) // \"1 day\"\n * formatInterval(172800) // \"2 days\"\n * ```\n */\nexport function formatInterval(seconds: number): string {\n // Check for well-known presets first\n const presets: Record<number, string> = {\n 604_800: 'weekly',\n 1_209_600: 'biweekly',\n 2_592_000: 'monthly',\n 7_776_000: 'quarterly',\n 31_536_000: 'yearly',\n }\n if (presets[seconds]) return presets[seconds]\n\n const days = Math.floor(seconds / 86_400)\n const hours = Math.floor((seconds % 86_400) / 3_600)\n const minutes = Math.floor((seconds % 3_600) / 60)\n\n if (days > 0 && hours > 0) return `${days}d ${hours}h`\n if (days > 0) return days === 1 ? '1 day' : `${days} days`\n if (hours > 0) return hours === 1 ? '1 hour' : `${hours} hours`\n if (minutes > 0) return minutes === 1 ? '1 minute' : `${minutes} minutes`\n return `${seconds}s`\n}\n","import type { CheckoutMetadata } from './types'\n\nexport interface MetadataValidationResult {\n valid: boolean\n errors: string[]\n}\n\n/**\n * Validate a metadata JSON object against the AutoPay metadata schema.\n *\n * @example\n * ```ts\n * const { valid, errors } = validateMetadata(jsonData)\n * if (!valid) console.error(errors)\n * ```\n */\nexport function validateMetadata(data: unknown): MetadataValidationResult {\n const errors: string[] = []\n\n if (!data || typeof data !== 'object') {\n return { valid: false, errors: ['Metadata must be an object'] }\n }\n\n const obj = data as Record<string, unknown>\n\n // version\n if (typeof obj.version !== 'string' || !obj.version) {\n errors.push('Missing or invalid \"version\" (must be a non-empty string)')\n }\n\n // plan\n if (!obj.plan || typeof obj.plan !== 'object') {\n errors.push('Missing or invalid \"plan\" (must be an object)')\n } else {\n const plan = obj.plan as Record<string, unknown>\n if (typeof plan.name !== 'string' || !plan.name) {\n errors.push('Missing or invalid \"plan.name\" (must be a non-empty string)')\n }\n if (typeof plan.description !== 'string' || !plan.description) {\n errors.push('Missing or invalid \"plan.description\" (must be a non-empty string)')\n }\n if (plan.tier !== undefined && typeof plan.tier !== 'string') {\n errors.push('\"plan.tier\" must be a string if provided')\n }\n if (plan.features !== undefined) {\n if (!Array.isArray(plan.features) || !plan.features.every((f) => typeof f === 'string')) {\n errors.push('\"plan.features\" must be an array of strings if provided')\n }\n }\n }\n\n // merchant\n if (!obj.merchant || typeof obj.merchant !== 'object') {\n errors.push('Missing or invalid \"merchant\" (must be an object)')\n } else {\n const merchant = obj.merchant as Record<string, unknown>\n if (typeof merchant.name !== 'string' || !merchant.name) {\n errors.push('Missing or invalid \"merchant.name\" (must be a non-empty string)')\n }\n if (merchant.logo !== undefined && typeof merchant.logo !== 'string') {\n errors.push('\"merchant.logo\" must be a string if provided')\n }\n if (merchant.website !== undefined && typeof merchant.website !== 'string') {\n errors.push('\"merchant.website\" must be a string if provided')\n }\n if (merchant.supportEmail !== undefined && typeof merchant.supportEmail !== 'string') {\n errors.push('\"merchant.supportEmail\" must be a string if provided')\n }\n }\n\n // display (optional)\n if (obj.display !== undefined) {\n if (typeof obj.display !== 'object' || obj.display === null) {\n errors.push('\"display\" must be an object if provided')\n } else {\n const display = obj.display as Record<string, unknown>\n if (display.color !== undefined && typeof display.color !== 'string') {\n errors.push('\"display.color\" must be a string if provided')\n }\n if (display.badge !== undefined && typeof display.badge !== 'string') {\n errors.push('\"display.badge\" must be a string if provided')\n }\n }\n }\n\n return { valid: errors.length === 0, errors }\n}\n\n/**\n * Create a valid metadata object with sensible defaults.\n *\n * @example\n * ```ts\n * const metadata = createMetadata({\n * planName: 'Pro',\n * planDescription: 'All premium features',\n * merchantName: 'Acme Corp',\n * })\n * ```\n */\nexport function createMetadata(options: {\n planName: string\n planDescription: string\n merchantName: string\n tier?: string\n features?: string[]\n logo?: string\n website?: string\n supportEmail?: string\n color?: string\n badge?: string\n}): CheckoutMetadata {\n const metadata: CheckoutMetadata = {\n version: '1.0',\n plan: {\n name: options.planName,\n description: options.planDescription,\n },\n merchant: {\n name: options.merchantName,\n },\n }\n\n if (options.tier) metadata.plan.tier = options.tier\n if (options.features) metadata.plan.features = options.features\n if (options.logo) metadata.merchant.logo = options.logo\n if (options.website) metadata.merchant.website = options.website\n if (options.supportEmail) metadata.merchant.supportEmail = options.supportEmail\n\n if (options.color || options.badge) {\n metadata.display = {}\n if (options.color) metadata.display.color = options.color\n if (options.badge) metadata.display.badge = options.badge\n }\n\n return metadata\n}\n"],"mappings":"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","names":[]}