1 | 'use strict';
|
2 |
|
3 | const EventEmitter = require('events').EventEmitter;
|
4 | const qs = require('qs');
|
5 | const crypto = require('crypto');
|
6 |
|
7 | const hasOwn = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | let exec = null;
|
13 | try {
|
14 | exec = require('child_process').exec;
|
15 | } catch (e) {
|
16 | if (e.code !== 'MODULE_NOT_FOUND') {
|
17 | throw e;
|
18 | }
|
19 | }
|
20 |
|
21 | const OPTIONS_KEYS = [
|
22 | 'apiKey',
|
23 | 'idempotencyKey',
|
24 | 'stripeAccount',
|
25 | 'apiVersion',
|
26 | 'maxNetworkRetries',
|
27 | 'timeout',
|
28 | ];
|
29 |
|
30 | const DEPRECATED_OPTIONS = {
|
31 | api_key: 'apiKey',
|
32 | idempotency_key: 'idempotencyKey',
|
33 | stripe_account: 'stripeAccount',
|
34 | stripe_version: 'apiVersion',
|
35 | stripeVersion: 'apiVersion',
|
36 | };
|
37 | const DEPRECATED_OPTIONS_KEYS = Object.keys(DEPRECATED_OPTIONS);
|
38 |
|
39 | const utils = (module.exports = {
|
40 | isOptionsHash(o) {
|
41 | return (
|
42 | o &&
|
43 | typeof o === 'object' &&
|
44 | (OPTIONS_KEYS.some((prop) => hasOwn(o, prop)) ||
|
45 | DEPRECATED_OPTIONS_KEYS.some((prop) => hasOwn(o, prop)))
|
46 | );
|
47 | },
|
48 |
|
49 | |
50 |
|
51 |
|
52 |
|
53 | stringifyRequestData: (data) => {
|
54 | return (
|
55 | qs
|
56 | .stringify(data, {
|
57 | serializeDate: (d) => Math.floor(d.getTime() / 1000),
|
58 | })
|
59 |
|
60 |
|
61 |
|
62 | .replace(/%5B/g, '[')
|
63 | .replace(/%5D/g, ']')
|
64 | );
|
65 | },
|
66 |
|
67 | |
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 | makeURLInterpolator: (() => {
|
74 | const rc = {
|
75 | '\n': '\\n',
|
76 | '"': '\\"',
|
77 | '\u2028': '\\u2028',
|
78 | '\u2029': '\\u2029',
|
79 | };
|
80 | return (str) => {
|
81 | const cleanString = str.replace(/["\n\r\u2028\u2029]/g, ($0) => rc[$0]);
|
82 | return (outputs) => {
|
83 | return cleanString.replace(/\{([\s\S]+?)\}/g, ($0, $1) =>
|
84 | encodeURIComponent(outputs[$1] || '')
|
85 | );
|
86 | };
|
87 | };
|
88 | })(),
|
89 |
|
90 | extractUrlParams: (path) => {
|
91 | const params = path.match(/\{\w+\}/g);
|
92 | if (!params) {
|
93 | return [];
|
94 | }
|
95 |
|
96 | return params.map((param) => param.replace(/[{}]/g, ''));
|
97 | },
|
98 |
|
99 | |
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | getDataFromArgs(args) {
|
106 | if (!Array.isArray(args) || !args[0] || typeof args[0] !== 'object') {
|
107 | return {};
|
108 | }
|
109 |
|
110 | if (!utils.isOptionsHash(args[0])) {
|
111 | return args.shift();
|
112 | }
|
113 |
|
114 | const argKeys = Object.keys(args[0]);
|
115 |
|
116 | const optionKeysInArgs = argKeys.filter((key) =>
|
117 | OPTIONS_KEYS.includes(key)
|
118 | );
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | if (
|
125 | optionKeysInArgs.length > 0 &&
|
126 | optionKeysInArgs.length !== argKeys.length
|
127 | ) {
|
128 | emitWarning(
|
129 | `Options found in arguments (${optionKeysInArgs.join(
|
130 | ', '
|
131 | )}). Did you mean to pass an options object? See https://github.com/stripe/stripe-node/wiki/Passing-Options.`
|
132 | );
|
133 | }
|
134 |
|
135 | return {};
|
136 | },
|
137 |
|
138 | |
139 |
|
140 |
|
141 | getOptionsFromArgs: (args) => {
|
142 | const opts = {
|
143 | auth: null,
|
144 | headers: {},
|
145 | settings: {},
|
146 | };
|
147 | if (args.length > 0) {
|
148 | const arg = args[args.length - 1];
|
149 | if (typeof arg === 'string') {
|
150 | opts.auth = args.pop();
|
151 | } else if (utils.isOptionsHash(arg)) {
|
152 | const params = args.pop();
|
153 |
|
154 | const extraKeys = Object.keys(params).filter(
|
155 | (key) => !OPTIONS_KEYS.includes(key)
|
156 | );
|
157 |
|
158 | if (extraKeys.length) {
|
159 | const nonDeprecated = extraKeys.filter((key) => {
|
160 | if (!DEPRECATED_OPTIONS[key]) {
|
161 | return true;
|
162 | }
|
163 | const newParam = DEPRECATED_OPTIONS[key];
|
164 | if (params[newParam]) {
|
165 | throw Error(
|
166 | `Both '${newParam}' and '${key}' were provided; please remove '${key}', which is deprecated.`
|
167 | );
|
168 | }
|
169 | |
170 |
|
171 |
|
172 | emitWarning(`'${key}' is deprecated; use '${newParam}' instead.`);
|
173 | params[newParam] = params[key];
|
174 | });
|
175 | if (nonDeprecated.length) {
|
176 | emitWarning(
|
177 | `Invalid options found (${extraKeys.join(', ')}); ignoring.`
|
178 | );
|
179 | }
|
180 | }
|
181 |
|
182 | if (params.apiKey) {
|
183 | opts.auth = params.apiKey;
|
184 | }
|
185 | if (params.idempotencyKey) {
|
186 | opts.headers['Idempotency-Key'] = params.idempotencyKey;
|
187 | }
|
188 | if (params.stripeAccount) {
|
189 | opts.headers['Stripe-Account'] = params.stripeAccount;
|
190 | }
|
191 | if (params.apiVersion) {
|
192 | opts.headers['Stripe-Version'] = params.apiVersion;
|
193 | }
|
194 | if (Number.isInteger(params.maxNetworkRetries)) {
|
195 | opts.settings.maxNetworkRetries = params.maxNetworkRetries;
|
196 | }
|
197 | if (Number.isInteger(params.timeout)) {
|
198 | opts.settings.timeout = params.timeout;
|
199 | }
|
200 | }
|
201 | }
|
202 | return opts;
|
203 | },
|
204 |
|
205 | |
206 |
|
207 |
|
208 | protoExtend(sub) {
|
209 | const Super = this;
|
210 | const Constructor = hasOwn(sub, 'constructor')
|
211 | ? sub.constructor
|
212 | : function(...args) {
|
213 | Super.apply(this, args);
|
214 | };
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | Object.assign(Constructor, Super);
|
222 | Constructor.prototype = Object.create(Super.prototype);
|
223 | Object.assign(Constructor.prototype, sub);
|
224 |
|
225 | return Constructor;
|
226 | },
|
227 |
|
228 | |
229 |
|
230 |
|
231 | secureCompare: (a, b) => {
|
232 | a = Buffer.from(a);
|
233 | b = Buffer.from(b);
|
234 |
|
235 |
|
236 |
|
237 | if (a.length !== b.length) {
|
238 | return false;
|
239 | }
|
240 |
|
241 |
|
242 |
|
243 | if (crypto.timingSafeEqual) {
|
244 | return crypto.timingSafeEqual(a, b);
|
245 | }
|
246 |
|
247 | const len = a.length;
|
248 | let result = 0;
|
249 |
|
250 | for (let i = 0; i < len; ++i) {
|
251 | result |= a[i] ^ b[i];
|
252 | }
|
253 | return result === 0;
|
254 | },
|
255 |
|
256 | |
257 |
|
258 |
|
259 | removeNullish: (obj) => {
|
260 | if (typeof obj !== 'object') {
|
261 | throw new Error('Argument must be an object');
|
262 | }
|
263 |
|
264 | return Object.keys(obj).reduce((result, key) => {
|
265 | if (obj[key] != null) {
|
266 | result[key] = obj[key];
|
267 | }
|
268 | return result;
|
269 | }, {});
|
270 | },
|
271 |
|
272 | |
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 | normalizeHeaders: (obj) => {
|
279 | if (!(obj && typeof obj === 'object')) {
|
280 | return obj;
|
281 | }
|
282 |
|
283 | return Object.keys(obj).reduce((result, header) => {
|
284 | result[utils.normalizeHeader(header)] = obj[header];
|
285 | return result;
|
286 | }, {});
|
287 | },
|
288 |
|
289 | |
290 |
|
291 |
|
292 |
|
293 | normalizeHeader: (header) => {
|
294 | return header
|
295 | .split('-')
|
296 | .map(
|
297 | (text) => text.charAt(0).toUpperCase() + text.substr(1).toLowerCase()
|
298 | )
|
299 | .join('-');
|
300 | },
|
301 |
|
302 | |
303 |
|
304 |
|
305 |
|
306 | checkForStream: (obj) => {
|
307 | if (obj.file && obj.file.data) {
|
308 | return obj.file.data instanceof EventEmitter;
|
309 | }
|
310 | return false;
|
311 | },
|
312 |
|
313 | callbackifyPromiseWithTimeout: (promise, callback) => {
|
314 | if (callback) {
|
315 |
|
316 | return promise.then(
|
317 | (res) => {
|
318 | setTimeout(() => {
|
319 | callback(null, res);
|
320 | }, 0);
|
321 | },
|
322 | (err) => {
|
323 | setTimeout(() => {
|
324 | callback(err, null);
|
325 | }, 0);
|
326 | }
|
327 | );
|
328 | }
|
329 |
|
330 | return promise;
|
331 | },
|
332 |
|
333 | |
334 |
|
335 |
|
336 | pascalToCamelCase: (name) => {
|
337 | if (name === 'OAuth') {
|
338 | return 'oauth';
|
339 | } else {
|
340 | return name[0].toLowerCase() + name.substring(1);
|
341 | }
|
342 | },
|
343 |
|
344 | emitWarning,
|
345 |
|
346 | |
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 |
|
353 | safeExec: (cmd, cb) => {
|
354 |
|
355 |
|
356 | if (utils._exec === null) {
|
357 | cb(new Error('exec not available'), null);
|
358 | return;
|
359 | }
|
360 |
|
361 | try {
|
362 | utils._exec(cmd, cb);
|
363 | } catch (e) {
|
364 | cb(e, null);
|
365 | }
|
366 | },
|
367 |
|
368 |
|
369 | _exec: exec,
|
370 |
|
371 | isObject: (obj) => {
|
372 | const type = typeof obj;
|
373 | return (type === 'function' || type === 'object') && !!obj;
|
374 | },
|
375 |
|
376 |
|
377 | flattenAndStringify: (data) => {
|
378 | const result = {};
|
379 |
|
380 | const step = (obj, prevKey) => {
|
381 | Object.keys(obj).forEach((key) => {
|
382 | const value = obj[key];
|
383 |
|
384 | const newKey = prevKey ? `${prevKey}[${key}]` : key;
|
385 |
|
386 | if (utils.isObject(value)) {
|
387 | if (!Buffer.isBuffer(value) && !value.hasOwnProperty('data')) {
|
388 |
|
389 | return step(value, newKey);
|
390 | } else {
|
391 |
|
392 | result[newKey] = value;
|
393 | }
|
394 | } else {
|
395 |
|
396 | result[newKey] = String(value);
|
397 | }
|
398 | });
|
399 | };
|
400 |
|
401 | step(data);
|
402 |
|
403 | return result;
|
404 | },
|
405 |
|
406 | |
407 |
|
408 |
|
409 | uuid4: () => {
|
410 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
411 | const r = (Math.random() * 16) | 0;
|
412 | const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
413 | return v.toString(16);
|
414 | });
|
415 | },
|
416 |
|
417 | validateInteger: (name, n, defaultVal) => {
|
418 | if (!Number.isInteger(n)) {
|
419 | if (defaultVal !== undefined) {
|
420 | return defaultVal;
|
421 | } else {
|
422 | throw new Error(`${name} must be an integer`);
|
423 | }
|
424 | }
|
425 |
|
426 | return n;
|
427 | },
|
428 | });
|
429 |
|
430 | function emitWarning(warning) {
|
431 | if (typeof process.emitWarning !== 'function') {
|
432 | return console.warn(
|
433 | `Stripe: ${warning}`
|
434 | );
|
435 | }
|
436 |
|
437 | return process.emitWarning(warning, 'Stripe');
|
438 | }
|