creem-datafast 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/headers.ts","../src/core/metadata.ts","../src/core/checkout.ts","../src/core/creem-client.ts","../src/core/logger.ts","../src/core/datafast-client.ts","../src/core/idempotency.ts","../src/core/signature.ts","../src/core/amount.ts","../src/core/mapper.ts","../src/core/transaction.ts","../src/core/webhook.ts","../src/index.ts"],"sourcesContent":["import type { HeadersLike } from \"./types.js\";\n\nfunction isHeadersInstance(headers: HeadersLike): headers is Headers {\n return typeof Headers !== \"undefined\" && headers instanceof Headers;\n}\n\nfunction normalizeArrayValue(value: string | string[] | undefined): string | undefined {\n if (typeof value === \"string\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value[0];\n }\n\n return undefined;\n}\n\nexport function getHeaderValue(headers: HeadersLike, name: string): string | undefined {\n if (isHeadersInstance(headers)) {\n return headers.get(name) ?? headers.get(name.toLowerCase()) ?? undefined;\n }\n\n const expected = name.toLowerCase();\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === expected) {\n return normalizeArrayValue(value);\n }\n }\n\n return undefined;\n}\n\nexport function getHeaderValues(headers: HeadersLike, name: string): string[] {\n if (isHeadersInstance(headers)) {\n const value = headers.get(name) ?? headers.get(name.toLowerCase());\n return value ? [value] : [];\n }\n\n const expected = name.toLowerCase();\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() !== expected) {\n continue;\n }\n\n if (typeof value === \"string\") {\n return [value];\n }\n\n return value ?? [];\n }\n\n return [];\n}\n","import type { CheckoutMetadata, DataFastTracking } from \"./types.js\";\n\ninterface MergeTrackingOptions {\n captureSessionId?: boolean;\n preferTracking?: boolean;\n}\n\nfunction asMetadataString(value: unknown): string | undefined {\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n\n return undefined;\n}\n\nexport function readTrackingFromMetadata(metadata?: CheckoutMetadata): DataFastTracking {\n if (!metadata) {\n return {};\n }\n\n return {\n visitorId: asMetadataString(metadata.datafast_visitor_id),\n sessionId: asMetadataString(metadata.datafast_session_id)\n };\n}\n\nexport function mergeTrackingIntoMetadata(\n metadata: CheckoutMetadata | undefined,\n tracking: DataFastTracking,\n options: MergeTrackingOptions = {}\n): CheckoutMetadata {\n const result: CheckoutMetadata = { ...(metadata ?? {}) };\n const captureSessionId = options.captureSessionId ?? true;\n const preferTracking = options.preferTracking ?? false;\n\n if (tracking.visitorId && (preferTracking || result.datafast_visitor_id === undefined)) {\n result.datafast_visitor_id = tracking.visitorId;\n }\n\n if (\n captureSessionId &&\n tracking.sessionId &&\n (preferTracking || result.datafast_session_id === undefined)\n ) {\n result.datafast_session_id = tracking.sessionId;\n }\n\n return result;\n}\n","import { getHeaderValue } from \"./headers.js\";\nimport { mergeTrackingIntoMetadata, readTrackingFromMetadata } from \"./metadata.js\";\nimport { readTrackingFromCookieHeader } from \"./cookies.js\";\nimport { CreemDataFastError, MissingTrackingError } from \"./errors.js\";\nimport type {\n CheckoutDependencies,\n CreateCheckoutContext,\n CreateCheckoutParams,\n CreateCheckoutResult,\n DataFastTracking\n} from \"./types.js\";\n\nfunction hasExplicitTracking(tracking?: DataFastTracking): boolean {\n return Boolean(tracking?.visitorId || tracking?.sessionId);\n}\n\nfunction resolveTracking(\n explicit: DataFastTracking | undefined,\n metadataTracking: DataFastTracking,\n queryTracking: DataFastTracking,\n cookieTracking: DataFastTracking,\n captureSessionId: boolean\n): DataFastTracking {\n return {\n visitorId:\n explicit?.visitorId ??\n metadataTracking.visitorId ??\n queryTracking.visitorId ??\n cookieTracking.visitorId,\n sessionId: captureSessionId\n ? (explicit?.sessionId ??\n metadataTracking.sessionId ??\n queryTracking.sessionId ??\n cookieTracking.sessionId)\n : undefined\n };\n}\n\nfunction resolveCookieTracking(context?: CreateCheckoutContext): DataFastTracking {\n const requestCookieTracking = readTrackingFromCookieHeader(\n context?.request ? getHeaderValue(context.request.headers, \"cookie\") : undefined\n );\n const fallbackCookieTracking = readTrackingFromCookieHeader(context?.cookieHeader);\n\n return {\n visitorId: requestCookieTracking.visitorId ?? fallbackCookieTracking.visitorId,\n sessionId: requestCookieTracking.sessionId ?? fallbackCookieTracking.sessionId\n };\n}\n\nfunction readTrackingFromRequestUrl(request?: CreateCheckoutContext[\"request\"]): DataFastTracking {\n const requestUrl = request?.url;\n if (!requestUrl) {\n return {};\n }\n\n try {\n const url = new URL(requestUrl, \"http://localhost\");\n return {\n visitorId: url.searchParams.get(\"datafast_visitor_id\") ?? undefined,\n sessionId: url.searchParams.get(\"datafast_session_id\") ?? undefined\n };\n } catch {\n return {};\n }\n}\n\nexport async function createCheckout(\n params: CreateCheckoutParams,\n context: CreateCheckoutContext | undefined,\n dependencies: CheckoutDependencies\n): Promise<CreateCheckoutResult> {\n const cookieTracking = resolveCookieTracking(context);\n const metadataTracking = readTrackingFromMetadata(params.metadata);\n const queryTracking = readTrackingFromRequestUrl(context?.request);\n const tracking = resolveTracking(\n params.tracking,\n metadataTracking,\n queryTracking,\n cookieTracking,\n dependencies.captureSessionId\n );\n\n const strictTracking = context?.strictTracking ?? dependencies.strictTracking;\n if (strictTracking && !tracking.visitorId) {\n throw new MissingTrackingError(\"Missing datafast_visitor_id while strict tracking is enabled.\");\n }\n\n if (!tracking.visitorId) {\n dependencies.logger.warn(\"Creating Creem checkout without DataFast visitor tracking.\");\n }\n\n const finalMetadata = mergeTrackingIntoMetadata(params.metadata, tracking, {\n captureSessionId: dependencies.captureSessionId,\n preferTracking: hasExplicitTracking(params.tracking)\n });\n\n const raw = await dependencies.creem.createCheckout({\n productId: params.productId,\n successUrl: params.successUrl,\n requestId: params.requestId,\n units: params.units,\n discountCode: params.discountCode,\n customer: params.customer,\n customFields: params.customFields,\n metadata: finalMetadata\n });\n\n const checkoutId = raw.id;\n const checkoutUrl = raw.checkoutUrl ?? raw.checkout_url;\n\n if (typeof checkoutId !== \"string\" || typeof checkoutUrl !== \"string\") {\n throw new CreemDataFastError(\"Creem checkout response is missing id or checkoutUrl.\");\n }\n\n return {\n checkoutId,\n checkoutUrl,\n injectedTracking: tracking,\n finalMetadata,\n raw\n };\n}\n","import { Creem } from \"creem\";\n\nimport { CreemDataFastError } from \"./errors.js\";\nimport type {\n CreemDataFastOptions,\n CreemSdkClientLike,\n InternalCreateCheckoutRequest,\n InternalCreemClient\n} from \"./types.js\";\n\nfunction assertCreemClient(candidate: unknown): asserts candidate is CreemSdkClientLike {\n if (\n !candidate ||\n typeof candidate !== \"object\" ||\n !(\"checkouts\" in candidate) ||\n !(\"transactions\" in candidate)\n ) {\n throw new CreemDataFastError(\"Invalid creem client provided.\");\n }\n}\n\nexport function createCreemClient(options: CreemDataFastOptions): InternalCreemClient {\n const sdkCandidate =\n options.creemClient ??\n (options.creemApiKey\n ? new Creem({\n apiKey: options.creemApiKey,\n serverIdx: options.testMode ? 1 : 0\n })\n : undefined);\n\n if (!sdkCandidate) {\n throw new CreemDataFastError(\"Missing creemApiKey or creemClient.\");\n }\n\n assertCreemClient(sdkCandidate);\n\n if (\n !sdkCandidate.checkouts ||\n typeof sdkCandidate.checkouts.create !== \"function\" ||\n !sdkCandidate.transactions ||\n typeof sdkCandidate.transactions.getById !== \"function\"\n ) {\n throw new CreemDataFastError(\"Provided creem client does not expose the expected SDK methods.\");\n }\n\n return {\n async createCheckout(request: InternalCreateCheckoutRequest) {\n return sdkCandidate.checkouts!.create(request);\n },\n async getTransactionById(transactionId: string) {\n return sdkCandidate.transactions!.getById(transactionId);\n }\n };\n}\n","import type { LoggerLike } from \"./types.js\";\n\nconst noop = () => undefined;\n\nexport const noopLogger: LoggerLike = {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop\n};\n\nexport function resolveLogger(logger?: LoggerLike): LoggerLike {\n return logger ?? noopLogger;\n}\n","import { DataFastRequestError } from \"./errors.js\";\nimport { resolveLogger } from \"./logger.js\";\nimport type {\n CreemDataFastOptions,\n DataFastPaymentPayload,\n InternalDataFastClient,\n RetryConfig\n} from \"./types.js\";\n\nconst DATAFAST_PAYMENTS_URL = \"https://datafa.st/api/v1/payments\";\nconst DEFAULT_TIMEOUT_MS = 8_000;\nconst DEFAULT_RETRIES = 1;\nconst DEFAULT_BASE_DELAY_MS = 250;\nconst DEFAULT_MAX_DELAY_MS = 2_000;\nconst MAX_ERROR_BODY_LENGTH = 1_024;\nconst RETRYABLE_STATUSES = new Set([408, 429, 500, 502, 503, 504]);\n\nfunction resolveFetch(fetchImplementation?: typeof globalThis.fetch): typeof globalThis.fetch {\n const resolved = fetchImplementation ?? globalThis.fetch;\n\n if (!resolved) {\n throw new DataFastRequestError(\"Fetch implementation is required to call DataFast.\", {\n retryable: false\n });\n }\n\n return resolved;\n}\n\nfunction parseResponseBody(body: string): unknown {\n if (!body) {\n return undefined;\n }\n\n try {\n return JSON.parse(body);\n } catch {\n return body;\n }\n}\n\nfunction truncateString(value: string): string {\n if (value.length <= MAX_ERROR_BODY_LENGTH) {\n return value;\n }\n\n return `${value.slice(0, MAX_ERROR_BODY_LENGTH)}...[truncated]`;\n}\n\nfunction sanitizeResponseBody(body: unknown): unknown {\n if (body === undefined) {\n return undefined;\n }\n\n if (typeof body === \"string\") {\n return truncateString(body);\n }\n\n try {\n const serialized = JSON.stringify(body);\n\n if (serialized.length <= MAX_ERROR_BODY_LENGTH) {\n return body;\n }\n\n return truncateString(serialized);\n } catch {\n return \"[unserializable response body]\";\n }\n}\n\nfunction normalizePositiveInteger(value: number | undefined, fallback: number): number {\n if (value === undefined) {\n return fallback;\n }\n\n if (!Number.isFinite(value) || value < 0) {\n return fallback;\n }\n\n return Math.floor(value);\n}\n\nfunction resolveRetryConfig(retry?: RetryConfig): Required<RetryConfig> {\n return {\n retries: normalizePositiveInteger(retry?.retries, DEFAULT_RETRIES),\n baseDelayMs: normalizePositiveInteger(retry?.baseDelayMs, DEFAULT_BASE_DELAY_MS),\n maxDelayMs: normalizePositiveInteger(retry?.maxDelayMs, DEFAULT_MAX_DELAY_MS)\n };\n}\n\nfunction resolveTimeoutMs(timeoutMs: number | undefined): number {\n return normalizePositiveInteger(timeoutMs, DEFAULT_TIMEOUT_MS);\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction getRequestId(response: Response): string | undefined {\n return (\n response.headers.get(\"x-request-id\") ??\n response.headers.get(\"request-id\") ??\n response.headers.get(\"x-datafast-request-id\") ??\n undefined\n );\n}\n\nfunction isRetryableStatus(status: number): boolean {\n return RETRYABLE_STATUSES.has(status);\n}\n\nfunction computeDelayMs(attempt: number, retry: Required<RetryConfig>): number {\n return Math.min(retry.baseDelayMs * 2 ** attempt + Math.random() * 100, retry.maxDelayMs);\n}\n\nasync function sleep(delayMs: number): Promise<void> {\n if (delayMs <= 0) {\n return;\n }\n\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n}\n\nexport function createDataFastClient(options: CreemDataFastOptions): InternalDataFastClient {\n const fetchImplementation = resolveFetch(options.fetch);\n const logger = resolveLogger(options.logger);\n const timeoutMs = resolveTimeoutMs(options.timeoutMs);\n const retry = resolveRetryConfig(options.retry);\n\n return {\n async sendPayment(payload: DataFastPaymentPayload) {\n for (let attempt = 0; attempt <= retry.retries; attempt += 1) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetchImplementation(DATAFAST_PAYMENTS_URL, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${options.datafastApiKey}`,\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(payload),\n signal: controller.signal\n });\n\n const responseText = await response.text();\n const responseBody = sanitizeResponseBody(parseResponseBody(responseText));\n\n if (response.ok) {\n return responseBody;\n }\n\n const retryable = isRetryableStatus(response.status);\n const error = new DataFastRequestError(\n `DataFast request failed with status ${response.status}.`,\n {\n status: response.status,\n statusText: response.statusText,\n requestId: getRequestId(response),\n retryable,\n responseBody\n }\n );\n\n if (retryable && attempt < retry.retries) {\n logger.warn(\"Retrying DataFast request after retryable response.\", {\n attempt: attempt + 1,\n nextAttempt: attempt + 2,\n requestId: error.requestId,\n status: error.status,\n statusText: error.statusText\n });\n await sleep(computeDelayMs(attempt, retry));\n continue;\n }\n\n throw error;\n } catch (error) {\n const retryable = isAbortError(error) || !(error instanceof DataFastRequestError);\n\n if (retryable && attempt < retry.retries) {\n logger.warn(\"Retrying DataFast request after transport failure.\", {\n attempt: attempt + 1,\n nextAttempt: attempt + 2,\n reason: isAbortError(error) ? \"timeout\" : \"network_error\"\n });\n await sleep(computeDelayMs(attempt, retry));\n continue;\n }\n\n if (error instanceof DataFastRequestError) {\n throw error;\n }\n\n if (isAbortError(error)) {\n logger.warn(\"DataFast request timed out.\", {\n attempts: attempt + 1,\n timeoutMs\n });\n throw new DataFastRequestError(\n `DataFast request timed out after ${timeoutMs}ms.`,\n {\n retryable: true\n },\n { cause: error }\n );\n }\n\n throw new DataFastRequestError(\n \"DataFast request failed due to a network error.\",\n {\n retryable: true\n },\n { cause: error instanceof Error ? error : undefined }\n );\n } finally {\n clearTimeout(timeout);\n }\n }\n\n throw new DataFastRequestError(\"DataFast request failed unexpectedly.\", {\n retryable: false\n });\n }\n };\n}\n","import type { IdempotencyStore } from \"./types.js\";\n\ntype EntryStatus = \"processing\" | \"processed\";\n\ntype Entry = {\n expiresAt: number;\n status: EntryStatus;\n};\n\nexport class MemoryIdempotencyStore implements IdempotencyStore {\n private readonly values = new Map<string, Entry>();\n\n private getActiveEntry(key: string): Entry | undefined {\n const entry = this.values.get(key);\n if (!entry) {\n return undefined;\n }\n\n if (Date.now() > entry.expiresAt) {\n this.values.delete(key);\n return undefined;\n }\n\n return entry;\n }\n\n async claim(key: string, ttlSeconds = 300): Promise<boolean> {\n if (this.getActiveEntry(key)) {\n return false;\n }\n\n this.values.set(key, {\n expiresAt: Date.now() + ttlSeconds * 1000,\n status: \"processing\"\n });\n return true;\n }\n\n async complete(key: string, ttlSeconds = 86400): Promise<void> {\n this.values.set(key, {\n expiresAt: Date.now() + ttlSeconds * 1000,\n status: \"processed\"\n });\n }\n\n async release(key: string): Promise<void> {\n this.values.delete(key);\n }\n}\n\nexport function resolveIdempotencyStore(store?: IdempotencyStore): IdempotencyStore {\n return store ?? new MemoryIdempotencyStore();\n}\n\nexport function getIdempotencyKey(eventId: string): string {\n return `creem:event:${eventId}`;\n}\n\nexport async function claimEvent(\n eventId: string,\n store: IdempotencyStore,\n ttlSeconds: number\n): Promise<boolean> {\n return store.claim(getIdempotencyKey(eventId), ttlSeconds);\n}\n\nexport async function completeEvent(\n eventId: string,\n store: IdempotencyStore,\n ttlSeconds: number\n): Promise<void> {\n await store.complete(getIdempotencyKey(eventId), ttlSeconds);\n}\n\nexport async function releaseEvent(eventId: string, store: IdempotencyStore): Promise<void> {\n await store.release(getIdempotencyKey(eventId));\n}\n","import { CreemDataFastError } from \"./errors.js\";\nimport { getHeaderValue } from \"./headers.js\";\nimport type { HeadersLike } from \"./types.js\";\n\nconst textEncoder = new TextEncoder();\n\nfunction normalizeSignature(signature: string): string {\n return signature.trim().replace(/^sha256=/i, \"\");\n}\n\nfunction hexToUint8Array(input: string): Uint8Array | undefined {\n if (input.length === 0 || input.length % 2 !== 0 || /[^0-9a-f]/iu.test(input)) {\n return undefined;\n }\n\n const bytes = new Uint8Array(input.length / 2);\n\n for (let index = 0; index < input.length; index += 2) {\n const byte = Number.parseInt(input.slice(index, index + 2), 16);\n if (Number.isNaN(byte)) {\n return undefined;\n }\n\n bytes[index / 2] = byte;\n }\n\n return bytes;\n}\n\nfunction getWebCrypto(): SubtleCrypto {\n const subtle = globalThis.crypto?.subtle;\n if (subtle) {\n return subtle;\n }\n\n throw new CreemDataFastError(\"Web Crypto API is required to verify Creem signatures.\");\n}\n\nfunction toArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n if (bytes.buffer instanceof ArrayBuffer) {\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);\n }\n\n return Uint8Array.from(bytes).buffer;\n}\n\nexport function extractHeader(headers: HeadersLike, name: string): string | undefined {\n return getHeaderValue(headers, name);\n}\n\nexport async function verifyCreemSignature(\n rawBody: string,\n webhookSecret: string,\n signature: string\n): Promise<boolean> {\n const signatureBytes = hexToUint8Array(normalizeSignature(signature).toLowerCase());\n if (!signatureBytes) {\n return false;\n }\n\n const subtle = getWebCrypto();\n const key = await subtle.importKey(\n \"raw\",\n textEncoder.encode(webhookSecret),\n {\n name: \"HMAC\",\n hash: \"SHA-256\"\n },\n false,\n [\"verify\"]\n );\n\n return subtle.verify(\n \"HMAC\",\n key,\n toArrayBuffer(signatureBytes),\n toArrayBuffer(textEncoder.encode(rawBody))\n );\n}\n","const 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 \"UGX\",\n \"VND\",\n \"VUV\",\n \"XAF\",\n \"XOF\",\n \"XPF\"\n]);\n\nconst THREE_DECIMAL_CURRENCIES = new Set([\"BHD\", \"IQD\", \"JOD\", \"KWD\", \"LYD\", \"OMR\", \"TND\"]);\n\nexport function currencyExponent(currency: string): 0 | 2 | 3 {\n const normalized = currency.toUpperCase();\n\n if (ZERO_DECIMAL_CURRENCIES.has(normalized)) {\n return 0;\n }\n\n if (THREE_DECIMAL_CURRENCIES.has(normalized)) {\n return 3;\n }\n\n return 2;\n}\n\nexport function minorToMajor(amount: number, currency: string): number {\n const exponent = currencyExponent(currency);\n // Creem amounts arrive as integer minor units; normalize only by ISO currency exponent.\n return amount / 10 ** exponent;\n}\n","import { minorToMajor } from \"./amount.js\";\nimport { CreemDataFastError } from \"./errors.js\";\nimport type {\n CheckoutCompletedCustomer,\n CheckoutCompletedEvent,\n CheckoutMetadata,\n DataFastPaymentPayload,\n NormalizedTransaction,\n RefundCreatedCustomer,\n RefundCreatedEvent,\n SubscriptionPaidCustomer,\n SubscriptionPaidEvent\n} from \"./types.js\";\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return undefined;\n }\n\n return value as Record<string, unknown>;\n}\n\nfunction readMetadataValue(\n metadata: Record<string, unknown> | undefined,\n key: string\n): string | undefined {\n const value = metadata?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction toIsoTimestamp(value: number | string | undefined): string | undefined {\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return undefined;\n }\n\n const ms = value > 1e12 ? value : value * 1000;\n return new Date(ms).toISOString();\n}\n\nfunction toCustomer(\n value:\n | CheckoutCompletedCustomer\n | SubscriptionPaidCustomer\n | RefundCreatedCustomer\n | string\n | undefined\n): CheckoutCompletedCustomer | SubscriptionPaidCustomer | RefundCreatedCustomer | undefined {\n return typeof value === \"object\" && value !== null ? value : undefined;\n}\n\nfunction resolveCustomerId(\n value:\n | CheckoutCompletedCustomer\n | SubscriptionPaidCustomer\n | RefundCreatedCustomer\n | string\n | undefined\n): string | undefined {\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n\n return toCustomer(value)?.id;\n}\n\nfunction withOptionalFields(\n base: DataFastPaymentPayload,\n fields: Partial<DataFastPaymentPayload>\n): DataFastPaymentPayload {\n const result: DataFastPaymentPayload = { ...base };\n\n for (const [key, value] of Object.entries(fields)) {\n if (value !== undefined) {\n result[key as keyof DataFastPaymentPayload] = value as never;\n }\n }\n\n return result;\n}\n\nfunction getCheckoutMetadata(event: CheckoutCompletedEvent): CheckoutMetadata | undefined {\n return (\n (asRecord(event.object?.metadata) as CheckoutMetadata | undefined) ??\n (asRecord(event.object?.order?.metadata) as CheckoutMetadata | undefined)\n );\n}\n\nfunction getSubscriptionMetadata(event: SubscriptionPaidEvent): CheckoutMetadata | undefined {\n return asRecord(event.object?.metadata) as CheckoutMetadata | undefined;\n}\n\nfunction getRefundVisitorId(event: RefundCreatedEvent): string | undefined {\n return (\n readMetadataValue(asRecord(event.object?.metadata), \"datafast_visitor_id\") ??\n readMetadataValue(asRecord(event.object?.transaction?.metadata), \"datafast_visitor_id\")\n );\n}\n\nfunction getRefundAmount(event: RefundCreatedEvent): number | undefined {\n return event.object?.refund_amount ?? event.object?.refundAmount;\n}\n\nfunction getRefundCurrency(event: RefundCreatedEvent): string | undefined {\n return event.object?.refund_currency ?? event.object?.refundCurrency;\n}\n\nfunction getRefundTimestamp(event: RefundCreatedEvent): string | undefined {\n return toIsoTimestamp(\n event.object?.created_at ??\n event.object?.createdAt ??\n event.created_at ??\n event.createdAt ??\n event.object?.transaction?.created_at ??\n event.object?.transaction?.createdAt\n );\n}\n\nfunction isRefundRenewal(event: RefundCreatedEvent): boolean {\n const transaction = event.object?.transaction;\n return (\n Boolean(typeof transaction?.subscription === \"string\" && transaction.subscription.length > 0) ||\n transaction?.type === \"invoice\"\n );\n}\n\nexport function mapCheckoutCompletedToPayment(\n event: CheckoutCompletedEvent\n): DataFastPaymentPayload {\n const order = event.object?.order;\n if (!order) {\n throw new CreemDataFastError(\"checkout.completed payload is missing order.\");\n }\n\n const customerValue = event.object?.customer;\n const customer = toCustomer(customerValue);\n const metadata = getCheckoutMetadata(event);\n\n return withOptionalFields(\n {\n amount: minorToMajor(order.amount, order.currency),\n currency: order.currency,\n transaction_id: order.id,\n renewal: false\n },\n {\n customer_id: resolveCustomerId(customerValue),\n datafast_visitor_id: readMetadataValue(metadata, \"datafast_visitor_id\"),\n email: customer?.email,\n name: customer?.name\n }\n );\n}\n\nexport function mapSubscriptionPaidToPayment(\n event: SubscriptionPaidEvent,\n transaction?: NormalizedTransaction\n): DataFastPaymentPayload {\n const customerValue = event.object?.customer;\n const customer = toCustomer(customerValue);\n const metadata = getSubscriptionMetadata(event);\n const product = event.object?.product;\n const transactionId =\n transaction?.id ?? event.object?.last_transaction_id ?? event.object?.lastTransactionId;\n\n if (!transactionId) {\n throw new CreemDataFastError(\"subscription.paid payload is missing last_transaction_id.\");\n }\n\n if (!transaction) {\n if (!product || typeof product.price !== \"number\" || typeof product.currency !== \"string\") {\n throw new CreemDataFastError(\n \"subscription.paid payload is missing product pricing for fallback mapping.\"\n );\n }\n }\n\n return withOptionalFields(\n {\n amount: transaction\n ? minorToMajor(transaction.amount, transaction.currency)\n : minorToMajor(product!.price!, product!.currency!),\n currency: transaction?.currency ?? product!.currency!,\n transaction_id: transactionId,\n renewal: true\n },\n {\n customer_id: resolveCustomerId(customerValue),\n datafast_visitor_id: readMetadataValue(metadata, \"datafast_visitor_id\"),\n email: customer?.email,\n name: customer?.name,\n timestamp:\n transaction?.timestamp ??\n event.object?.last_transaction_date ??\n event.object?.lastTransactionDate\n }\n );\n}\n\nexport function mapRefundCreatedToPayment(event: RefundCreatedEvent): DataFastPaymentPayload {\n const refundId = event.object?.id;\n const refundAmount = getRefundAmount(event);\n const refundCurrency = getRefundCurrency(event);\n const customerValue = event.object?.customer ?? event.object?.transaction?.customer;\n const customer = toCustomer(customerValue);\n\n if (typeof refundId !== \"string\" || refundId.length === 0) {\n throw new CreemDataFastError(\"refund.created payload is missing refund id.\");\n }\n\n if (typeof refundAmount !== \"number\" || typeof refundCurrency !== \"string\") {\n throw new CreemDataFastError(\"refund.created payload is missing refund amount or currency.\");\n }\n\n return withOptionalFields(\n {\n amount: minorToMajor(refundAmount, refundCurrency),\n currency: refundCurrency,\n refunded: true,\n renewal: isRefundRenewal(event),\n transaction_id: refundId\n },\n {\n customer_id: resolveCustomerId(customerValue),\n datafast_visitor_id: getRefundVisitorId(event),\n email: customer?.email,\n name: customer?.name,\n timestamp: getRefundTimestamp(event)\n }\n );\n}\n","import { TransactionHydrationError } from \"./errors.js\";\nimport type { InternalCreemClient, NormalizedTransaction } from \"./types.js\";\n\ninterface RawTransaction {\n id?: string;\n amount?: number;\n currency?: string;\n createdAt?: number;\n created_at?: number;\n}\n\nfunction toIsoTimestamp(input?: number): string | undefined {\n if (typeof input !== \"number\" || Number.isNaN(input)) {\n return undefined;\n }\n\n const ms = input > 1e12 ? input : input * 1000;\n return new Date(ms).toISOString();\n}\n\nexport async function hydrateTransaction(\n creem: InternalCreemClient,\n transactionId: string\n): Promise<NormalizedTransaction> {\n try {\n const rawTransaction = (await creem.getTransactionById(transactionId)) as RawTransaction;\n if (\n !rawTransaction ||\n typeof rawTransaction.id !== \"string\" ||\n typeof rawTransaction.amount !== \"number\" ||\n typeof rawTransaction.currency !== \"string\"\n ) {\n throw new TransactionHydrationError(\"Creem transaction response is missing required fields.\");\n }\n\n return {\n amount: rawTransaction.amount,\n currency: rawTransaction.currency,\n id: rawTransaction.id,\n timestamp: toIsoTimestamp(rawTransaction.createdAt ?? rawTransaction.created_at)\n };\n } catch (error) {\n if (error instanceof TransactionHydrationError) {\n throw error;\n }\n\n throw new TransactionHydrationError(`Failed to hydrate transaction ${transactionId}.`, {\n cause: error\n });\n }\n}\n","import { InvalidCreemSignatureError, UnsupportedWebhookEventError } from \"./errors.js\";\nimport { claimEvent, completeEvent, releaseEvent, resolveIdempotencyStore } from \"./idempotency.js\";\nimport {\n mapCheckoutCompletedToPayment,\n mapRefundCreatedToPayment,\n mapSubscriptionPaidToPayment\n} from \"./mapper.js\";\nimport { extractHeader, verifyCreemSignature } from \"./signature.js\";\nimport { hydrateTransaction } from \"./transaction.js\";\nimport type {\n CheckoutCompletedEvent,\n DataFastPaymentPayload,\n HandleWebhookParams,\n HandleWebhookResult,\n RefundCreatedEvent,\n SubscriptionPaidEvent,\n SupportedWebhookEvent,\n WebhookHandlerDependencies\n} from \"./types.js\";\n\nfunction getEventType(payload: Record<string, unknown>): string | undefined {\n const eventType = payload.eventType ?? payload.event_type;\n return typeof eventType === \"string\" ? eventType : undefined;\n}\n\nfunction getEventId(payload: Record<string, unknown>): string | undefined {\n return typeof payload.id === \"string\" ? payload.id : undefined;\n}\n\nfunction isSupportedWebhookEvent(eventType: string): eventType is SupportedWebhookEvent {\n return (\n eventType === \"checkout.completed\" ||\n eventType === \"subscription.paid\" ||\n eventType === \"refund.created\"\n );\n}\n\nfunction parseWebhookPayload(rawBody: string): Record<string, unknown> {\n return JSON.parse(rawBody) as Record<string, unknown>;\n}\n\nfunction isInitialSubscriptionCheckout(payload: CheckoutCompletedEvent): boolean {\n const orderType = payload.object?.order?.type;\n const subscription = payload.object?.subscription;\n\n return (\n orderType === \"recurring\" ||\n typeof subscription === \"string\" ||\n (typeof subscription === \"object\" && subscription !== null)\n );\n}\n\nexport async function handleWebhook(\n params: HandleWebhookParams,\n dependencies: WebhookHandlerDependencies\n): Promise<HandleWebhookResult> {\n const signature = extractHeader(params.headers, \"creem-signature\");\n if (!signature) {\n throw new InvalidCreemSignatureError(\"Missing creem-signature header.\");\n }\n\n if (!(await verifyCreemSignature(params.rawBody, dependencies.creemWebhookSecret, signature))) {\n throw new InvalidCreemSignatureError(\"Invalid Creem webhook signature.\");\n }\n\n const payload = parseWebhookPayload(params.rawBody);\n const eventType = getEventType(payload);\n const eventId = getEventId(payload);\n\n if (!eventType) {\n throw new UnsupportedWebhookEventError(\"Webhook payload is missing eventType.\");\n }\n\n if (!isSupportedWebhookEvent(eventType)) {\n return {\n ok: true,\n ignored: true,\n eventId,\n eventType,\n reason: \"unsupported_event\"\n };\n }\n\n if (!eventId) {\n throw new UnsupportedWebhookEventError(\"Supported webhook payload is missing id.\");\n }\n\n const idempotencyStore = resolveIdempotencyStore(dependencies.idempotencyStore);\n const canProcess = await claimEvent(\n eventId,\n idempotencyStore,\n dependencies.idempotencyInFlightTtlSeconds\n );\n if (!canProcess) {\n return {\n ok: true,\n ignored: true,\n eventId,\n eventType,\n reason: \"duplicate_event\"\n };\n }\n\n if (\n eventType === \"checkout.completed\" &&\n isInitialSubscriptionCheckout(payload as CheckoutCompletedEvent)\n ) {\n await completeEvent(eventId, idempotencyStore, dependencies.idempotencyProcessedTtlSeconds);\n return {\n ok: true,\n ignored: true,\n eventId,\n eventType,\n reason: \"delegated_to_subscription_paid\"\n };\n }\n\n let normalizedPayload: DataFastPaymentPayload;\n let datafastResponse: unknown;\n\n try {\n normalizedPayload =\n eventType === \"checkout.completed\"\n ? mapCheckoutCompletedToPayment(payload as CheckoutCompletedEvent)\n : eventType === \"refund.created\"\n ? mapRefundCreatedToPayment(payload as RefundCreatedEvent)\n : await (async () => {\n const subscriptionPayload = payload as SubscriptionPaidEvent;\n const lastTransactionId =\n subscriptionPayload.object?.last_transaction_id ??\n subscriptionPayload.object?.lastTransactionId;\n\n if (dependencies.hydrateTransactionOnSubscriptionPaid && lastTransactionId) {\n try {\n const transaction = await hydrateTransaction(\n dependencies.creem,\n lastTransactionId\n );\n return mapSubscriptionPaidToPayment(subscriptionPayload, transaction);\n } catch (error) {\n dependencies.logger.warn(\n \"Falling back to subscription product pricing after transaction hydration failure.\",\n {\n error,\n lastTransactionId\n }\n );\n }\n }\n\n return mapSubscriptionPaidToPayment(subscriptionPayload);\n })();\n\n datafastResponse = await dependencies.datafast.sendPayment(normalizedPayload);\n } catch (error) {\n await releaseEvent(eventId, idempotencyStore);\n throw error;\n }\n\n await completeEvent(eventId, idempotencyStore, dependencies.idempotencyProcessedTtlSeconds);\n\n return {\n ok: true,\n ignored: false,\n eventId,\n eventType,\n deduplicated: false,\n payload: normalizedPayload,\n datafastResponse\n };\n}\n","import { createCheckout } from \"./core/checkout.js\";\nimport { createCreemClient } from \"./core/creem-client.js\";\nimport { createDataFastClient } from \"./core/datafast-client.js\";\nimport { InvalidCreemSignatureError } from \"./core/errors.js\";\nimport { MemoryIdempotencyStore } from \"./core/idempotency.js\";\nimport { resolveLogger } from \"./core/logger.js\";\nimport { extractHeader, verifyCreemSignature } from \"./core/signature.js\";\nimport { handleWebhook } from \"./core/webhook.js\";\nimport type { CreemDataFastClient, CreemDataFastOptions, HeadersLike } from \"./core/types.js\";\n\nexport type {\n CheckoutCustomerInput,\n CheckoutCustomFieldInput,\n CheckoutMetadata,\n CreateCheckoutContext,\n CreateCheckoutParams,\n CreateCheckoutResult,\n CreemCheckoutCreateRequest,\n CreemCheckoutResponse,\n CreemDataFastClient,\n CreemDataFastOptions,\n CreemSdkClientLike,\n DataFastPaymentPayload,\n DataFastTracking,\n HandleWebhookParams,\n HandleWebhookResult,\n HeadersLike,\n IgnoredWebhookResult,\n IdempotencyStore,\n LoggerLike,\n MetadataValue,\n ProcessedWebhookResult,\n RequestLike,\n RetryConfig,\n SupportedWebhookEvent\n} from \"./core/types.js\";\n\nexport {\n CreemDataFastError,\n DataFastRequestError,\n InvalidCreemSignatureError,\n MissingTrackingError\n} from \"./core/errors.js\";\n\nexport { MemoryIdempotencyStore } from \"./core/idempotency.js\";\n\nexport function createCreemDataFast(options: CreemDataFastOptions): CreemDataFastClient {\n const logger = resolveLogger(options.logger);\n const creem = createCreemClient(options);\n const datafast = createDataFastClient(options);\n const captureSessionId = options.captureSessionId ?? true;\n const strictTracking = options.strictTracking ?? false;\n const hydrateTransactionOnSubscriptionPaid = options.hydrateTransactionOnSubscriptionPaid ?? true;\n const idempotencyStore = options.idempotencyStore ?? new MemoryIdempotencyStore();\n const idempotencyInFlightTtlSeconds = options.idempotencyInFlightTtlSeconds ?? 300;\n const idempotencyProcessedTtlSeconds = options.idempotencyProcessedTtlSeconds ?? 86400;\n\n return {\n createCheckout(params, context) {\n return createCheckout(params, context, {\n creem,\n captureSessionId,\n strictTracking,\n logger\n });\n },\n handleWebhook(params) {\n return handleWebhook(params, {\n creemWebhookSecret: options.creemWebhookSecret,\n datafast,\n creem,\n idempotencyStore,\n idempotencyInFlightTtlSeconds,\n idempotencyProcessedTtlSeconds,\n hydrateTransactionOnSubscriptionPaid,\n logger\n });\n },\n /**\n * Returns `true` for a valid signature and `false` for an invalid one.\n * Throws `InvalidCreemSignatureError` when the `creem-signature` header is missing.\n */\n async verifyWebhookSignature(rawBody: string, headers: HeadersLike): Promise<boolean> {\n const signature = extractHeader(headers, \"creem-signature\");\n if (!signature) {\n throw new InvalidCreemSignatureError(\"Missing creem-signature header.\");\n }\n\n return verifyCreemSignature(rawBody, options.creemWebhookSecret, signature);\n }\n };\n}\n"],"mappings":"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","names":["toIsoTimestamp"]}
package/dist/next.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { C as CreemDataFastClient, N as NextWebhookHandlerOptions, n as HandleWebhookResult } from './types-4FOgdKj_.js';
2
+
3
+ /**
4
+ * Runs the core webhook flow from a standard Web `Request`.
5
+ *
6
+ * Prefer this when you want custom Next.js response logic but do not want to
7
+ * manually read `request.text()` and pass the raw body through yourself.
8
+ *
9
+ * Note: this consumes the request body stream.
10
+ */
11
+ declare function handleWebhookRequest(client: CreemDataFastClient, request: Request): Promise<HandleWebhookResult>;
12
+ /**
13
+ * Returns a ready-to-export Next.js route handler for the default webhook path.
14
+ *
15
+ * Prefer this for the minimal integration. Use `handleWebhookRequest()` instead
16
+ * when you need to branch on the webhook result or craft your own response.
17
+ */
18
+ declare function createNextWebhookHandler(client: CreemDataFastClient, options?: NextWebhookHandlerOptions): (request: Request) => Promise<Response>;
19
+
20
+ export { NextWebhookHandlerOptions, createNextWebhookHandler, handleWebhookRequest };
package/dist/next.js ADDED
@@ -0,0 +1,31 @@
1
+ import {
2
+ InvalidCreemSignatureError
3
+ } from "./chunk-EPUWLMCL.js";
4
+
5
+ // src/adapters/next.ts
6
+ async function handleWebhookRequest(client, request) {
7
+ const rawBody = await request.text();
8
+ return client.handleWebhook({
9
+ rawBody,
10
+ headers: request.headers
11
+ });
12
+ }
13
+ function createNextWebhookHandler(client, options = {}) {
14
+ return async (request) => {
15
+ try {
16
+ await handleWebhookRequest(client, request);
17
+ return new Response("OK", { status: 200 });
18
+ } catch (error) {
19
+ await options.onError?.(error);
20
+ if (error instanceof InvalidCreemSignatureError) {
21
+ return new Response("Invalid signature", { status: 400 });
22
+ }
23
+ return new Response("Internal error", { status: 500 });
24
+ }
25
+ };
26
+ }
27
+ export {
28
+ createNextWebhookHandler,
29
+ handleWebhookRequest
30
+ };
31
+ //# sourceMappingURL=next.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/next.ts"],"sourcesContent":["import { InvalidCreemSignatureError } from \"../core/errors.js\";\nimport type {\n CreemDataFastClient,\n HandleWebhookResult,\n NextWebhookHandlerOptions\n} from \"../core/types.js\";\n\n/**\n * Runs the core webhook flow from a standard Web `Request`.\n *\n * Prefer this when you want custom Next.js response logic but do not want to\n * manually read `request.text()` and pass the raw body through yourself.\n *\n * Note: this consumes the request body stream.\n */\nexport async function handleWebhookRequest(\n client: CreemDataFastClient,\n request: Request\n): Promise<HandleWebhookResult> {\n const rawBody = await request.text();\n return client.handleWebhook({\n rawBody,\n headers: request.headers\n });\n}\n\n/**\n * Returns a ready-to-export Next.js route handler for the default webhook path.\n *\n * Prefer this for the minimal integration. Use `handleWebhookRequest()` instead\n * when you need to branch on the webhook result or craft your own response.\n */\nexport function createNextWebhookHandler(\n client: CreemDataFastClient,\n options: NextWebhookHandlerOptions = {}\n): (request: Request) => Promise<Response> {\n return async (request: Request) => {\n try {\n await handleWebhookRequest(client, request);\n\n return new Response(\"OK\", { status: 200 });\n } catch (error) {\n await options.onError?.(error);\n\n if (error instanceof InvalidCreemSignatureError) {\n return new Response(\"Invalid signature\", { status: 400 });\n }\n\n return new Response(\"Internal error\", { status: 500 });\n }\n };\n}\n"],"mappings":";;;;;AAeA,eAAsB,qBACpB,QACA,SAC8B;AAC9B,QAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,SAAO,OAAO,cAAc;AAAA,IAC1B;AAAA,IACA,SAAS,QAAQ;AAAA,EACnB,CAAC;AACH;AAQO,SAAS,yBACd,QACA,UAAqC,CAAC,GACG;AACzC,SAAO,OAAO,YAAqB;AACjC,QAAI;AACF,YAAM,qBAAqB,QAAQ,OAAO;AAE1C,aAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C,SAAS,OAAO;AACd,YAAM,QAAQ,UAAU,KAAK;AAE7B,UAAI,iBAAiB,4BAA4B;AAC/C,eAAO,IAAI,SAAS,qBAAqB,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC1D;AAEA,aAAO,IAAI,SAAS,kBAAkB,EAAE,QAAQ,IAAI,CAAC;AAAA,IACvD;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,215 @@
1
+ type MetadataValue = string | number | boolean | null;
2
+ type CheckoutMetadata = Record<string, MetadataValue>;
3
+ interface DataFastTracking {
4
+ visitorId?: string;
5
+ sessionId?: string;
6
+ }
7
+ interface LoggerLike {
8
+ debug(message: string, meta?: unknown): void;
9
+ info(message: string, meta?: unknown): void;
10
+ warn(message: string, meta?: unknown): void;
11
+ error(message: string, meta?: unknown): void;
12
+ }
13
+ interface IdempotencyStore {
14
+ claim(key: string, ttlSeconds?: number): Promise<boolean>;
15
+ complete(key: string, ttlSeconds?: number): Promise<void>;
16
+ release(key: string): Promise<void>;
17
+ }
18
+ type HeadersLike = Headers | Record<string, string | string[] | undefined>;
19
+ interface RequestLike {
20
+ headers: HeadersLike;
21
+ url?: string;
22
+ }
23
+ interface CheckoutCustomerInput {
24
+ id?: string;
25
+ email?: string;
26
+ }
27
+ interface CheckoutCustomFieldInput {
28
+ type: "text" | "checkbox";
29
+ key: string;
30
+ label: string;
31
+ optional?: boolean;
32
+ text?: {
33
+ maxLength?: number;
34
+ minLength?: number;
35
+ };
36
+ checkbox?: {
37
+ label: string;
38
+ };
39
+ }
40
+ interface CreateCheckoutParams {
41
+ productId: string;
42
+ successUrl: string;
43
+ requestId?: string;
44
+ units?: number;
45
+ discountCode?: string;
46
+ customer?: CheckoutCustomerInput;
47
+ customFields?: CheckoutCustomFieldInput[];
48
+ metadata?: CheckoutMetadata;
49
+ tracking?: DataFastTracking;
50
+ }
51
+ interface CreateCheckoutContext {
52
+ request?: RequestLike;
53
+ cookieHeader?: string;
54
+ strictTracking?: boolean;
55
+ }
56
+ interface CreateCheckoutResult {
57
+ checkoutId: string;
58
+ checkoutUrl: string;
59
+ injectedTracking: DataFastTracking;
60
+ finalMetadata: CheckoutMetadata;
61
+ raw: unknown;
62
+ }
63
+ /**
64
+ * Raw webhook input for Creem signature verification and processing.
65
+ *
66
+ * `rawBody` must be the exact unparsed request body string that Creem sent.
67
+ * Passing JSON that was parsed and re-serialized can break signature checks.
68
+ */
69
+ interface HandleWebhookParams {
70
+ rawBody: string;
71
+ headers: HeadersLike;
72
+ }
73
+ type SupportedWebhookEvent = "checkout.completed" | "subscription.paid" | "refund.created";
74
+ /**
75
+ * DataFast payment payload fields currently documented by the receiver API.
76
+ *
77
+ * `datafast_session_id` is captured during checkout and preserved in Creem
78
+ * metadata, but it is intentionally not forwarded here until DataFast documents
79
+ * support for it on `POST /api/v1/payments`.
80
+ */
81
+ interface DataFastPaymentPayload {
82
+ amount: number;
83
+ currency: string;
84
+ transaction_id: string;
85
+ datafast_visitor_id?: string;
86
+ email?: string;
87
+ name?: string;
88
+ customer_id?: string;
89
+ renewal?: boolean;
90
+ refunded?: boolean;
91
+ timestamp?: string;
92
+ }
93
+ interface RetryConfig {
94
+ retries?: number;
95
+ baseDelayMs?: number;
96
+ maxDelayMs?: number;
97
+ }
98
+ /**
99
+ * Minimal request shape that the injected Creem SDK checkout client must accept.
100
+ */
101
+ interface CreemCheckoutCreateRequest {
102
+ productId: string;
103
+ successUrl: string;
104
+ requestId?: string;
105
+ units?: number;
106
+ discountCode?: string;
107
+ customer?: CheckoutCustomerInput;
108
+ customFields?: CheckoutCustomFieldInput[];
109
+ metadata?: CheckoutMetadata;
110
+ }
111
+ /**
112
+ * Minimal response shape that `createCheckout()` must return for this package to work.
113
+ */
114
+ interface CreemCheckoutResponse {
115
+ id?: string;
116
+ checkoutUrl?: string;
117
+ checkout_url?: string;
118
+ [key: string]: unknown;
119
+ }
120
+ /**
121
+ * Minimal public contract for a custom Creem SDK instance injected through `creemClient`.
122
+ */
123
+ interface CreemSdkClientLike {
124
+ checkouts: {
125
+ create(request: CreemCheckoutCreateRequest): Promise<CreemCheckoutResponse>;
126
+ };
127
+ transactions: {
128
+ getById(transactionId: string): Promise<unknown>;
129
+ };
130
+ }
131
+ /**
132
+ * Successful webhook processing result for supported events that were forwarded
133
+ * to DataFast.
134
+ */
135
+ type ProcessedWebhookResult = {
136
+ ok: true;
137
+ ignored: false;
138
+ eventId: string;
139
+ eventType: SupportedWebhookEvent;
140
+ deduplicated: boolean;
141
+ payload: DataFastPaymentPayload;
142
+ datafastResponse: unknown;
143
+ };
144
+ /**
145
+ * Successful webhook result for deliveries that were intentionally ignored,
146
+ * such as unsupported event types or duplicates.
147
+ */
148
+ type IgnoredWebhookResult = {
149
+ ok: true;
150
+ ignored: true;
151
+ eventId?: string;
152
+ eventType?: string;
153
+ reason: "unsupported_event" | "duplicate_event" | "delegated_to_subscription_paid";
154
+ };
155
+ /**
156
+ * Discriminated result from webhook handling.
157
+ *
158
+ * Branch on `ignored` to distinguish forwarded payments from intentionally
159
+ * ignored deliveries.
160
+ */
161
+ type HandleWebhookResult = ProcessedWebhookResult | IgnoredWebhookResult;
162
+ interface CreemDataFastOptions {
163
+ creemApiKey?: string;
164
+ creemClient?: CreemSdkClientLike;
165
+ creemWebhookSecret: string;
166
+ datafastApiKey: string;
167
+ testMode?: boolean;
168
+ captureSessionId?: boolean;
169
+ hydrateTransactionOnSubscriptionPaid?: boolean;
170
+ strictTracking?: boolean;
171
+ idempotencyInFlightTtlSeconds?: number;
172
+ idempotencyProcessedTtlSeconds?: number;
173
+ logger?: LoggerLike;
174
+ idempotencyStore?: IdempotencyStore;
175
+ timeoutMs?: number;
176
+ retry?: RetryConfig;
177
+ fetch?: typeof globalThis.fetch;
178
+ }
179
+ interface CreemDataFastClient {
180
+ createCheckout(params: CreateCheckoutParams, context?: CreateCheckoutContext): Promise<CreateCheckoutResult>;
181
+ /**
182
+ * Verifies, deduplicates, normalizes, and forwards a raw Creem webhook payload.
183
+ *
184
+ * Use this when you already have the exact raw request body and headers from your framework.
185
+ */
186
+ handleWebhook(params: HandleWebhookParams): Promise<HandleWebhookResult>;
187
+ /**
188
+ * Validates the `creem-signature` header against the exact raw webhook body.
189
+ *
190
+ * This is useful when you need signature checks separately from full webhook processing.
191
+ * Returns `true` for a valid signature and `false` for an invalid one.
192
+ * Throws `InvalidCreemSignatureError` when the `creem-signature` header is missing.
193
+ */
194
+ verifyWebhookSignature(rawBody: string, headers: HeadersLike): Promise<boolean>;
195
+ }
196
+ interface NextWebhookHandlerOptions {
197
+ onError?: (error: unknown) => void | Promise<void>;
198
+ }
199
+ interface ExpressLikeRequest {
200
+ headers: Record<string, string | string[] | undefined>;
201
+ body: Buffer | string;
202
+ }
203
+ interface ExpressLikeResponse {
204
+ status(code: number): ExpressLikeResponse;
205
+ send(body?: unknown): void;
206
+ }
207
+ interface ExpressWebhookHandlerOptions {
208
+ onError?: (error: unknown) => void | Promise<void>;
209
+ }
210
+ interface BrowserTrackingResult {
211
+ visitorId?: string;
212
+ sessionId?: string;
213
+ }
214
+
215
+ export type { BrowserTrackingResult as B, CreemDataFastClient as C, DataFastPaymentPayload as D, ExpressWebhookHandlerOptions as E, HandleWebhookParams as H, IdempotencyStore as I, LoggerLike as L, MetadataValue as M, NextWebhookHandlerOptions as N, ProcessedWebhookResult as P, RequestLike as R, SupportedWebhookEvent as S, ExpressLikeRequest as a, ExpressLikeResponse as b, CreemDataFastOptions as c, CheckoutCustomFieldInput as d, CheckoutCustomerInput as e, CheckoutMetadata as f, CreateCheckoutContext as g, CreateCheckoutParams as h, CreateCheckoutResult as i, CreemCheckoutCreateRequest as j, CreemCheckoutResponse as k, CreemSdkClientLike as l, DataFastTracking as m, HandleWebhookResult as n, HeadersLike as o, IgnoredWebhookResult as p, RetryConfig as q };
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "creem-datafast",
3
+ "version": "0.1.0",
4
+ "description": "Wraps the official Creem SDK to capture DataFast visitor attribution at checkout and forward verified payment and refund webhooks — with built-in Next.js and Express adapters.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ },
15
+ "./idempotency/upstash": {
16
+ "types": "./dist/idempotency/upstash.d.ts",
17
+ "import": "./dist/idempotency/upstash.js"
18
+ },
19
+ "./next": {
20
+ "types": "./dist/next.d.ts",
21
+ "import": "./dist/next.js"
22
+ },
23
+ "./express": {
24
+ "types": "./dist/express.d.ts",
25
+ "import": "./dist/express.js"
26
+ },
27
+ "./client": {
28
+ "types": "./dist/client.d.ts",
29
+ "import": "./dist/client.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "sideEffects": false,
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/santigamo/creem-datafast.git"
41
+ },
42
+ "homepage": "https://github.com/santigamo/creem-datafast#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/santigamo/creem-datafast/issues"
45
+ },
46
+ "author": "Santiago Garcia",
47
+ "keywords": [
48
+ "creem",
49
+ "datafast",
50
+ "payments",
51
+ "webhooks",
52
+ "checkout",
53
+ "attribution",
54
+ "nextjs",
55
+ "express",
56
+ "analytics",
57
+ "subscriptions",
58
+ "refunds"
59
+ ],
60
+ "engines": {
61
+ "node": ">=18"
62
+ },
63
+ "scripts": {
64
+ "build": "tsup",
65
+ "format": "biome format --write src tests scripts example-next example-express package.json tsconfig.json tsup.config.ts vitest.config.ts biome.json",
66
+ "format:check": "biome check --javascript-linter-enabled=false --json-linter-enabled=false --css-linter-enabled=false --graphql-linter-enabled=false --grit-linter-enabled=false --html-linter-enabled=false --assist-enabled=false src tests scripts example-next example-express package.json tsconfig.json tsup.config.ts vitest.config.ts biome.json",
67
+ "lint": "biome lint src tests scripts example-next example-express package.json tsconfig.json tsup.config.ts vitest.config.ts biome.json",
68
+ "pack:local": "pnpm build && pnpm pack --pack-destination .artifacts",
69
+ "smoke:bun": "node scripts/smoke-bun-consumer.mjs",
70
+ "smoke:cloudflare-worker": "node scripts/smoke-cloudflare-worker.mjs",
71
+ "smoke:consumer": "node scripts/smoke-consumer.mjs",
72
+ "test:coverage": "vitest run --coverage",
73
+ "typecheck": "tsc --noEmit",
74
+ "test": "vitest run"
75
+ },
76
+ "dependencies": {
77
+ "creem": "^1.4.2"
78
+ },
79
+ "peerDependencies": {
80
+ "@upstash/redis": "^1.37.0"
81
+ },
82
+ "peerDependenciesMeta": {
83
+ "@upstash/redis": {
84
+ "optional": true
85
+ }
86
+ },
87
+ "devDependencies": {
88
+ "@biomejs/biome": "^2.4.6",
89
+ "@types/node": "^25.5.0",
90
+ "@upstash/redis": "^1.37.0",
91
+ "@vitest/coverage-v8": "^3.2.4",
92
+ "esbuild": "^0.27.4",
93
+ "miniflare": "^4.20260312.0",
94
+ "tsup": "^8.5.1",
95
+ "typescript": "^5.9.3",
96
+ "vitest": "^3.2.4"
97
+ },
98
+ "packageManager": "pnpm@10.6.5"
99
+ }