UNPKG

@hyperse/paypal-node-sdk

Version:

NodeJS SDK for PayPal Checkout APIs

137 lines (136 loc) 4.92 kB
import { readFileSync } from 'fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import paypalhttp, {} from '@paypal/paypalhttp'; import { AccessToken } from './AccessToken.js'; import { AccessTokenRequest } from './AccessTokenRequest.js'; import {} from './HttpRequestBase.js'; import {} from './PayPalEnvironment.js'; import { TokenCache } from './TokenCache.js'; /** * Provider method to simulate __dirname veriable. * @param url import.meta.url * @param paths paths to join. */ const getDirname = (url, ...paths) => { return join(dirname(fileURLToPath(url)), ...paths); }; /** * PayPal Http client * Documentation * * @see {@link https://github.com/hyperse-io/paypal-node-sdk/tree/main/src/core/PayPalHttpClient.ts} */ export class PayPalHttpClient extends paypalhttp.HttpClient { /** * @param environment - The environment for this client * @param refreshToken - The refreshToken to be used to generate the access Token. */ constructor(environment, refreshToken) { super(environment); /** * An injector that fetches token when the client has no token or is expired and queues calls if the token is refreshing * @param {Object} request - The current request for the client * @return {Promise.<any>} Promise that fetches a new access Token */ this.authInjector = (request) => { if (request.headers.Authorization) { return; } if (this._cache.isValid()) { this._setAuthHeader(request); } else if (this._cache.isLocked()) { return this._cache.wait(request).then(() => this._setAuthHeader(request)); } else if (!this._cache.isValid()) { return Promise.all([ this._cache.wait(request), this.fetchAccessToken(), ]).then(() => this._setAuthHeader(request)); } }; this._cache = TokenCache.cacheForEnvironment(environment, refreshToken); this.refreshToken = refreshToken; this.addInjector(this.authInjector); this.package = JSON.parse(readFileSync(getDirname(import.meta.url, '../../package.json'), 'utf8')); this.addInjector(function (req) { req.headers['Accept-Encoding'] = 'gzip'; req.headers['sdk_name'] = 'Checkout SDK'; req.headers['sdk_version'] = '1.0.2'; req.headers['sdk_tech_stack'] = 'NodeJS ' + process.version; req.headers['api_integration_type'] = 'PAYPALSDK'; }); } /** * Returns the user agent for this client implementation * @override * @return {string} - The user agent string */ getUserAgent() { return ('PayPalSDK/PayPal-node-SDK ' + this.package.version + ' (node ' + process.version + '-' + process.arch + '-' + process.platform + '; OpenSSL ' + process.versions.openssl + ')'); } /** * Executes a request and returns a Promise * @param request Request Instance of HttpRequest * @returns */ execute(request) { return super.execute(request).catch((err) => { if (err.statusCode === 401) { return this._retryRequest(request); } return Promise.reject(err); }); } _retryRequest(request) { const promise = this._cache.wait(request).then(() => { this._setAuthHeader(request); return super.execute(request); }); if (this._cache.isLocked()) { return promise; } // Avoids node UnhandledPromiseRejectionWarning on access token failure. return Promise.race([this.fetchAccessToken(), promise]).then(() => promise); } fetchAccessToken() { this._cache.lock(); return super .execute(new AccessTokenRequest(this.environment, this.refreshToken)) .then((resp) => { const token = new AccessToken(resp.result); this._cache.setToken(token); this._cache.notify(); this._cache.unlock(); return token; }) .catch((err) => { this._cache.setToken(null); this._cache.notify(err); this._cache.unlock(); return Promise.reject(err); }); } /** * Sets the Authorization header for this request based on the client token * @param {Object} request - The request to modify * @private * @return {void} */ _setAuthHeader(request) { const token = this._cache.getToken(); request.headers = request.headers || {}; request.headers.Authorization = token?.authorizationString(); } }