paypal-checkout
Version:
PayPal Checkout components, for integrating checkout products.
162 lines (123 loc) • 7.18 kB
JavaScript
/* @flow */
import { ZalgoPromise } from 'zalgo-promise/src';
import { addPayloadBuilder } from 'beaver-logger/client';
import { type SameDomainWindowType } from 'cross-domain-utils/src';
type RequestOptionsType = {|
url : string,
method? : string,
headers? : { [key : string] : string },
json? : Object,
data? : { [key : string] : string },
body? : string,
win? : SameDomainWindowType,
timeout? : number
|};
const HEADERS = {
CONTENT_TYPE: 'content-type',
ACCEPT: 'accept'
};
const headerBuilders = [];
const corrids = [];
addPayloadBuilder(() => {
return {
prev_corr_ids: corrids.join(',')
};
});
function parseHeaders(rawHeaders : string = '') : { [string] : string } {
const result = {};
for (const line of rawHeaders.trim().split('\n')) {
const [ key, ...values ] = line.split(':');
result[key.toLowerCase()] = values.join(':').trim();
}
return result;
}
export function request({ url, method = 'get', headers = {}, json, data, body, win = window, timeout = 0 } : RequestOptionsType) : ZalgoPromise<Object> {
if (url === '/demo/checkout/api/braintree/client-token/') {
// $FlowFixMe
return ZalgoPromise.resolve('eyJ2ZXJzaW9uIjoyLCJhdXRob3JpemF0aW9uRmluZ2VycHJpbnQiOiJjMDFhZmRkM2Y1OTJmNWVhNTNlMzE5MWQwYmIyMWVjYjM5NzNlZGM1MzkwNDZiMjJmNTA2ODEyNzIzZmRlMTJifGNsaWVudF9pZD1jbGllbnRfaWQkc2FuZGJveCQ0ZHByYmZjNnBoNTk1Y2NqXHUwMDI2Y3JlYXRlZF9hdD0yMDE3LTA0LTI2VDIzOjI2OjU5Ljg3OTA3ODYwNiswMDAwXHUwMDI2bWVyY2hhbnRfaWQ9M3cydHR2d2QyNDY1NDhoZCIsImNvbmZpZ1VybCI6Imh0dHBzOi8vYXBpLnNhbmRib3guYnJhaW50cmVlZ2F0ZXdheS5jb206NDQzL21lcmNoYW50cy8zdzJ0dHZ3ZDI0NjU0OGhkL2NsaWVudF9hcGkvdjEvY29uZmlndXJhdGlvbiIsImNoYWxsZW5nZXMiOltdLCJlbnZpcm9ubWVudCI6InNhbmRib3giLCJjbGllbnRBcGlVcmwiOiJodHRwczovL2FwaS5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tOjQ0My9tZXJjaGFudHMvM3cydHR2d2QyNDY1NDhoZC9jbGllbnRfYXBpIiwiYXNzZXRzVXJsIjoiaHR0cHM6Ly9hc3NldHMuYnJhaW50cmVlZ2F0ZXdheS5jb20iLCJhdXRoVXJsIjoiaHR0cHM6Ly9hdXRoLnZlbm1vLnNhbmRib3guYnJhaW50cmVlZ2F0ZXdheS5jb20iLCJhbmFseXRpY3MiOnsidXJsIjoiaHR0cHM6Ly9jbGllbnQtYW5hbHl0aWNzLnNhbmRib3guYnJhaW50cmVlZ2F0ZXdheS5jb20vM3cydHR2d2QyNDY1NDhoZCJ9LCJ0aHJlZURTZWN1cmVFbmFibGVkIjpmYWxzZSwicGF5cGFsRW5hYmxlZCI6dHJ1ZSwicGF5cGFsIjp7ImRpc3BsYXlOYW1lIjoiYmFyY28uMDMtZmFjaWxpdGF0b3JAZ21haWwuY29tIiwiY2xpZW50SWQiOiJBV3VZdnFnMGtaN2Y5S0V4TVpqZU53T3RjQV8yZVhnOWpMZy1QSnBGX0pnYk44M0YyVml5aEdnV2JCNDg4RGU3MFpucGRBZEI2TUNqekNqSyIsInByaXZhY3lVcmwiOiJodHRwczovL2V4YW1wbGUuY29tIiwidXNlckFncmVlbWVudFVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJiYXNlVXJsIjoiaHR0cHM6Ly9hc3NldHMuYnJhaW50cmVlZ2F0ZXdheS5jb20iLCJhc3NldHNVcmwiOiJodHRwczovL2NoZWNrb3V0LnBheXBhbC5jb20iLCJkaXJlY3RCYXNlVXJsIjpudWxsLCJhbGxvd0h0dHAiOnRydWUsImVudmlyb25tZW50Tm9OZXR3b3JrIjpmYWxzZSwiZW52aXJvbm1lbnQiOiJvZmZsaW5lIiwidW52ZXR0ZWRNZXJjaGFudCI6ZmFsc2UsImJyYWludHJlZUNsaWVudElkIjoibWFzdGVyY2xpZW50MyIsImJpbGxpbmdBZ3JlZW1lbnRzRW5hYmxlZCI6dHJ1ZSwibWVyY2hhbnRBY2NvdW50SWQiOiJVU0QiLCJjdXJyZW5jeUlzb0NvZGUiOiJVU0QifSwiY29pbmJhc2VFbmFibGVkIjpmYWxzZSwibWVyY2hhbnRJZCI6IjN3MnR0dndkMjQ2NTQ4aGQiLCJ2ZW5tbyI6Im9mZiJ9');
}
return new ZalgoPromise((resolve, reject) => {
if ((json && data) || (json && body) || (data && json)) {
throw new Error(`Only options.json or options.data or options.body should be passed`);
}
const normalizedHeaders = {};
for (const key of Object.keys(headers)) {
normalizedHeaders[key.toLowerCase()] = headers[key];
}
if (json) {
normalizedHeaders[HEADERS.CONTENT_TYPE] = normalizedHeaders[HEADERS.CONTENT_TYPE] || 'application/json';
} else if (data || body) {
normalizedHeaders[HEADERS.CONTENT_TYPE] = normalizedHeaders[HEADERS.CONTENT_TYPE] || 'application/x-www-form-urlencoded; charset=utf-8';
}
normalizedHeaders[HEADERS.ACCEPT] = normalizedHeaders[HEADERS.ACCEPT] || 'application/json';
for (const headerBuilder of headerBuilders) {
const builtHeaders = headerBuilder();
for (const key of Object.keys(builtHeaders)) {
normalizedHeaders[key.toLowerCase()] = builtHeaders[key];
}
}
const xhr = new win.XMLHttpRequest();
xhr.addEventListener('load', function xhrLoad() : void {
const responseHeaders = parseHeaders(this.getAllResponseHeaders());
const corrID = responseHeaders['paypal-debug-id'] || 'unknown';
if (responseHeaders['paypal-debug-id']) {
corrids.push(responseHeaders['paypal-debug-id']);
}
if (!this.status) {
return reject(new Error(`Request to ${ method.toLowerCase() } ${ url } failed: no response status code. Correlation id: ${ corrID }`));
}
const contentType = responseHeaders['content-type'];
const isJSON = contentType && (contentType.indexOf('application/json') === 0 || contentType.indexOf('text/json') === 0);
let res = this.responseText;
try {
res = JSON.parse(this.responseText);
} catch (err) {
if (isJSON) {
return reject(new Error(`Invalid json: ${ this.responseText }. Correlation id: ${ corrID }`));
}
}
if (this.status >= 400) {
let message = `Request to ${ method.toLowerCase() } ${ url } failed with ${ this.status } error. Correlation id: ${ corrID }`;
if (res) {
if (typeof res === 'object' && res !== null) {
res = JSON.stringify(res, null, 4);
}
message = `${ message }\n\n${ res }\n`;
}
return reject(new Error(message));
}
return resolve(res);
}, false);
xhr.addEventListener('error', function xhrError(evt) {
const corrID = this.getResponseHeader('paypal-debug-id');
reject(new Error(`Request to ${ method.toLowerCase() } ${ url } failed: ${ evt.toString() }. Correlation id: ${ corrID }`));
}, false);
xhr.open(method, url, true);
for (const key in normalizedHeaders) {
if (normalizedHeaders.hasOwnProperty(key)) {
xhr.setRequestHeader(key, normalizedHeaders[key]);
}
}
if (json) {
body = JSON.stringify(json);
} else if (data) {
body = Object.keys(data).map(key => {
return `${ encodeURIComponent(key) }=${ data ? encodeURIComponent(data[key]) : '' }`;
}).join('&');
}
xhr.timeout = timeout;
xhr.ontimeout = function xhrTimeout() {
reject(new Error(`Request to ${ method.toLowerCase() } ${ url } has timed out`));
};
xhr.send(body);
});
}
request.get = (url : string, options = {}) => {
return request({ method: 'get', url, ...options });
};
request.post = (url : string, data, options = {}) => {
return request({ method: 'post', url, data, ...options });
};
request.addHeaderBuilder = (method) => {
headerBuilders.push(method);
};