@paykit-sdk/core
Version:
The Payment Toolkit for Typescript
123 lines (120 loc) • 3.45 kB
JavaScript
// src/error.ts
var PayKitError = class extends Error {
code;
statusCode;
provider;
method;
context;
constructor(message, options) {
super(message);
this.name = this.constructor.name;
this.code = options?.code || "PAYKIT_ERROR";
this.statusCode = options?.statusCode || 500;
this.provider = options?.provider;
this.method = options?.method;
this.context = options?.context;
if (options?.cause) {
this.cause = options.cause;
}
Error.captureStackTrace(this, this.constructor);
}
};
var OperationFailedError = class extends PayKitError {
constructor(operation, provider, options) {
const message = `Failed to ${operation}${provider ? ` with ${provider}` : ""}${options?.reason ? `: ${options.reason}` : ""}`;
super(message, {
code: "OPERATION_FAILED",
statusCode: 500,
provider,
method: operation,
cause: options?.cause
});
this.stack = void 0;
}
};
// src/oauth2-token-manager.ts
var OAuth2TokenManager = class {
_accessToken = null;
_expiresAt = 0;
// epoch ms
_refreshPromise = null;
config;
constructor(config) {
this.config = {
...config,
method: config.method ?? "POST",
expiryBuffer: config.expiryBuffer ?? 300
};
}
/**
* Get a valid access token, refreshing if necessary
*/
getToken = async () => {
if (this._accessToken && this._expiresAt > Date.now()) {
return this._accessToken;
}
if (this._refreshPromise) return this._refreshPromise;
this._refreshPromise = this._refreshToken().finally(() => {
this._refreshPromise = null;
});
return this._refreshPromise;
};
_refreshToken = async () => {
const credentials = Buffer.from(
`${this.config.credentials.username}:${this.config.credentials.password}`
).toString("base64");
const headers = {
Authorization: `Basic ${credentials}`,
...this.config.requestHeaders && {
...this.config.requestHeaders
}
};
const response = this.config.method === "GET" ? await this.config.client.get(this.config.tokenEndpoint, {
headers
}) : await this.config.client.post(this.config.tokenEndpoint, {
headers,
...this.config.requestBody && {
body: this.config.requestBody
}
});
if (!response.ok) {
throw new OperationFailedError(
"getAccessToken",
this.config.provider,
{
cause: new Error(
`Failed to obtain access token: ${JSON.stringify(response.value || response.error)}`
)
}
);
}
const tokenData = this.config.responseAdapter(
response.value
);
if (!tokenData.accessToken || !tokenData.expiresIn) {
throw new OperationFailedError(
"getAccessToken",
this.config.provider,
{
cause: new Error(
"Invalid token response: missing accessToken or expiresIn"
)
}
);
}
this._accessToken = tokenData.accessToken;
this._expiresAt = Date.now() + (tokenData.expiresIn - this.config.expiryBuffer) * 1e3;
return tokenData.accessToken;
};
/**
* Get authorization headers for API requests
*/
getAuthHeaders = async () => {
const token = await this.getToken();
return {
Authorization: `Bearer ${token}`,
...this.config.authHeaders && { ...this.config.authHeaders }
};
};
};
export { OAuth2TokenManager };