client.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /* Copyright 2015-2016 PayPal, Inc. */
  2. "use strict";
  3. var http = require('http');
  4. var https = require('https');
  5. var querystring = require('querystring');
  6. var configuration = require('./configure');
  7. var semver = require('semver');
  8. /**
  9. * Wraps the http client, handles request parameters, populates request headers, handles response
  10. * @param {String} http_method HTTP Method GET/POST
  11. * @param {String} path url endpoint
  12. * @param {Object} data Payload for HTTP Request
  13. * @param {Object} http_options_param Configuration parameters
  14. * @param {Function} cb [description]
  15. */
  16. var invoke = exports.invoke = function invoke(http_method, path, data, http_options_param, cb) {
  17. var client = (http_options_param.schema === 'http') ? http : https;
  18. var request_data = data;
  19. if (http_method === 'GET') {
  20. //format object parameters into GET request query string
  21. if (typeof request_data !== 'string') {
  22. request_data = querystring.stringify(request_data);
  23. }
  24. if (request_data) {
  25. path = path + "?" + request_data;
  26. request_data = "";
  27. }
  28. } else if (typeof request_data !== 'string') {
  29. request_data = JSON.stringify(request_data);
  30. }
  31. var http_options = {};
  32. if (http_options_param) {
  33. http_options = JSON.parse(JSON.stringify(http_options_param));
  34. if (!http_options.headers) {
  35. http_options.headers = {};
  36. }
  37. http_options.path = path;
  38. http_options.method = http_method;
  39. if (request_data) {
  40. http_options.headers['Content-Length'] = Buffer.byteLength(request_data, 'utf-8');
  41. }
  42. if (!http_options.headers.Accept) {
  43. http_options.headers.Accept = 'application/json';
  44. }
  45. if (!http_options.headers['Content-Type']) {
  46. http_options.headers['Content-Type'] = 'application/json';
  47. }
  48. http_options.headers['User-Agent'] = configuration.userAgent;
  49. http_options.withCredentials = false;
  50. }
  51. // Enable full request response logging in development/non-production environment only
  52. if (configuration.default_options.mode !== 'live' && process.env.PAYPAL_DEBUG) {
  53. console.dir(JSON.stringify(http_options.headers));
  54. console.dir(request_data);
  55. }
  56. //PCI compliance
  57. if (process.versions !== undefined && process.versions.openssl !== undefined && semver.lt(process.versions.openssl.slice(0, 5), '1.0.1')) {
  58. console.warn('WARNING: openssl version ' + process.versions.openssl + ' detected. Per PCI Security Council mandate (https://github.com/paypal/TLS-update), you MUST update to the latest security library.');
  59. }
  60. var req = client.request(http_options);
  61. req.on('error', function (e) {
  62. console.log('problem with request: ' + e.message);
  63. cb(e, null);
  64. });
  65. req.on('response', function (res) {
  66. var response = '';
  67. //do not setEndcoding with browserify
  68. if (res.setEncoding) {
  69. res.setEncoding('utf8');
  70. }
  71. res.on('data', function (chunk) {
  72. response += chunk;
  73. });
  74. res.on('end', function () {
  75. var err = null;
  76. try {
  77. //export PAYPAL_DEBUG to development to get access to paypal-debug-id
  78. //for questions to merchant technical services.
  79. if (res.headers['paypal-debug-id'] !== undefined && process.env.PAYPAL_DEBUG) {
  80. console.log('paypal-debug-id: ' + res.headers['paypal-debug-id']);
  81. if (configuration.default_options.mode !== 'live') {
  82. console.dir(JSON.stringify(res.headers));
  83. console.dir(response);
  84. }
  85. }
  86. // Set response to an empty object if no data was received
  87. if (response.trim() === '') {
  88. response = {};
  89. } else if (typeof res.headers['content-type'] === "string" &&
  90. res.headers['content-type'].match(/^application\/json(?:;.*)?$/) !== null) {
  91. // Set response to be parsed JSON object if data received is json
  92. // expect that content-type header has application/json when it
  93. // returns data
  94. response = JSON.parse(response);
  95. }
  96. response.httpStatusCode = res.statusCode;
  97. } catch (e) {
  98. err = new Error('Invalid JSON Response Received. If the response received is empty, please check' +
  99. 'the httpStatusCode attribute of error message for 401 or 403. It is possible that the client credentials' +
  100. 'are invalid for the environment you are using, be it live or sandbox.');
  101. err.error = {
  102. name: 'Invalid JSON Response Received, JSON Parse Error.'
  103. };
  104. err.response = response;
  105. err.httpStatusCode = res.statusCode;
  106. response = null;
  107. }
  108. if (!err && (res.statusCode < 200 || res.statusCode >= 300)) {
  109. err = new Error('Response Status : ' + res.statusCode);
  110. // response contains the full json description of the error
  111. // that PayPal returns and information link
  112. err.response = response;
  113. if (process.env.PAYPAL_DEBUG) {
  114. err.response_stringified = JSON.stringify(response);
  115. }
  116. err.httpStatusCode = res.statusCode;
  117. response = null;
  118. }
  119. cb(err, response);
  120. });
  121. });
  122. if (request_data) {
  123. req.write(request_data);
  124. }
  125. req.end();
  126. };