@paykit-sdk/core
Version:
The Payment Toolkit for Typescript
170 lines (164 loc) • 4.84 kB
JavaScript
import { setTimeout } from 'timers/promises';
import { z } from 'zod';
// src/tools/utils.ts
// src/error.ts
var UnTraceableError = class extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
this.stack = void 0;
}
};
var PAYKIT_METADATA_KEY = "__paykit";
z.record(z.string(), z.string());
// src/tools/try-catch.ts
function tryCatchSync(fn) {
try {
const data = fn();
return [data, void 0];
} catch (error) {
return [void 0, error];
}
}
// src/tools/utils.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(buildError(errorMessage, 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"
);
};
async function executeWithRetryWithHandler(apiCall, errorHandler, maxRetries = 3, baseDelay = 1e3, currentAttempt = 1) {
try {
return await apiCall();
} catch (error) {
const handledError = errorHandler(error, currentAttempt);
if (!handledError.retry) {
if (handledError.data == null) throw error;
return handledError.data;
}
if (handledError.retry && currentAttempt <= maxRetries) {
const delay = baseDelay * Math.pow(2, currentAttempt - 1) * (0.5 + Math.random() * 0.5);
await setTimeout(delay);
return executeWithRetryWithHandler(
apiCall,
errorHandler,
maxRetries,
baseDelay,
currentAttempt + 1
);
}
if (handledError.data == null) throw error;
return handledError.data;
}
}
function buildError(message, cause) {
const error = new Error(message);
if (cause) error.cause = cause;
return error;
}
var validateRequiredKeys = (requiredKeys, source, errorMessage, errorInstance) => {
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(", ");
const error = typeof errorMessage === "function" ? errorMessage(missingKeys) : errorMessage.replace("{keys}", missingKeysList);
throw errorInstance?.(error) ?? new UnTraceableError(error);
}
return result;
};
var parseJSON = (str, schema2) => {
try {
const parsed = JSON.parse(str);
return schema2.parse(parsed);
} catch (error) {
return null;
}
};
var schema = () => {
return (schema2) => schema2;
};
var omitInternalMetadata = (metadata, internalKeys = [PAYKIT_METADATA_KEY]) => {
return Object.entries(metadata).reduce((acc, [key, value]) => {
if (!internalKeys.includes(key)) {
acc[key] = String(value);
}
return acc;
}, {});
};
var stringifyMetadataValues = (metadata) => {
return Object.fromEntries(
Object.entries(metadata).map(([key, value]) => [
key,
typeof value === "string" ? value : JSON.stringify(value)
])
);
};
var getURLFromHeaders = (headers) => {
if (headers["origin"]) return headers["origin"];
if (headers["x-forwarded-host"]) {
const protocol = headers["x-forwarded-proto"] ?? "https";
const host = headers["x-forwarded-host"];
const path = headers["x-forwarded-path"] ?? "";
return `${protocol}://${host}${path}`;
}
if (headers["host"]) {
const protocol = headers["x-forwarded-proto"] ?? "https";
const host = headers["host"];
const path = headers["x-original-url"] ?? headers["x-forwarded-path"] ?? "";
return `${protocol}://${host}${path}`;
}
return "";
};
var refundReasonMatcher = (matcher) => {
if (/duplicate|duplicated|double.*charge|charged.*twice/i.test(
matcher.toLowerCase().trim()
)) {
return "duplicate";
}
if (/fraud|fraudulent|unauthorized|scam|stolen.*card|not.*authorized/i.test(
matcher.toLowerCase().trim()
)) {
return "fraudulent";
}
if (/customer.*request|requested.*customer|customer.*want|cancel|refund.*request|changed.*mind/i.test(
matcher.toLowerCase().trim()
)) {
return "requested_by_customer";
}
return "other";
};
export { ERR, OK, buildError, executeWithRetryWithHandler, getURLFromHeaders, omitInternalMetadata, parseJSON, refundReasonMatcher, safeDecode, safeEncode, safeParse, schema, stringifyMetadataValues, unwrapAsync, validateRequiredKeys };