@autopayprotocol/sdk 0.1.1 → 0.1.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @autopayprotocol/sdk
2
2
 
3
- Server-side utility package for merchants integrating with [AutoPay Protocol](https://autopay.xyz).
3
+ Server-side utility package for merchants integrating with [AutoPay Protocol](https://autopayprotocol.com).
4
4
 
5
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
6
 
@@ -80,12 +80,30 @@ if (event.type === 'charge.succeeded') {
80
80
 
81
81
  | Export | Value |
82
82
  |--------|-------|
83
- | `intervals.monthly` | `2_592_000` |
83
+ | `intervals.minute` | `60` |
84
84
  | `intervals.weekly` | `604_800` |
85
- | `intervals.custom(14, 'days')` | `1_209_600` |
85
+ | `intervals.biweekly` | `1_209_600` |
86
+ | `intervals.monthly` | `2_592_000` |
87
+ | `intervals.quarterly` | `7_776_000` |
88
+ | `intervals.yearly` | `31_536_000` |
89
+ | `intervals.custom(count, unit)` | Custom interval — units: `'minutes'`, `'hours'`, `'days'`, `'months'`, `'years'` |
86
90
  | `PROTOCOL_FEE_BPS` | `250` (2.5%) |
91
+ | `USDC_DECIMALS` | `6` |
92
+ | `MIN_INTERVAL` | `60` (1 minute) |
93
+ | `MAX_INTERVAL` | `31_536_000` (365 days) |
94
+ | `MAX_RETRIES` | `3` |
95
+ | `DEFAULT_CHECKOUT_BASE_URL` | `'https://autopayprotocol.com'` |
87
96
  | `chains` | Chain configs (Polygon Amoy, Arbitrum Sepolia, Arc Testnet) |
88
97
 
98
+ ### Error Classes
99
+
100
+ | Class | Code |
101
+ |-------|------|
102
+ | `AutoPayError` | Base error with `code` property |
103
+ | `AutoPayWebhookError` | `'WEBHOOK_VERIFICATION_FAILED'` |
104
+ | `AutoPayCheckoutError` | `'INVALID_CHECKOUT_PARAMS'` |
105
+ | `AutoPayMetadataError` | `'INVALID_METADATA'` |
106
+
89
107
  ### Webhook Event Types
90
108
 
91
109
  All events share `{ type, timestamp, data: { policyId, chainId, payer, merchant } }` plus event-specific fields:
@@ -98,10 +116,14 @@ All events share `{ type, timestamp, data: { policyId, chainId, payer, merchant
98
116
  | `policy.revoked` | `endTime` |
99
117
  | `policy.cancelled_by_failure` | `consecutiveFailures`, `endTime` |
100
118
 
119
+ ### Exported Types
120
+
121
+ `CheckoutOptions`, `SuccessRedirect`, `IntervalPreset`, `WebhookEvent`, `WebhookEventType`, `ChargeSucceededEvent`, `ChargeFailedEvent`, `PolicyCreatedEvent`, `PolicyRevokedEvent`, `PolicyCancelledByFailureEvent`, `CheckoutMetadata`, `FeeBreakdown`, `MetadataValidationResult`, `ChainConfig`
122
+
101
123
  ## Zero Dependencies
102
124
 
103
125
  This package has **zero runtime dependencies**. It only uses Node.js built-in `crypto`.
104
126
 
105
127
  ## License
106
128
 
107
- MIT
129
+ Apache-2.0
package/dist/index.cjs CHANGED
@@ -130,7 +130,7 @@ var chains = {
130
130
  explorer: "https://explorer-testnet.arc.gel.network"
131
131
  }
132
132
  };
133
- var DEFAULT_CHECKOUT_BASE_URL = "https://app.autopay.xyz";
133
+ var DEFAULT_CHECKOUT_BASE_URL = "https://autopayprotocol.com";
134
134
 
135
135
  // src/checkout.ts
136
136
  var INTERVAL_MAP = {
@@ -1 +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":[]}
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://autopayprotocol.com'\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":[]}
package/dist/index.d.cts CHANGED
@@ -14,7 +14,7 @@ interface CheckoutOptions {
14
14
  cancelUrl: string;
15
15
  /** Optional spending cap in human-readable USDC. Omit for unlimited. */
16
16
  spendingCap?: number;
17
- /** Optional base URL override (default: https://app.autopay.xyz) */
17
+ /** Optional base URL override (default: https://autopayprotocol.com) */
18
18
  baseUrl?: string;
19
19
  }
20
20
  interface SuccessRedirect {
@@ -147,7 +147,7 @@ interface ChainConfig {
147
147
  }
148
148
  declare const chains: Record<string, ChainConfig>;
149
149
  /** Default checkout base URL */
150
- declare const DEFAULT_CHECKOUT_BASE_URL = "https://app.autopay.xyz";
150
+ declare const DEFAULT_CHECKOUT_BASE_URL = "https://autopayprotocol.com";
151
151
 
152
152
  /** Resolve an interval preset or number to seconds */
153
153
  declare function resolveInterval(interval: IntervalPreset | number): number;
package/dist/index.d.ts CHANGED
@@ -14,7 +14,7 @@ interface CheckoutOptions {
14
14
  cancelUrl: string;
15
15
  /** Optional spending cap in human-readable USDC. Omit for unlimited. */
16
16
  spendingCap?: number;
17
- /** Optional base URL override (default: https://app.autopay.xyz) */
17
+ /** Optional base URL override (default: https://autopayprotocol.com) */
18
18
  baseUrl?: string;
19
19
  }
20
20
  interface SuccessRedirect {
@@ -147,7 +147,7 @@ interface ChainConfig {
147
147
  }
148
148
  declare const chains: Record<string, ChainConfig>;
149
149
  /** Default checkout base URL */
150
- declare const DEFAULT_CHECKOUT_BASE_URL = "https://app.autopay.xyz";
150
+ declare const DEFAULT_CHECKOUT_BASE_URL = "https://autopayprotocol.com";
151
151
 
152
152
  /** Resolve an interval preset or number to seconds */
153
153
  declare function resolveInterval(interval: IntervalPreset | number): number;
package/dist/index.js CHANGED
@@ -81,7 +81,7 @@ var chains = {
81
81
  explorer: "https://explorer-testnet.arc.gel.network"
82
82
  }
83
83
  };
84
- var DEFAULT_CHECKOUT_BASE_URL = "https://app.autopay.xyz";
84
+ var DEFAULT_CHECKOUT_BASE_URL = "https://autopayprotocol.com";
85
85
 
86
86
  // src/checkout.ts
87
87
  var INTERVAL_MAP = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/constants.ts","../src/checkout.ts","../src/webhooks.ts","../src/amounts.ts","../src/metadata.ts"],"sourcesContent":["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":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/constants.ts","../src/checkout.ts","../src/webhooks.ts","../src/amounts.ts","../src/metadata.ts"],"sourcesContent":["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://autopayprotocol.com'\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":[]}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@autopayprotocol/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Merchant SDK for AutoPay Protocol — checkout URLs, webhook verification, USDC helpers",
5
- "license": "MIT",
5
+ "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/apeoverflow/Auto-Pay-Protocol.git",
@@ -30,7 +30,7 @@
30
30
  "dist"
31
31
  ],
32
32
  "engines": {
33
- "node": ">=18"
33
+ "node": ">=20"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "tsup",