@happyvertical/payments 0.79.0 → 0.80.1
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/AGENT.md +1 -1
- package/dist/adapters/base-usdc.js +720 -1101
- package/dist/adapters/base-usdc.js.map +1 -1
- package/dist/adapters/btc.js +465 -790
- package/dist/adapters/btc.js.map +1 -1
- package/dist/adapters/stripe.js +556 -959
- package/dist/adapters/stripe.js.map +1 -1
- package/dist/chunks/errors-feZrwnTy.js +39 -0
- package/dist/chunks/errors-feZrwnTy.js.map +1 -0
- package/dist/chunks/shared-DVl6UzD9.js +260 -0
- package/dist/chunks/shared-DVl6UzD9.js.map +1 -0
- package/dist/index.js +33 -47
- package/dist/index.js.map +1 -1
- package/dist/testing/conformance.js +93 -109
- package/dist/testing/conformance.js.map +1 -1
- package/metadata.json +1 -1
- package/package.json +5 -5
- package/dist/chunks/errors-BgFC46qQ.js +0 -45
- package/dist/chunks/errors-BgFC46qQ.js.map +0 -1
- package/dist/chunks/shared-DGHSqDQT.js +0 -392
- package/dist/chunks/shared-DGHSqDQT.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-DVl6UzD9.js","names":[],"sources":["../../src/shared.ts"],"sourcesContent":["import {\n PaymentConfigurationError,\n PaymentProviderError,\n PaymentUnsupportedError,\n} from './errors.js';\nimport type {\n PaymentEvent,\n PaymentStatus,\n PaymentStatusResult,\n WatchPaymentInput,\n} from './types.js';\n\nexport type FetchLike = (\n input: string | URL | Request,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport const TERMINAL_PAYMENT_STATUSES = new Set<PaymentStatus>([\n 'confirmed',\n 'failed',\n 'expired',\n 'refunded',\n]);\nexport const DEFAULT_MAX_STORED_PAYMENT_OPTIONS = 1_000;\nconst MAX_DECIMAL_PRECISION = 100;\nconst ZERO_DECIMAL_CURRENCIES = new Set([\n 'BIF',\n 'CLP',\n 'DJF',\n 'GNF',\n 'JPY',\n 'KMF',\n 'KRW',\n 'MGA',\n 'PYG',\n 'RWF',\n 'VND',\n 'VUV',\n 'XAF',\n 'XOF',\n 'XPF',\n]);\nconst THREE_DECIMAL_CURRENCIES = new Set([\n 'BHD',\n 'IQD',\n 'JOD',\n 'KWD',\n 'LYD',\n 'OMR',\n 'TND',\n]);\nconst TWO_DECIMAL_WHOLE_UNIT_CURRENCIES = new Set(['ISK', 'UGX']);\n\nexport function getFetch(fetchLike?: FetchLike): FetchLike {\n const resolved = fetchLike ?? globalThis.fetch;\n\n if (!resolved) {\n throw new PaymentProviderError(\n 'A fetch implementation is required in this runtime.',\n );\n }\n\n if (typeof resolved !== 'function') {\n throw new PaymentProviderError('Fetch implementation must be a function.');\n }\n\n return resolved.bind(globalThis) as FetchLike;\n}\n\nexport function normalizeDate(value: Date | string): Date {\n if (!(value instanceof Date) && typeof value !== 'string') {\n throw new PaymentProviderError(`Invalid date value: ${String(value)}`);\n }\n\n const date = value instanceof Date ? value : new Date(value);\n\n if (Number.isNaN(date.getTime())) {\n throw new PaymentProviderError(`Invalid date value: ${String(value)}`);\n }\n\n return date;\n}\n\nexport function normalizeFutureDate(\n value: Date | string,\n context: string,\n): Date {\n const date = normalizeDate(value);\n\n if (date.getTime() <= Date.now()) {\n throw new PaymentProviderError(`${context} is in the past.`);\n }\n\n return date;\n}\n\nexport function normalizeAmount(value: string): string {\n if (typeof value !== 'string') {\n throw new PaymentProviderError('Monetary amounts must be decimal strings.');\n }\n\n const raw = value.trim();\n\n if (!/^\\d+(\\.\\d+)?$/.test(raw)) {\n throw new PaymentProviderError(`Invalid amount value: ${String(value)}`);\n }\n\n const [whole, fraction] = raw.split('.');\n const normalizedWhole = whole.replace(/^0+(?=\\d)/, '') || '0';\n const normalizedFraction = fraction?.replace(/0+$/, '');\n\n return normalizedFraction\n ? `${normalizedWhole}.${normalizedFraction}`\n : normalizedWhole;\n}\n\nexport function normalizePositiveAmount(\n value: string,\n context: string,\n): string {\n const amount = normalizeAmount(value);\n\n if (amount === '0') {\n throw new PaymentProviderError(`${context} must be greater than zero.`);\n }\n\n return amount;\n}\n\nexport function normalizeCurrency(value: string, context: string): string {\n if (typeof value !== 'string') {\n throw new PaymentProviderError(`${context} currency must be a string.`);\n }\n\n const normalized = value.trim().toUpperCase();\n\n if (!/^[A-Z0-9]{3,10}$/.test(normalized)) {\n throw new PaymentProviderError(\n `${context} currency must be a currency code, received ${String(value)}.`,\n );\n }\n\n return normalized;\n}\n\nexport function normalizeMinorUnitAmount(\n value: number,\n context: string,\n): number {\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {\n throw new PaymentProviderError(\n `${context} must be a non-negative integer in the smallest currency unit, received ${String(value)}.`,\n );\n }\n\n return value;\n}\n\nexport function normalizePositiveMinorUnitAmount(\n value: number,\n context: string,\n): number {\n const amount = normalizeMinorUnitAmount(value, context);\n\n if (amount === 0) {\n throw new PaymentProviderError(`${context} must be greater than zero.`);\n }\n\n return amount;\n}\n\nexport function normalizePositivePaymentAmount(\n value: number,\n currency: string,\n context: string,\n): number {\n const amount = normalizePositiveMinorUnitAmount(value, context);\n const normalizedCurrency = normalizeCurrency(currency, context);\n\n if (\n TWO_DECIMAL_WHOLE_UNIT_CURRENCIES.has(normalizedCurrency) &&\n amount % 100 !== 0\n ) {\n throw new PaymentProviderError(\n `${context} for ${normalizedCurrency} must be evenly divisible by 100.`,\n );\n }\n\n return amount;\n}\n\nexport function normalizeMaxStoredPaymentOptions(\n value: number | undefined,\n context: string,\n): number {\n if (value === undefined) {\n return DEFAULT_MAX_STORED_PAYMENT_OPTIONS;\n }\n\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new PaymentConfigurationError(\n `${context} must be a positive safe integer.`,\n );\n }\n\n return value;\n}\n\nexport function rememberPaymentOption<TOption>(\n optionsByQuote: Map<string, TOption>,\n quoteId: string,\n option: TOption,\n maxStoredPaymentOptions: number,\n): void {\n optionsByQuote.delete(quoteId);\n optionsByQuote.set(quoteId, option);\n\n while (optionsByQuote.size > maxStoredPaymentOptions) {\n const oldestQuoteId = optionsByQuote.keys().next().value;\n\n if (oldestQuoteId === undefined) {\n return;\n }\n\n optionsByQuote.delete(oldestQuoteId);\n }\n}\n\nexport function normalizeNonEmptyString(\n value: string,\n context: string,\n): string {\n if (typeof value !== 'string') {\n throw new PaymentProviderError(`${context} must be a string.`);\n }\n\n const normalized = value.trim();\n\n if (!normalized) {\n throw new PaymentProviderError(`${context} must not be empty.`);\n }\n\n return normalized;\n}\n\nexport function normalizeUrlString(value: string, context: string): string {\n const normalized = normalizeNonEmptyString(value, context);\n\n try {\n const url = new URL(normalized);\n\n if (!['http:', 'https:'].includes(url.protocol)) {\n throw new PaymentConfigurationError(`${context} must use http or https.`);\n }\n } catch (error) {\n if (error instanceof PaymentConfigurationError) {\n throw error;\n }\n\n throw new PaymentConfigurationError(`${context} must be a valid URL.`, {\n cause: error,\n });\n }\n\n return normalized;\n}\n\nexport function decimalToAtomicUnits(value: string, decimals: number): string {\n const precision = normalizeDecimalPrecision(decimals);\n\n const normalized = normalizeAmount(value);\n const [whole, fraction = ''] = normalized.split('.');\n\n if (fraction.length > precision) {\n throw new PaymentProviderError(\n `Amount ${String(value)} has more than ${precision} decimal places.`,\n );\n }\n\n const padded = `${fraction}${'0'.repeat(precision)}`.slice(0, precision);\n const combined = `${whole}${padded}`.replace(/^0+(?=\\d)/, '');\n\n return combined || '0';\n}\n\nexport function atomicUnitsToDecimal(\n value: string | bigint,\n decimals: number,\n): string {\n const precision = normalizeDecimalPrecision(decimals);\n\n if (typeof value !== 'string' && typeof value !== 'bigint') {\n throw new PaymentProviderError(\n `Invalid atomic amount value: ${String(value)}`,\n );\n }\n\n const raw = typeof value === 'bigint' ? value.toString() : value;\n\n if (!/^\\d+$/.test(raw)) {\n throw new PaymentProviderError(\n `Invalid atomic amount value: ${String(value)}`,\n );\n }\n\n if (precision === 0) {\n return raw || '0';\n }\n\n const padded = raw.padStart(precision + 1, '0');\n const whole = padded.slice(0, -precision);\n const fraction = padded.slice(-precision).replace(/0+$/, '');\n\n return fraction ? `${whole}.${fraction}` : whole;\n}\n\nexport function amountToMinorUnits(value: string, decimals = 2): string {\n return decimalToAtomicUnits(value, decimals);\n}\n\nexport function minorUnitsToDecimal(value: number, decimals: number): string {\n return atomicUnitsToDecimal(\n BigInt(normalizeMinorUnitAmount(value, 'Minor-unit')),\n decimals,\n );\n}\n\nexport function decimalToMinorUnitAmount(\n value: string | bigint,\n decimals: number,\n context: string,\n): number {\n const atomic =\n typeof value === 'bigint'\n ? value\n : BigInt(decimalToAtomicUnits(value, decimals));\n\n if (atomic < 0n) {\n throw new PaymentProviderError(\n `Invalid atomic amount value: ${String(value)}`,\n );\n }\n\n if (atomic > BigInt(Number.MAX_SAFE_INTEGER)) {\n throw new PaymentProviderError(\n `${context} amount exceeds JavaScript's safe integer range.`,\n );\n }\n\n return Number(atomic);\n}\n\nexport function currencyMinorUnitDecimals(currency: string): number {\n const normalized = normalizeCurrency(currency, 'Payment');\n\n if (normalized === 'BTC') {\n return 8;\n }\n\n if (normalized === 'USDC') {\n return 6;\n }\n\n if (THREE_DECIMAL_CURRENCIES.has(normalized)) {\n return 3;\n }\n\n return ZERO_DECIMAL_CURRENCIES.has(normalized) ? 0 : 2;\n}\n\nexport function applyExpiryToPendingStatus(\n status: PaymentStatus,\n expiresAt?: Date,\n): PaymentStatus {\n if (!expiresAt || expiresAt >= new Date()) {\n return status;\n }\n\n return status === 'pending' || status === 'requires_action'\n ? 'expired'\n : status;\n}\n\nfunction normalizeDecimalPrecision(decimals: number): number {\n if (\n !Number.isSafeInteger(decimals) ||\n decimals < 0 ||\n decimals > MAX_DECIMAL_PRECISION\n ) {\n throw new PaymentProviderError(\n `Invalid decimal precision: ${String(decimals)}`,\n );\n }\n\n return decimals;\n}\n\nexport function compareDecimalAmounts(left: string, right: string): -1 | 0 | 1 {\n const [leftWhole, leftFraction = ''] = normalizeAmount(left).split('.');\n const [rightWhole, rightFraction = ''] = normalizeAmount(right).split('.');\n const leftWholeValue = BigInt(leftWhole ?? '0');\n const rightWholeValue = BigInt(rightWhole ?? '0');\n\n if (leftWholeValue > rightWholeValue) {\n return 1;\n }\n\n if (leftWholeValue < rightWholeValue) {\n return -1;\n }\n\n const scale = Math.max(leftFraction.length, rightFraction.length);\n const leftFractionValue = BigInt(\n (leftFraction + '0'.repeat(scale)).slice(0, scale) || '0',\n );\n const rightFractionValue = BigInt(\n (rightFraction + '0'.repeat(scale)).slice(0, scale) || '0',\n );\n\n if (leftFractionValue > rightFractionValue) {\n return 1;\n }\n\n if (leftFractionValue < rightFractionValue) {\n return -1;\n }\n\n return 0;\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason);\n }\n\n return new Promise((resolve, reject) => {\n let timeout: ReturnType<typeof setTimeout>;\n const handleAbort = (): void => {\n clearTimeout(timeout);\n reject(signal?.reason);\n };\n const handleResolve = (): void => {\n signal?.removeEventListener('abort', handleAbort);\n resolve();\n };\n\n timeout = setTimeout(handleResolve, ms);\n signal?.addEventListener('abort', handleAbort, { once: true });\n });\n}\n\nexport async function* pollPaymentStatus(\n input: WatchPaymentInput,\n getStatus: () => Promise<PaymentStatusResult>,\n): AsyncIterable<PaymentEvent> {\n const startedAt = Date.now();\n const pollIntervalMs = normalizePollIntervalMs(input.pollIntervalMs ?? 5_000);\n const timeoutMs =\n input.timeoutMs === undefined\n ? undefined\n : normalizeTimeoutMs(input.timeoutMs);\n let lastStatus: PaymentStatus | undefined;\n let lastResult: PaymentStatusResult | undefined;\n\n const remainingTimeoutMs = (): number | undefined =>\n timeoutMs === undefined ? undefined : timeoutMs - (Date.now() - startedAt);\n\n const yieldTimeoutEvent = async (): Promise<PaymentEvent> => ({\n ...(lastResult ?? (await getStatus())),\n event: 'timeout',\n status: lastResult?.status ?? 'pending',\n updatedAt: new Date(),\n });\n\n while (!input.signal?.aborted) {\n if ((remainingTimeoutMs() ?? 1) <= 0) {\n yield await yieldTimeoutEvent();\n return;\n }\n\n const status = await getStatus();\n lastResult = status;\n\n if (status.status !== lastStatus) {\n const event = toPaymentEvent(status);\n yield event;\n lastStatus = status.status;\n\n if (TERMINAL_PAYMENT_STATUSES.has(status.status)) {\n return;\n }\n }\n\n const remaining = remainingTimeoutMs();\n\n if (remaining !== undefined && remaining <= 0) {\n yield await yieldTimeoutEvent();\n return;\n }\n\n await sleep(\n remaining === undefined\n ? pollIntervalMs\n : Math.min(pollIntervalMs, remaining),\n input.signal,\n );\n }\n}\n\nfunction normalizePollIntervalMs(value: number): number {\n if (!Number.isInteger(value) || value <= 0) {\n throw new PaymentProviderError(\n `pollIntervalMs must be a positive integer, received ${String(value)}.`,\n );\n }\n\n return value;\n}\n\nfunction normalizeTimeoutMs(value: number): number {\n if (!Number.isInteger(value) || value < 0) {\n throw new PaymentProviderError(\n `timeoutMs must be a non-negative integer, received ${String(value)}.`,\n );\n }\n\n return value;\n}\n\nexport function toPaymentEvent(status: PaymentStatusResult): PaymentEvent {\n if (status.status === 'confirmed') {\n return { ...status, event: 'confirmed' };\n }\n\n if (status.status === 'failed') {\n return { ...status, event: 'failed' };\n }\n\n if (status.status === 'expired') {\n return { ...status, event: 'expired' };\n }\n\n if (status.status === 'refunded') {\n return { ...status, event: 'refunded' };\n }\n\n return { ...status, event: 'status' };\n}\n\nexport async function readJsonResponse<T>(\n response: Response,\n context: string,\n): Promise<T> {\n const text = await response.text();\n let body: unknown;\n\n if (text) {\n try {\n body = JSON.parse(text);\n } catch (error) {\n if (response.ok) {\n throw new PaymentProviderError(`${context}: invalid JSON response`, {\n cause: error,\n });\n }\n\n body = undefined;\n }\n }\n\n if (!response.ok) {\n const message =\n body && typeof body === 'object' && 'message' in body\n ? String((body as { message: unknown }).message)\n : body &&\n typeof body === 'object' &&\n typeof (body as { error?: unknown }).error === 'string'\n ? String((body as { error: string }).error)\n : body &&\n typeof body === 'object' &&\n 'error' in body &&\n typeof (body as { error?: { message?: unknown } }).error\n ?.message === 'string'\n ? (body as { error: { message: string } }).error.message\n : text || response.statusText;\n\n throw new PaymentProviderError(`${context}: ${message}`, {\n retryable: response.status >= 500,\n });\n }\n\n if (body === undefined) {\n throw new PaymentProviderError(`${context}: empty response body`);\n }\n\n return body as T;\n}\n\nexport function formEncode(params: Record<string, unknown>): string {\n const searchParams = new URLSearchParams();\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) {\n continue;\n }\n\n searchParams.set(key, String(value));\n }\n\n return searchParams.toString();\n}\n\nexport function throwUnsupported(message: string): never {\n throw new PaymentUnsupportedError(message);\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,49 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { a as PaymentVerificationError, i as PaymentUnsupportedError, n as PaymentError, r as PaymentProviderError, t as PaymentConfigurationError } from "./chunks/errors-feZrwnTy.js";
|
|
2
|
+
//#region src/factory.ts
|
|
3
3
|
async function createPaymentBackend(options) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return new BtcAdapter(backendOptions);
|
|
28
|
-
}
|
|
29
|
-
case "stripe": {
|
|
30
|
-
const { StripeAdapter } = await import("./adapters/stripe.js");
|
|
31
|
-
return new StripeAdapter(backendOptions);
|
|
32
|
-
}
|
|
33
|
-
default:
|
|
34
|
-
throw new PaymentConfigurationError(
|
|
35
|
-
`Unknown payment backend type: ${normalizedType}`
|
|
36
|
-
);
|
|
37
|
-
}
|
|
4
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) throw new PaymentConfigurationError("Payment backend options must be an object.");
|
|
5
|
+
const type = options.type;
|
|
6
|
+
if (typeof type !== "string" || !type.trim()) throw new PaymentConfigurationError("Payment backend type must be a string.");
|
|
7
|
+
const normalizedType = type.trim();
|
|
8
|
+
const backendOptions = {
|
|
9
|
+
...options,
|
|
10
|
+
type: normalizedType
|
|
11
|
+
};
|
|
12
|
+
switch (backendOptions.type) {
|
|
13
|
+
case "base-usdc": {
|
|
14
|
+
const { BaseUsdcAdapter } = await import("./adapters/base-usdc.js");
|
|
15
|
+
return new BaseUsdcAdapter(backendOptions);
|
|
16
|
+
}
|
|
17
|
+
case "btc": {
|
|
18
|
+
const { BtcAdapter } = await import("./adapters/btc.js");
|
|
19
|
+
return new BtcAdapter(backendOptions);
|
|
20
|
+
}
|
|
21
|
+
case "stripe": {
|
|
22
|
+
const { StripeAdapter } = await import("./adapters/stripe.js");
|
|
23
|
+
return new StripeAdapter(backendOptions);
|
|
24
|
+
}
|
|
25
|
+
default: throw new PaymentConfigurationError(`Unknown payment backend type: ${normalizedType}`);
|
|
26
|
+
}
|
|
38
27
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
createPaymentBackend
|
|
48
|
-
};
|
|
49
|
-
//# sourceMappingURL=index.js.map
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/index.ts
|
|
30
|
+
/** @internal */
|
|
31
|
+
var PACKAGE_VERSION_INITIALIZED = true;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { PACKAGE_VERSION_INITIALIZED, PaymentConfigurationError, PaymentError, PaymentProviderError, PaymentUnsupportedError, PaymentVerificationError, createPaymentBackend };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/factory.ts","../src/index.ts"],"sourcesContent":["import type { BaseUsdcAdapterOptions } from './adapters/base-usdc.js';\nimport type { BtcAdapterOptions } from './adapters/btc.js';\nimport type { StripeAdapterOptions } from './adapters/stripe.js';\nimport { PaymentConfigurationError } from './errors.js';\nimport type { PaymentBackend } from './types.js';\n\nexport type PaymentBackendOptions =\n | ({ type: 'base-usdc' } & BaseUsdcAdapterOptions)\n | ({ type: 'btc' } & BtcAdapterOptions)\n | ({ type: 'stripe' } & StripeAdapterOptions);\n\nexport async function createPaymentBackend(\n options: PaymentBackendOptions,\n): Promise<PaymentBackend> {\n if (!options || typeof options !== 'object' || Array.isArray(options)) {\n throw new PaymentConfigurationError(\n 'Payment backend options must be an object.',\n );\n }\n\n const type = (options as { type?: unknown }).type;\n\n if (typeof type !== 'string' || !type.trim()) {\n throw new PaymentConfigurationError(\n 'Payment backend type must be a string.',\n );\n }\n\n const normalizedType = type.trim();\n const backendOptions = {\n ...options,\n type: normalizedType,\n } as PaymentBackendOptions;\n\n switch (backendOptions.type) {\n case 'base-usdc': {\n const { BaseUsdcAdapter } = await import('./adapters/base-usdc.js');\n return new BaseUsdcAdapter(backendOptions);\n }\n case 'btc': {\n const { BtcAdapter } = await import('./adapters/btc.js');\n return new BtcAdapter(backendOptions);\n }\n case 'stripe': {\n const { StripeAdapter } = await import('./adapters/stripe.js');\n return new StripeAdapter(backendOptions);\n }\n default:\n throw new PaymentConfigurationError(\n `Unknown payment backend type: ${normalizedType}`,\n );\n }\n}\n","/**\n * @happyvertical/payments - Multi-backend payment provider abstraction.\n *\n * The default entrypoint exports only provider contracts, shared errors, and\n * the dynamic factory. Adapter implementations live behind subpath exports:\n * `@happyvertical/payments/base-usdc`, `@happyvertical/payments/btc`, and\n * `@happyvertical/payments/stripe`.\n */\n\nexport * from './errors.js';\nexport * from './factory.js';\nexport * from './types.js';\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/factory.ts","../src/index.ts"],"sourcesContent":["import type { BaseUsdcAdapterOptions } from './adapters/base-usdc.js';\nimport type { BtcAdapterOptions } from './adapters/btc.js';\nimport type { StripeAdapterOptions } from './adapters/stripe.js';\nimport { PaymentConfigurationError } from './errors.js';\nimport type { PaymentBackend } from './types.js';\n\nexport type PaymentBackendOptions =\n | ({ type: 'base-usdc' } & BaseUsdcAdapterOptions)\n | ({ type: 'btc' } & BtcAdapterOptions)\n | ({ type: 'stripe' } & StripeAdapterOptions);\n\nexport async function createPaymentBackend(\n options: PaymentBackendOptions,\n): Promise<PaymentBackend> {\n if (!options || typeof options !== 'object' || Array.isArray(options)) {\n throw new PaymentConfigurationError(\n 'Payment backend options must be an object.',\n );\n }\n\n const type = (options as { type?: unknown }).type;\n\n if (typeof type !== 'string' || !type.trim()) {\n throw new PaymentConfigurationError(\n 'Payment backend type must be a string.',\n );\n }\n\n const normalizedType = type.trim();\n const backendOptions = {\n ...options,\n type: normalizedType,\n } as PaymentBackendOptions;\n\n switch (backendOptions.type) {\n case 'base-usdc': {\n const { BaseUsdcAdapter } = await import('./adapters/base-usdc.js');\n return new BaseUsdcAdapter(backendOptions);\n }\n case 'btc': {\n const { BtcAdapter } = await import('./adapters/btc.js');\n return new BtcAdapter(backendOptions);\n }\n case 'stripe': {\n const { StripeAdapter } = await import('./adapters/stripe.js');\n return new StripeAdapter(backendOptions);\n }\n default:\n throw new PaymentConfigurationError(\n `Unknown payment backend type: ${normalizedType}`,\n );\n }\n}\n","/**\n * @happyvertical/payments - Multi-backend payment provider abstraction.\n *\n * The default entrypoint exports only provider contracts, shared errors, and\n * the dynamic factory. Adapter implementations live behind subpath exports:\n * `@happyvertical/payments/base-usdc`, `@happyvertical/payments/btc`, and\n * `@happyvertical/payments/stripe`.\n */\n\nexport * from './errors.js';\nexport * from './factory.js';\nexport * from './types.js';\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"mappings":";;AAWA,eAAsB,qBACpB,SACyB;CACzB,IAAI,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,GAClE,MAAM,IAAI,0BACR,4CACF;CAGF,MAAM,OAAQ,QAA+B;CAE7C,IAAI,OAAO,SAAS,YAAY,CAAC,KAAK,KAAK,GACzC,MAAM,IAAI,0BACR,wCACF;CAGF,MAAM,iBAAiB,KAAK,KAAK;CACjC,MAAM,iBAAiB;EACrB,GAAG;EACH,MAAM;CACR;CAEA,QAAQ,eAAe,MAAvB;EACE,KAAK,aAAa;GAChB,MAAM,EAAE,oBAAoB,MAAM,OAAO;GACzC,OAAO,IAAI,gBAAgB,cAAc;EAC3C;EACA,KAAK,OAAO;GACV,MAAM,EAAE,eAAe,MAAM,OAAO;GACpC,OAAO,IAAI,WAAW,cAAc;EACtC;EACA,KAAK,UAAU;GACb,MAAM,EAAE,kBAAkB,MAAM,OAAO;GACvC,OAAO,IAAI,cAAc,cAAc;EACzC;EACA,SACE,MAAM,IAAI,0BACR,iCAAiC,gBACnC;CACJ;AACF;;;;ACtCA,IAAa,8BAA8B"}
|
|
@@ -1,114 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
//#region src/testing/conformance.ts
|
|
2
|
+
var defaultPaymentOptionInput = {
|
|
3
|
+
quoteId: "quote-conformance",
|
|
4
|
+
amount: 1234,
|
|
5
|
+
currency: "USD",
|
|
6
|
+
expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1e3)
|
|
7
7
|
};
|
|
8
8
|
function definePaymentBackendConformanceTests(backendName, harness, testApi = readGlobalConformanceTestApi()) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
destination: "destination-conformance",
|
|
86
|
-
amount: 100,
|
|
87
|
-
currency: "USD"
|
|
88
|
-
}
|
|
89
|
-
);
|
|
90
|
-
expect(payout.backendId).toBe(backend.capabilities.id);
|
|
91
|
-
expect(payout.destination).toEqual(expect.any(String));
|
|
92
|
-
expect(payout.amount).toEqual(expect.any(Number));
|
|
93
|
-
expect(payout.currency).toEqual(expect.any(String));
|
|
94
|
-
expect(payout).toMatchObject(harness.expectedPayout ?? {});
|
|
95
|
-
});
|
|
96
|
-
});
|
|
9
|
+
const { describe, expect, it } = testApi;
|
|
10
|
+
describe(`${backendName} PaymentBackend conformance`, () => {
|
|
11
|
+
it("declares required capabilities", async () => {
|
|
12
|
+
const backend = await harness.createBackend();
|
|
13
|
+
expect(backend.capabilities.id).toEqual(expect.any(String));
|
|
14
|
+
expect(backend.capabilities.displayName).toEqual(expect.any(String));
|
|
15
|
+
expect(backend.capabilities.settlementCurrency).toEqual(expect.any(String));
|
|
16
|
+
expect(backend.capabilities.chain).toEqual(expect.any(String));
|
|
17
|
+
expect([
|
|
18
|
+
"address",
|
|
19
|
+
"url",
|
|
20
|
+
"psbt"
|
|
21
|
+
]).toContain(backend.capabilities.settlementShape);
|
|
22
|
+
expect(typeof backend.capabilities.x402Capable).toBe("boolean");
|
|
23
|
+
expect(typeof backend.capabilities.supportsRefunds).toBe("boolean");
|
|
24
|
+
expect(typeof backend.capabilities.supportsPayouts).toBe("boolean");
|
|
25
|
+
expect(typeof backend.capabilities.supportsWebhooks).toBe("boolean");
|
|
26
|
+
expect(backend.capabilities.confirmationLatency.expectedSeconds).toBeGreaterThanOrEqual(0);
|
|
27
|
+
expect(backend.capabilities).toMatchObject(harness.expectedCapabilities ?? {});
|
|
28
|
+
});
|
|
29
|
+
it("creates a payment option with stable quote metadata", async () => {
|
|
30
|
+
const backend = await harness.createBackend();
|
|
31
|
+
const input = harness.createPaymentOptionInput ?? defaultPaymentOptionInput;
|
|
32
|
+
const option = await backend.createPaymentOption(input);
|
|
33
|
+
expect(option.backendId).toBe(backend.capabilities.id);
|
|
34
|
+
expect(option.quoteId).toBe(input.quoteId);
|
|
35
|
+
expect(option.payTo).toEqual(expect.any(String));
|
|
36
|
+
expect(option.settlementCurrency).toBe(backend.capabilities.settlementCurrency);
|
|
37
|
+
expect(option.settlementShape).toBe(backend.capabilities.settlementShape);
|
|
38
|
+
expect(option.settlementAmount).toEqual(expect.any(Number));
|
|
39
|
+
expect(option.amount).toBe(input.amount);
|
|
40
|
+
expect(option.currency).toBe(input.currency);
|
|
41
|
+
expect(option.expiresAt).toBeInstanceOf(Date);
|
|
42
|
+
expect(option).toMatchObject(harness.expectedOption ?? {});
|
|
43
|
+
});
|
|
44
|
+
it("reports idempotent status for an option", async () => {
|
|
45
|
+
const backend = await harness.createBackend();
|
|
46
|
+
const input = harness.createPaymentOptionInput ?? defaultPaymentOptionInput;
|
|
47
|
+
const option = await backend.createPaymentOption(input);
|
|
48
|
+
const status = await backend.getStatus(input.quoteId, option.payTo);
|
|
49
|
+
expect(status.backendId).toBe(backend.capabilities.id);
|
|
50
|
+
expect(status.quoteId).toBe(input.quoteId);
|
|
51
|
+
expect(status.payTo).toBe(option.payTo);
|
|
52
|
+
expect(status.updatedAt).toBeInstanceOf(Date);
|
|
53
|
+
expect(status).toMatchObject(harness.expectedStatus ?? {});
|
|
54
|
+
});
|
|
55
|
+
it("returns an async iterable from watchPayment", async () => {
|
|
56
|
+
const backend = await harness.createBackend();
|
|
57
|
+
const input = harness.createPaymentOptionInput ?? defaultPaymentOptionInput;
|
|
58
|
+
const option = await backend.createPaymentOption(input);
|
|
59
|
+
const stream = backend.watchPayment({
|
|
60
|
+
quoteId: input.quoteId,
|
|
61
|
+
payTo: option.payTo,
|
|
62
|
+
pollIntervalMs: 1,
|
|
63
|
+
timeoutMs: 1
|
|
64
|
+
});
|
|
65
|
+
expect(typeof stream[Symbol.asyncIterator]).toBe("function");
|
|
66
|
+
await expect(stream[Symbol.asyncIterator]().next()).resolves.toMatchObject({ value: expect.objectContaining({
|
|
67
|
+
backendId: backend.capabilities.id,
|
|
68
|
+
quoteId: input.quoteId
|
|
69
|
+
}) });
|
|
70
|
+
});
|
|
71
|
+
it("sends payouts through the backend payout surface", async () => {
|
|
72
|
+
const backend = await harness.createBackend();
|
|
73
|
+
const payout = await backend.sendPayout(harness.payoutInput ?? {
|
|
74
|
+
destination: "destination-conformance",
|
|
75
|
+
amount: 100,
|
|
76
|
+
currency: "USD"
|
|
77
|
+
});
|
|
78
|
+
expect(payout.backendId).toBe(backend.capabilities.id);
|
|
79
|
+
expect(payout.destination).toEqual(expect.any(String));
|
|
80
|
+
expect(payout.amount).toEqual(expect.any(Number));
|
|
81
|
+
expect(payout.currency).toEqual(expect.any(String));
|
|
82
|
+
expect(payout).toMatchObject(harness.expectedPayout ?? {});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
97
85
|
}
|
|
98
86
|
function readGlobalConformanceTestApi() {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
throw new Error(
|
|
108
|
-
"definePaymentBackendConformanceTests requires a test API. Pass { describe, expect, it } from Vitest or enable compatible test globals."
|
|
109
|
-
);
|
|
87
|
+
const candidate = globalThis;
|
|
88
|
+
if (typeof candidate.describe === "function" && typeof candidate.it === "function" && typeof candidate.expect === "function" && typeof candidate.expect.any === "function" && typeof candidate.expect.objectContaining === "function") return {
|
|
89
|
+
describe: candidate.describe,
|
|
90
|
+
expect: candidate.expect,
|
|
91
|
+
it: candidate.it
|
|
92
|
+
};
|
|
93
|
+
throw new Error("definePaymentBackendConformanceTests requires a test API. Pass { describe, expect, it } from Vitest or enable compatible test globals.");
|
|
110
94
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
//# sourceMappingURL=conformance.js.map
|
|
95
|
+
//#endregion
|
|
96
|
+
export { definePaymentBackendConformanceTests };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=conformance.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance.js","sources":["../../src/testing/conformance.ts"],"sourcesContent":["import type {\n CreatePaymentOptionInput,\n PaymentBackend,\n PaymentBackendCapabilities,\n PaymentOption,\n PaymentStatusResult,\n PayoutResult,\n} from '../types.js';\n\ntype ConformanceExpectation = {\n toBe(expected: unknown): void;\n toEqual(expected: unknown): void;\n toBeGreaterThanOrEqual(expected: number): void;\n toBeInstanceOf(expected: unknown): void;\n toContain(expected: unknown): void;\n toMatchObject(expected: unknown): void;\n resolves: {\n toMatchObject(expected: unknown): Promise<void>;\n };\n};\n\ntype ConformanceExpect = ((actual: unknown) => ConformanceExpectation) & {\n any(expected: unknown): unknown;\n objectContaining(expected: Record<string, unknown>): unknown;\n};\n\nexport interface PaymentBackendConformanceTestApi {\n describe(name: string, callback: () => void): void;\n it(name: string, callback: () => void | Promise<void>): void;\n expect: ConformanceExpect;\n}\n\nexport interface PaymentBackendConformanceHarness {\n createBackend(): PaymentBackend | Promise<PaymentBackend>;\n createPaymentOptionInput?: CreatePaymentOptionInput;\n expectedCapabilities?: Partial<PaymentBackendCapabilities>;\n expectedOption?: Partial<PaymentOption>;\n expectedStatus?: Partial<PaymentStatusResult>;\n payoutInput?: Parameters<PaymentBackend['sendPayout']>[0];\n expectedPayout?: Partial<PayoutResult>;\n}\n\nconst futureExpiryOffsetMs = 365 * 24 * 60 * 60 * 1_000;\n\nconst defaultPaymentOptionInput: CreatePaymentOptionInput = {\n quoteId: 'quote-conformance',\n amount: 1234,\n currency: 'USD',\n expiresAt: new Date(Date.now() + futureExpiryOffsetMs),\n};\n\nexport function definePaymentBackendConformanceTests(\n backendName: string,\n harness: PaymentBackendConformanceHarness,\n testApi = readGlobalConformanceTestApi(),\n): void {\n const { describe, expect, it } = testApi;\n\n describe(`${backendName} PaymentBackend conformance`, () => {\n it('declares required capabilities', async () => {\n const backend = await harness.createBackend();\n\n expect(backend.capabilities.id).toEqual(expect.any(String));\n expect(backend.capabilities.displayName).toEqual(expect.any(String));\n expect(backend.capabilities.settlementCurrency).toEqual(\n expect.any(String),\n );\n expect(backend.capabilities.chain).toEqual(expect.any(String));\n expect(['address', 'url', 'psbt']).toContain(\n backend.capabilities.settlementShape,\n );\n expect(typeof backend.capabilities.x402Capable).toBe('boolean');\n expect(typeof backend.capabilities.supportsRefunds).toBe('boolean');\n expect(typeof backend.capabilities.supportsPayouts).toBe('boolean');\n expect(typeof backend.capabilities.supportsWebhooks).toBe('boolean');\n expect(\n backend.capabilities.confirmationLatency.expectedSeconds,\n ).toBeGreaterThanOrEqual(0);\n expect(backend.capabilities).toMatchObject(\n harness.expectedCapabilities ?? {},\n );\n });\n\n it('creates a payment option with stable quote metadata', async () => {\n const backend = await harness.createBackend();\n const input =\n harness.createPaymentOptionInput ?? defaultPaymentOptionInput;\n const option = await backend.createPaymentOption(input);\n\n expect(option.backendId).toBe(backend.capabilities.id);\n expect(option.quoteId).toBe(input.quoteId);\n expect(option.payTo).toEqual(expect.any(String));\n expect(option.settlementCurrency).toBe(\n backend.capabilities.settlementCurrency,\n );\n expect(option.settlementShape).toBe(backend.capabilities.settlementShape);\n expect(option.settlementAmount).toEqual(expect.any(Number));\n expect(option.amount).toBe(input.amount);\n expect(option.currency).toBe(input.currency);\n expect(option.expiresAt).toBeInstanceOf(Date);\n expect(option).toMatchObject(harness.expectedOption ?? {});\n });\n\n it('reports idempotent status for an option', async () => {\n const backend = await harness.createBackend();\n const input =\n harness.createPaymentOptionInput ?? defaultPaymentOptionInput;\n const option = await backend.createPaymentOption(input);\n const status = await backend.getStatus(input.quoteId, option.payTo);\n\n expect(status.backendId).toBe(backend.capabilities.id);\n expect(status.quoteId).toBe(input.quoteId);\n expect(status.payTo).toBe(option.payTo);\n expect(status.updatedAt).toBeInstanceOf(Date);\n expect(status).toMatchObject(harness.expectedStatus ?? {});\n });\n\n it('returns an async iterable from watchPayment', async () => {\n const backend = await harness.createBackend();\n const input =\n harness.createPaymentOptionInput ?? defaultPaymentOptionInput;\n const option = await backend.createPaymentOption(input);\n const stream = backend.watchPayment({\n quoteId: input.quoteId,\n payTo: option.payTo,\n pollIntervalMs: 1,\n timeoutMs: 1,\n });\n\n expect(typeof stream[Symbol.asyncIterator]).toBe('function');\n await expect(\n stream[Symbol.asyncIterator]().next(),\n ).resolves.toMatchObject({\n value: expect.objectContaining({\n backendId: backend.capabilities.id,\n quoteId: input.quoteId,\n }),\n });\n });\n\n it('sends payouts through the backend payout surface', async () => {\n const backend = await harness.createBackend();\n const payout = await backend.sendPayout(\n harness.payoutInput ?? {\n destination: 'destination-conformance',\n amount: 100,\n currency: 'USD',\n },\n );\n\n expect(payout.backendId).toBe(backend.capabilities.id);\n expect(payout.destination).toEqual(expect.any(String));\n expect(payout.amount).toEqual(expect.any(Number));\n expect(payout.currency).toEqual(expect.any(String));\n expect(payout).toMatchObject(harness.expectedPayout ?? {});\n });\n });\n}\n\nfunction readGlobalConformanceTestApi(): PaymentBackendConformanceTestApi {\n const candidate = globalThis as {\n describe?: unknown;\n expect?: unknown;\n it?: unknown;\n };\n\n if (\n typeof candidate.describe === 'function' &&\n typeof candidate.it === 'function' &&\n typeof candidate.expect === 'function' &&\n typeof (candidate.expect as { any?: unknown }).any === 'function' &&\n typeof (candidate.expect as { objectContaining?: unknown })\n .objectContaining === 'function'\n ) {\n return {\n describe:\n candidate.describe as PaymentBackendConformanceTestApi['describe'],\n expect: candidate.expect as ConformanceExpect,\n it: candidate.it as PaymentBackendConformanceTestApi['it'],\n };\n }\n\n throw new Error(\n 'definePaymentBackendConformanceTests requires a test API. Pass { describe, expect, it } from Vitest or enable compatible test globals.',\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conformance.js","names":[],"sources":["../../src/testing/conformance.ts"],"sourcesContent":["import type {\n CreatePaymentOptionInput,\n PaymentBackend,\n PaymentBackendCapabilities,\n PaymentOption,\n PaymentStatusResult,\n PayoutResult,\n} from '../types.js';\n\ntype ConformanceExpectation = {\n toBe(expected: unknown): void;\n toEqual(expected: unknown): void;\n toBeGreaterThanOrEqual(expected: number): void;\n toBeInstanceOf(expected: unknown): void;\n toContain(expected: unknown): void;\n toMatchObject(expected: unknown): void;\n resolves: {\n toMatchObject(expected: unknown): Promise<void>;\n };\n};\n\ntype ConformanceExpect = ((actual: unknown) => ConformanceExpectation) & {\n any(expected: unknown): unknown;\n objectContaining(expected: Record<string, unknown>): unknown;\n};\n\nexport interface PaymentBackendConformanceTestApi {\n describe(name: string, callback: () => void): void;\n it(name: string, callback: () => void | Promise<void>): void;\n expect: ConformanceExpect;\n}\n\nexport interface PaymentBackendConformanceHarness {\n createBackend(): PaymentBackend | Promise<PaymentBackend>;\n createPaymentOptionInput?: CreatePaymentOptionInput;\n expectedCapabilities?: Partial<PaymentBackendCapabilities>;\n expectedOption?: Partial<PaymentOption>;\n expectedStatus?: Partial<PaymentStatusResult>;\n payoutInput?: Parameters<PaymentBackend['sendPayout']>[0];\n expectedPayout?: Partial<PayoutResult>;\n}\n\nconst futureExpiryOffsetMs = 365 * 24 * 60 * 60 * 1_000;\n\nconst defaultPaymentOptionInput: CreatePaymentOptionInput = {\n quoteId: 'quote-conformance',\n amount: 1234,\n currency: 'USD',\n expiresAt: new Date(Date.now() + futureExpiryOffsetMs),\n};\n\nexport function definePaymentBackendConformanceTests(\n backendName: string,\n harness: PaymentBackendConformanceHarness,\n testApi = readGlobalConformanceTestApi(),\n): void {\n const { describe, expect, it } = testApi;\n\n describe(`${backendName} PaymentBackend conformance`, () => {\n it('declares required capabilities', async () => {\n const backend = await harness.createBackend();\n\n expect(backend.capabilities.id).toEqual(expect.any(String));\n expect(backend.capabilities.displayName).toEqual(expect.any(String));\n expect(backend.capabilities.settlementCurrency).toEqual(\n expect.any(String),\n );\n expect(backend.capabilities.chain).toEqual(expect.any(String));\n expect(['address', 'url', 'psbt']).toContain(\n backend.capabilities.settlementShape,\n );\n expect(typeof backend.capabilities.x402Capable).toBe('boolean');\n expect(typeof backend.capabilities.supportsRefunds).toBe('boolean');\n expect(typeof backend.capabilities.supportsPayouts).toBe('boolean');\n expect(typeof backend.capabilities.supportsWebhooks).toBe('boolean');\n expect(\n backend.capabilities.confirmationLatency.expectedSeconds,\n ).toBeGreaterThanOrEqual(0);\n expect(backend.capabilities).toMatchObject(\n harness.expectedCapabilities ?? {},\n );\n });\n\n it('creates a payment option with stable quote metadata', async () => {\n const backend = await harness.createBackend();\n const input =\n harness.createPaymentOptionInput ?? defaultPaymentOptionInput;\n const option = await backend.createPaymentOption(input);\n\n expect(option.backendId).toBe(backend.capabilities.id);\n expect(option.quoteId).toBe(input.quoteId);\n expect(option.payTo).toEqual(expect.any(String));\n expect(option.settlementCurrency).toBe(\n backend.capabilities.settlementCurrency,\n );\n expect(option.settlementShape).toBe(backend.capabilities.settlementShape);\n expect(option.settlementAmount).toEqual(expect.any(Number));\n expect(option.amount).toBe(input.amount);\n expect(option.currency).toBe(input.currency);\n expect(option.expiresAt).toBeInstanceOf(Date);\n expect(option).toMatchObject(harness.expectedOption ?? {});\n });\n\n it('reports idempotent status for an option', async () => {\n const backend = await harness.createBackend();\n const input =\n harness.createPaymentOptionInput ?? defaultPaymentOptionInput;\n const option = await backend.createPaymentOption(input);\n const status = await backend.getStatus(input.quoteId, option.payTo);\n\n expect(status.backendId).toBe(backend.capabilities.id);\n expect(status.quoteId).toBe(input.quoteId);\n expect(status.payTo).toBe(option.payTo);\n expect(status.updatedAt).toBeInstanceOf(Date);\n expect(status).toMatchObject(harness.expectedStatus ?? {});\n });\n\n it('returns an async iterable from watchPayment', async () => {\n const backend = await harness.createBackend();\n const input =\n harness.createPaymentOptionInput ?? defaultPaymentOptionInput;\n const option = await backend.createPaymentOption(input);\n const stream = backend.watchPayment({\n quoteId: input.quoteId,\n payTo: option.payTo,\n pollIntervalMs: 1,\n timeoutMs: 1,\n });\n\n expect(typeof stream[Symbol.asyncIterator]).toBe('function');\n await expect(\n stream[Symbol.asyncIterator]().next(),\n ).resolves.toMatchObject({\n value: expect.objectContaining({\n backendId: backend.capabilities.id,\n quoteId: input.quoteId,\n }),\n });\n });\n\n it('sends payouts through the backend payout surface', async () => {\n const backend = await harness.createBackend();\n const payout = await backend.sendPayout(\n harness.payoutInput ?? {\n destination: 'destination-conformance',\n amount: 100,\n currency: 'USD',\n },\n );\n\n expect(payout.backendId).toBe(backend.capabilities.id);\n expect(payout.destination).toEqual(expect.any(String));\n expect(payout.amount).toEqual(expect.any(Number));\n expect(payout.currency).toEqual(expect.any(String));\n expect(payout).toMatchObject(harness.expectedPayout ?? {});\n });\n });\n}\n\nfunction readGlobalConformanceTestApi(): PaymentBackendConformanceTestApi {\n const candidate = globalThis as {\n describe?: unknown;\n expect?: unknown;\n it?: unknown;\n };\n\n if (\n typeof candidate.describe === 'function' &&\n typeof candidate.it === 'function' &&\n typeof candidate.expect === 'function' &&\n typeof (candidate.expect as { any?: unknown }).any === 'function' &&\n typeof (candidate.expect as { objectContaining?: unknown })\n .objectContaining === 'function'\n ) {\n return {\n describe:\n candidate.describe as PaymentBackendConformanceTestApi['describe'],\n expect: candidate.expect as ConformanceExpect,\n it: candidate.it as PaymentBackendConformanceTestApi['it'],\n };\n }\n\n throw new Error(\n 'definePaymentBackendConformanceTests requires a test API. Pass { describe, expect, it } from Vitest or enable compatible test globals.',\n );\n}\n"],"mappings":"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"}
|
package/metadata.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/payments",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.80.1",
|
|
4
4
|
"description": "Payment backend abstraction with adapters for Base USDC, BTCPay Server, and Stripe",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
"@noble/hashes": "2.0.1",
|
|
70
70
|
"@scure/bip32": "2.0.1",
|
|
71
71
|
"@types/node": "24.12.2",
|
|
72
|
-
"@vitest/coverage-v8": "4.1.
|
|
73
|
-
"typescript": "
|
|
74
|
-
"vite": "
|
|
72
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
73
|
+
"typescript": "5.9.3",
|
|
74
|
+
"vite": "8.1.4",
|
|
75
75
|
"vite-plugin-dts": "4.5.4",
|
|
76
|
-
"vitest": "
|
|
76
|
+
"vitest": "4.1.10"
|
|
77
77
|
},
|
|
78
78
|
"scripts": {
|
|
79
79
|
"test": "vitest --config ../../vitest.package.config.ts run",
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
class PaymentError extends Error {
|
|
2
|
-
code;
|
|
3
|
-
retryable;
|
|
4
|
-
constructor(message, code = "PAYMENT_PROVIDER_ERROR", options = {}) {
|
|
5
|
-
super(
|
|
6
|
-
message,
|
|
7
|
-
options.cause === void 0 ? void 0 : { cause: options.cause }
|
|
8
|
-
);
|
|
9
|
-
this.name = "PaymentError";
|
|
10
|
-
this.code = code;
|
|
11
|
-
this.retryable = options.retryable ?? false;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
class PaymentConfigurationError extends PaymentError {
|
|
15
|
-
constructor(message, options = {}) {
|
|
16
|
-
super(message, "PAYMENT_CONFIGURATION_ERROR", options);
|
|
17
|
-
this.name = "PaymentConfigurationError";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
class PaymentProviderError extends PaymentError {
|
|
21
|
-
constructor(message, options = {}) {
|
|
22
|
-
super(message, "PAYMENT_PROVIDER_ERROR", options);
|
|
23
|
-
this.name = "PaymentProviderError";
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
class PaymentUnsupportedError extends PaymentError {
|
|
27
|
-
constructor(message, options = {}) {
|
|
28
|
-
super(message, "PAYMENT_UNSUPPORTED", options);
|
|
29
|
-
this.name = "PaymentUnsupportedError";
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
class PaymentVerificationError extends PaymentError {
|
|
33
|
-
constructor(message, options = {}) {
|
|
34
|
-
super(message, "PAYMENT_VERIFICATION_FAILED", options);
|
|
35
|
-
this.name = "PaymentVerificationError";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
export {
|
|
39
|
-
PaymentConfigurationError as P,
|
|
40
|
-
PaymentError as a,
|
|
41
|
-
PaymentProviderError as b,
|
|
42
|
-
PaymentUnsupportedError as c,
|
|
43
|
-
PaymentVerificationError as d
|
|
44
|
-
};
|
|
45
|
-
//# sourceMappingURL=errors-BgFC46qQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors-BgFC46qQ.js","sources":["../../src/errors.ts"],"sourcesContent":["export type PaymentErrorCode =\n | 'PAYMENT_CONFIGURATION_ERROR'\n | 'PAYMENT_PROVIDER_ERROR'\n | 'PAYMENT_UNSUPPORTED'\n | 'PAYMENT_VERIFICATION_FAILED'\n | 'PAYMENT_TIMEOUT'\n | 'PAYMENT_INVALID_INPUT';\n\nexport class PaymentError extends Error {\n readonly code: PaymentErrorCode;\n readonly retryable: boolean;\n\n constructor(\n message: string,\n code: PaymentErrorCode = 'PAYMENT_PROVIDER_ERROR',\n options: { cause?: unknown; retryable?: boolean } = {},\n ) {\n super(\n message,\n options.cause === undefined ? undefined : { cause: options.cause },\n );\n this.name = 'PaymentError';\n this.code = code;\n this.retryable = options.retryable ?? false;\n }\n}\n\nexport class PaymentConfigurationError extends PaymentError {\n constructor(message: string, options: { cause?: unknown } = {}) {\n super(message, 'PAYMENT_CONFIGURATION_ERROR', options);\n this.name = 'PaymentConfigurationError';\n }\n}\n\nexport class PaymentProviderError extends PaymentError {\n constructor(\n message: string,\n options: { cause?: unknown; retryable?: boolean } = {},\n ) {\n super(message, 'PAYMENT_PROVIDER_ERROR', options);\n this.name = 'PaymentProviderError';\n }\n}\n\nexport class PaymentUnsupportedError extends PaymentError {\n constructor(message: string, options: { cause?: unknown } = {}) {\n super(message, 'PAYMENT_UNSUPPORTED', options);\n this.name = 'PaymentUnsupportedError';\n }\n}\n\nexport class PaymentVerificationError extends PaymentError {\n constructor(message: string, options: { cause?: unknown } = {}) {\n super(message, 'PAYMENT_VERIFICATION_FAILED', options);\n this.name = 'PaymentVerificationError';\n }\n}\n"],"names":[],"mappings":"AAQO,MAAM,qBAAqB,MAAM;AAAA,EAC7B;AAAA,EACA;AAAA,EAET,YACE,SACA,OAAyB,0BACzB,UAAoD,CAAA,GACpD;AACA;AAAA,MACE;AAAA,MACA,QAAQ,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAA;AAAA,IAAM;AAEnE,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,YAAY,QAAQ,aAAa;AAAA,EACxC;AACF;AAEO,MAAM,kCAAkC,aAAa;AAAA,EAC1D,YAAY,SAAiB,UAA+B,IAAI;AAC9D,UAAM,SAAS,+BAA+B,OAAO;AACrD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,6BAA6B,aAAa;AAAA,EACrD,YACE,SACA,UAAoD,IACpD;AACA,UAAM,SAAS,0BAA0B,OAAO;AAChD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,gCAAgC,aAAa;AAAA,EACxD,YAAY,SAAiB,UAA+B,IAAI;AAC9D,UAAM,SAAS,uBAAuB,OAAO;AAC7C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,iCAAiC,aAAa;AAAA,EACzD,YAAY,SAAiB,UAA+B,IAAI;AAC9D,UAAM,SAAS,+BAA+B,OAAO;AACrD,SAAK,OAAO;AAAA,EACd;AACF;"}
|