UNPKG

@paykit-sdk/core

Version:

The Payment Toolkit for Typescript

611 lines (590 loc) 18.6 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { AbortedError: () => AbortedError, ConnectionError: () => ConnectionError, ERR: () => ERR, HTTPClient: () => HTTPClient, HTTPError: () => HTTPError, Logger: () => Logger, OK: () => OK, PayKit: () => PayKit, TimeoutError: () => TimeoutError, UnauthorizedError: () => UnauthorizedError, UnknownError: () => UnknownError, ValidationError: () => ValidationError, Webhook: () => Webhook, __IS_CLIENT__: () => __IS_CLIENT__, billingModeSchema: () => billingModeSchema, createCheckoutSchema: () => createCheckoutSchema, createCustomerSchema: () => createCustomerSchema, delay: () => delay, headersExtractor: () => headersExtractor, isAbortError: () => isAbortError, isConnectionError: () => isConnectionError, isTimeoutError: () => isTimeoutError, isUnauthorizedError: () => isUnauthorizedError, logger: () => logger, metadataSchema: () => metadataSchema, retrieveCheckoutSchema: () => retrieveCheckoutSchema, retrieveCustomerSchema: () => retrieveCustomerSchema, retrieveSubscriptionSchema: () => retrieveSubscriptionSchema, safeDecode: () => safeDecode, safeEncode: () => safeEncode, safeParse: () => safeParse, stringifyObjectValues: () => stringifyObjectValues, toPaykitEvent: () => toPaykitEvent, truncate: () => truncate, tryCatchAsync: () => tryCatchAsync, tryCatchSync: () => tryCatchSync, unwrapAsync: () => unwrapAsync, updateCustomerSchema: () => updateCustomerSchema, updateSubscriptionSchema: () => updateSubscriptionSchema, validateEnvVars: () => validateEnvVars }); module.exports = __toCommonJS(index_exports); // src/webhook-provider.ts var Webhook = class { handlers = /* @__PURE__ */ new Map(); config = null; setup(config) { this.config = config; return this; } on(eventType, handler) { if (!this.config) throw new Error("Webhook not configured. Call setup() first."); if (!this.handlers.has(eventType)) this.handlers.set(eventType, []); this.handlers.get(eventType)?.push(handler); return this; } async handle(dto) { if (!this.config) throw new Error("Webhook not configured. Call setup() first."); const { webhookSecret, provider } = this.config; const event = await provider.handleWebhook({ ...dto, webhookSecret }); const handlers = this.handlers.get(event.type); if (handlers && handlers.length > 0) { await Promise.all(handlers.map((handler) => handler(event))); } } }; // src/resources/checkout.ts var import_zod2 = require("zod"); // src/resources/metadata.ts var import_zod = require("zod"); var metadataSchema = import_zod.z.record(import_zod.z.string(), import_zod.z.string()); // src/resources/checkout.ts var billingModeSchema = import_zod2.z.enum(["one_time", "recurring"]); var createCheckoutSchema = import_zod2.z.object({ /** * The ID of the customer. */ customer_id: import_zod2.z.string(), /** * The metadata of the checkout. */ metadata: metadataSchema, /** * The mode of the checkout. */ session_type: billingModeSchema, /** * The item ID of the checkout. */ item_id: import_zod2.z.string(), /** * Extra information to be sent to the provider e.g tax, trial days, etc. */ provider_metadata: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional() }); var retrieveCheckoutSchema = import_zod2.z.object({ id: import_zod2.z.string() }); // src/resources/customer.ts var import_zod3 = require("zod"); var createCustomerSchema = import_zod3.z.object({ /** * The email of the customer. */ email: import_zod3.z.string().email(), /** * The name of the customer. */ name: import_zod3.z.string().optional(), /** * The metadata of the customer. */ metadata: metadataSchema.optional() }); var updateCustomerSchema = import_zod3.z.object({ /** * The email of the customer. */ email: import_zod3.z.string().email().optional(), /** * The name of the customer. */ name: import_zod3.z.string().optional(), /** * The metadata of the customer. */ metadata: metadataSchema.optional() }); var retrieveCustomerSchema = import_zod3.z.object({ id: import_zod3.z.string() }); // src/resources/subscription.ts var import_zod4 = require("zod"); var updateSubscriptionSchema = import_zod4.z.object({ metadata: metadataSchema.optional() }); var retrieveSubscriptionSchema = import_zod4.z.object({ id: import_zod4.z.string() }); // src/resources/webhook.ts var toPaykitEvent = (event) => event; // src/tools/error.ts var HTTPError = class extends Error { cause; name = "HTTPError"; constructor(message, opts) { let msg = message; if (opts?.cause) { msg += `: ${opts.cause}`; } super(msg, opts); if (typeof this.cause === "undefined") { this.cause = opts?.cause; } } }; var UnauthorizedError = class extends HTTPError { name = "PaykitUnauthorizedError"; }; var ConnectionError = class extends HTTPError { name = "PaykitConnectionError"; }; var AbortedError = class extends HTTPError { name = "PaykitAbortedError"; }; var TimeoutError = class extends HTTPError { name = "PaykitTimeoutError"; }; var ValidationError = class extends HTTPError { name = "PaykitValidationError"; }; var UnknownError = class extends HTTPError { name = "PaykitUnknownError"; }; // src/tools/try-catch.ts async function tryCatchAsync(promise) { try { const data = await promise; return [data, void 0]; } catch (error) { return [void 0, error]; } } function tryCatchSync(fn) { try { const data = fn(); return [data, void 0]; } catch (error) { return [void 0, error]; } } // src/tools/fp.ts var OK = (value) => ({ ok: true, value }); var ERR = (error) => ({ ok: false, error }); var unwrapAsync = async (pr) => { const r = await pr; if (!r.ok) throw r.error; return r.value; }; function safeParse(rawValue, fn, errorMessage) { const [result, error] = tryCatchSync(() => fn(rawValue)); if (error) return ERR(new ValidationError(errorMessage, { cause: error })); return OK(result); } var safeEncode = (value) => { return safeParse(value, (value2) => Buffer.from(JSON.stringify(value2)).toString("base64"), "Failed to encode value"); }; var safeDecode = (value) => { return safeParse(value, (value2) => JSON.parse(Buffer.from(value2, "base64").toString("utf-8")), "Failed to decode value"); }; // src/tools/http.ts function isConnectionError(err) { if (typeof err !== "object" || err == null) { return false; } const isBrowserErr = err instanceof TypeError && err.message.toLowerCase().startsWith("failed to fetch"); const isNodeErr = err instanceof TypeError && err.message.toLowerCase().startsWith("fetch failed"); const isBunErr = "name" in err && err.name === "ConnectionError"; const isGenericErr = "code" in err && typeof err.code === "string" && err.code.toLowerCase() === "econnreset"; return isBrowserErr || isNodeErr || isGenericErr || isBunErr; } function isTimeoutError(err) { if (typeof err !== "object" || err == null) { return false; } const isNative = "name" in err && err.name === "TimeoutError"; const isLegacyNative = "code" in err && err.code === 23; const isGenericErr = "code" in err && typeof err.code === "string" && err.code.toLowerCase() === "econnaborted"; return isNative || isLegacyNative || isGenericErr; } function isAbortError(err) { if (typeof err !== "object" || err == null) { return false; } const isNative = "name" in err && err.name === "AbortError"; const isLegacyNative = "code" in err && err.code === 20; const isGenericErr = "code" in err && typeof err.code === "string" && err.code.toLowerCase() === "econnaborted"; return isNative || isLegacyNative || isGenericErr; } function isUnauthorizedError(err) { if (typeof err !== "object" || err == null) { return false; } return err instanceof Error && ["unauthorized", "401"].includes(err.message.toLowerCase()); } // src/tools/webhook.ts var headersExtractor = (headers, requiredHeaders) => { const extractedHeaders = []; for (const key of requiredHeaders) { const value = headers[key] || headers[key.toLowerCase()]; if (value) { extractedHeaders.push({ key, value: Array.isArray(value) ? value : [value] }); } } return extractedHeaders; }; // src/tools/is-client.ts var __IS_CLIENT__ = typeof window !== "undefined"; // src/tools/delay.ts var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); // src/tools/string.ts var truncate = (str, num, suffix = "...") => { if (!str) return ""; if (str.length < num) return str; return str.slice(0, num) + suffix; }; var stringifyObjectValues = (obj) => { return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, JSON.stringify(value)])); }; // src/tools/validate-env.ts var validateEnvVars = (requiredKeys, source) => { const missingKeys = []; const result = {}; for (const key of requiredKeys) { const value = source[key]; if (!value) { missingKeys.push(key); } else { result[key] = value; } } if (missingKeys.length > 0) { const missingKeysList = missingKeys.join(", "); throw new Error(`Missing required environment variables: ${missingKeysList}`); } return result; }; // src/logger.ts var import_chalk = __toESM(require("chalk")); var Logger = class { silent; verbose; spinnerInterval = null; spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]; currentSpinnerFrame = 0; constructor(options = {}) { this.silent = options.silent || false; this.verbose = options.verbose || false; } /** * Success messages (dark green) */ success(message) { if (this.silent) return; console.log(import_chalk.default.hex("#166534")(`\u2714 ${message}`)); } /** * Info messages (dark green) */ info(message) { if (this.silent) return; console.log(import_chalk.default.hex("#15803d")(`\u2139 ${message}`)); } /** * Warning messages (amber) */ warn(message) { if (this.silent) return; console.log(import_chalk.default.hex("#d97706")(`\u26A0 ${message}`)); } /** * Error messages (red) */ error(message) { if (this.silent) return; console.error(import_chalk.default.hex("#dc2626")(`\u2716 ${message}`)); } /** * Debug messages (gray, only in verbose mode) */ debug(message) { if (this.silent || !this.verbose) return; console.log(import_chalk.default.gray(`\u{1F50D} ${message}`)); } /** * Tip messages (dark green) */ tip(message) { if (this.silent) return; console.log(import_chalk.default.hex("#16a34a")(`\u{1F4A1} ${message}`)); } /** * Code block styling */ code(code) { if (this.silent) return; console.log(import_chalk.default.bgGray.white(` ${code} `)); } /** * Section headers */ section(title) { if (this.silent) return; console.log(import_chalk.default.hex("#166534").bold.underline(` ${title}`)); } /** * Progress indicator with spinning animation */ progress(message) { if (this.silent) return; this.stopSpinner(); this.startSpinner(message); } /** * Start the spinner animation */ startSpinner(message) { const updateSpinner = () => { process.stdout.clearLine(0); process.stdout.cursorTo(0); const spinner = this.spinnerFrames[this.currentSpinnerFrame]; process.stdout.write(import_chalk.default.hex("#15803d")(`${spinner} ${message}... `)); this.currentSpinnerFrame = (this.currentSpinnerFrame + 1) % this.spinnerFrames.length; }; updateSpinner(); this.spinnerInterval = setInterval(updateSpinner, 80); } /** * Stop the spinner animation */ stopSpinner() { if (this.spinnerInterval) { clearInterval(this.spinnerInterval); this.spinnerInterval = null; } } /** * Clear progress line */ clearProgress() { if (this.silent) return; this.stopSpinner(); process.stdout.clearLine(0); process.stdout.cursorTo(0); } /** * Table-like output for key-value pairs */ table(data) { if (this.silent) return; const maxKeyLength = Math.max(...Object.keys(data).map((k) => k.length)); Object.entries(data).forEach(([key, value]) => { const paddedKey = key.padEnd(maxKeyLength); console.log(import_chalk.default.gray(`${paddedKey}: `) + import_chalk.default.white(value)); }); } /** * List items */ list(items, bullet = "\u2022") { if (this.silent) return; items.forEach((item) => { console.log(import_chalk.default.hex("#16a34a")(`${bullet} `) + import_chalk.default.white(item)); }); } /** * Divider line */ divider(char = "\u2500", length = 50) { if (this.silent) return; console.log(import_chalk.default.hex("#16a34a")(char.repeat(length))); } /** * Spacer */ spacer(lines = 1) { if (this.silent) return; for (let i = 0; i < lines; i++) { console.log(""); } } /** * Brand header */ brand() { if (this.silent) return; console.log(import_chalk.default.hex("#166534").bold("PayKit CLI")); this.divider(); } }; var logger = new Logger(); // src/http.ts var HTTPClient = class { constructor(config) { this.config = config; } errorHandler = (err) => { switch (true) { case isUnauthorizedError(err): return ERR(new UnauthorizedError("Unauthorized", { cause: err })); case isConnectionError(err): return ERR(new ConnectionError("Connection error", { cause: err })); case isTimeoutError(err): return ERR(new TimeoutError("Timeout error", { cause: err })); case isAbortError(err): return ERR(new AbortedError("Aborted error", { cause: err })); default: return ERR(new UnknownError("Unknown error", { cause: err })); } }; getFullUrl(endpoint) { const cleanEndpoint = endpoint.startsWith("/") ? endpoint.slice(1) : endpoint; return `${this.config.baseUrl}/${cleanEndpoint}`; } getRequestOptions(options) { return { headers: { "Content-Type": "application/json", ...this.config.headers, ...options?.headers }, ...options }; } async get(endpoint, options) { const url = this.getFullUrl(endpoint); const requestOptions = this.getRequestOptions(options); return await fetch(url, { method: "GET", ...requestOptions }).then((res) => OK(res.json())).catch((err) => this.errorHandler(err)); } async post(endpoint, options) { const url = this.getFullUrl(endpoint); const requestOptions = this.getRequestOptions(options); return await fetch(url, { method: "POST", ...requestOptions }).then((res) => OK(res.json())).catch((err) => this.errorHandler(err)); } async delete(endpoint, options) { const url = this.getFullUrl(endpoint); const requestOptions = this.getRequestOptions(options); return await fetch(url, { method: "DELETE", ...requestOptions }).then((res) => OK(res.json())).catch((err) => this.errorHandler(err)); } async put(endpoint, options) { const url = this.getFullUrl(endpoint); const requestOptions = this.getRequestOptions(options); return await fetch(url, { method: "PUT", ...requestOptions }).then((res) => OK(res.json())).catch((err) => this.errorHandler(err)); } async patch(endpoint, options) { const url = this.getFullUrl(endpoint); const requestOptions = this.getRequestOptions(options); return await fetch(url, { method: "PATCH", ...requestOptions }).then((res) => OK(res.json())).catch((err) => this.errorHandler(err)); } }; // src/index.ts var PayKit = class { constructor(provider) { this.provider = provider; } checkouts = { create: (params) => this.provider.createCheckout(params), retrieve: (id) => this.provider.retrieveCheckout(id) }; customers = { create: (params) => this.provider.createCustomer(params), update: (id, params) => this.provider.updateCustomer(id, params), retrieve: (id) => this.provider.retrieveCustomer(id) }; subscriptions = { update: (id, params) => this.provider.updateSubscription(id, params), cancel: (id) => this.provider.cancelSubscription(id) }; webhooks = { setup: (config) => new Webhook().setup({ ...config, provider: this.provider }) }; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AbortedError, ConnectionError, ERR, HTTPClient, HTTPError, Logger, OK, PayKit, TimeoutError, UnauthorizedError, UnknownError, ValidationError, Webhook, __IS_CLIENT__, billingModeSchema, createCheckoutSchema, createCustomerSchema, delay, headersExtractor, isAbortError, isConnectionError, isTimeoutError, isUnauthorizedError, logger, metadataSchema, retrieveCheckoutSchema, retrieveCustomerSchema, retrieveSubscriptionSchema, safeDecode, safeEncode, safeParse, stringifyObjectValues, toPaykitEvent, truncate, tryCatchAsync, tryCatchSync, unwrapAsync, updateCustomerSchema, updateSubscriptionSchema, validateEnvVars });