Notification.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /* Copyright 2015-2016 PayPal, Inc. */
  2. "use strict";
  3. var generate = require('../generate');
  4. var api = require('../api');
  5. var https = require('https');
  6. var crypto = require('crypto');
  7. var crc32 = require('buffer-crc32');
  8. /**
  9. * Exposes REST endpoints for creating and managing webhooks
  10. * @return {Object} webhook functions
  11. */
  12. function webhook() {
  13. var baseURL = '/v1/notifications/webhooks/';
  14. var operations = ['create', 'list', 'get', 'del', 'delete'];
  15. var ret = {
  16. baseURL: baseURL,
  17. replace: function replace(id, data, config, cb) {
  18. api.executeHttp('PATCH', this.baseURL + id, data, config, cb);
  19. },
  20. eventTypes: function eventTypes(id, config, cb) {
  21. api.executeHttp('GET', this.baseURL + id + '/event-types', {}, config, cb);
  22. }
  23. };
  24. ret = generate.mixin(ret, operations);
  25. return ret;
  26. }
  27. /**
  28. * Exposes REST endpoints for working with subscribed webhooks events
  29. *
  30. * https://developer.paypal.com/webapps/developer/docs/integration/direct/rest-webhooks-overview/#events
  31. * @return {Object} webhook event functions
  32. */
  33. function webhookEvent() {
  34. var baseURL = '/v1/notifications/webhooks-events/';
  35. var operations = ['list', 'get'];
  36. /**
  37. * Instead of calling this method, it is recommended that you initiate a GET request in your code for the webhook
  38. * event data and use the returned information from the webhook or use the updated verify() function. See
  39. * https://github.com/paypal/PayPal-node-SDK/wiki/Webhook-Validation
  40. *
  41. * @example
  42. * var paypal = require('paypal-rest-sdk');
  43. * function(request, response) {
  44. * try {
  45. * // Get the Webhook event id from the incoming event request
  46. * var webhookEventId = JSON.parse(request.body).id;
  47. *
  48. * paypal.notification.webhookEvent.get(webhookEventId, function (error, webhookEvent) {
  49. * if (error) {
  50. * console.log(error);
  51. * // The webhook event data could not be found.
  52. * // Send a HTTP 503 response status code ( http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4 )
  53. * // to signal to PayPal to resend the request at a later time.
  54. * response.sendStatus(503);
  55. * } else {
  56. * // Proceed to use the data from PayPal
  57. * console.log("Get webhookEvent Response");
  58. * console.log(JSON.stringify(webhookEvent));
  59. * response.sendStatus(200);
  60. * }
  61. * });
  62. * } catch (e) {
  63. * // The webhook id could not be found or any other error occurred.
  64. * // Send a HTTP 503 response status code ( http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4 )
  65. * // to signal to PayPal to resend the request at a later time
  66. * response.sendStatus(503);
  67. * }
  68. * }
  69. *
  70. * @deprecated
  71. * @param {String} body raw body of request
  72. * @param {Function} cb callback function
  73. */
  74. function getAndVerify(body, cb) {
  75. var response = false;
  76. var err = null;
  77. try {
  78. var webhookEventId = JSON.parse(body).id;
  79. api.executeHttp('GET', baseURL + webhookEventId, {}, function (error, res) {
  80. if (error) {
  81. cb(error, response);
  82. } else {
  83. cb(err, true);
  84. }
  85. });
  86. } catch (e) {
  87. err = new Error("Webhook Event Id attribute not found. Possible reason could be invalid JSON Object.");
  88. cb(err, response);
  89. }
  90. }
  91. /**
  92. * @param {Object} headers from request
  93. * @param {String} raw body of request
  94. * @param {String} webhook id
  95. * @param {Function} callback function
  96. */
  97. function verify(headers, body, webhookId, callback) {
  98. // In an effort not to break existing applications, accept old arguments temporarily
  99. if (arguments.length > 4) {
  100. /* jshint validthis: true */
  101. return verifyLegacy.apply(this, arguments);
  102. }
  103. if (typeof headers !== 'object') {
  104. return callback(new Error("headers is not an object"), false);
  105. }
  106. // Normalizes headers
  107. Object.keys(headers).forEach(function (header) {
  108. headers[header.toUpperCase()] = headers[header];
  109. });
  110. var webhookEventBody = (typeof body === "string") ? JSON.parse(body) : body;
  111. var payload = {
  112. 'auth_algo': headers['PAYPAL-AUTH-ALGO'],
  113. 'cert_url': headers['PAYPAL-CERT-URL'],
  114. 'transmission_id': headers['PAYPAL-TRANSMISSION-ID'],
  115. 'transmission_sig': headers['PAYPAL-TRANSMISSION-SIG'],
  116. 'transmission_time': headers['PAYPAL-TRANSMISSION-TIME'],
  117. 'webhook_id': webhookId,
  118. 'webhook_event': webhookEventBody
  119. };
  120. api.executeHttp('POST', '/v1/notifications/verify-webhook-signature', payload, callback);
  121. }
  122. function verifyLegacy(certURL, transmissionId, timeStamp, webhookId, eventBody, ppTransmissionSig, cb) {
  123. // Emit a warning that the arguments have changed
  124. if (process.env.NODE_ENV === 'development') {
  125. console.log('PayPal-Node-SDK: Webhook verify arguments have changed. Please check the latest documentation on https://developer.paypal.com/docs/integration/direct/rest-webhooks-overview/#event-signature.');
  126. }
  127. var headers = {
  128. // This is currently the default auth algorithm. If this changes, need to change. Legacy method did
  129. // not pass in the algorithm.
  130. 'PAYPAL-AUTH-ALGO': 'SHA256withRSA',
  131. 'PAYPAL-CERT-URL': certURL,
  132. 'PAYPAL-TRANSMISSION-ID': transmissionId,
  133. 'PAYPAL-TRANSMISSION-SIG': ppTransmissionSig,
  134. 'PAYPAL-TRANSMISSION-TIME': timeStamp
  135. };
  136. function legacyCallback(error, response) {
  137. if (error) {
  138. cb(error, false);
  139. } else {
  140. // Verification status must be SUCCESS
  141. if (response.verification_status === "SUCCESS") {
  142. cb(null, true);
  143. } else {
  144. cb(null, false);
  145. }
  146. }
  147. }
  148. return verify(headers, eventBody, webhookId, legacyCallback);
  149. }
  150. var ret = {
  151. baseURL: baseURL,
  152. verify: verify,
  153. getAndVerify: getAndVerify,
  154. resend: function resend(id, config, cb) {
  155. api.executeHttp('POST', this.baseURL + id + '/resend', {}, config, cb);
  156. }
  157. };
  158. ret = generate.mixin(ret, operations);
  159. return ret;
  160. }
  161. /**
  162. * Exposes REST endpoint for listing available event types for webhooks
  163. * @return {Object} webhook event type functions
  164. */
  165. function webhookEventType() {
  166. var baseURL = '/v1/notifications/webhooks-event-types/';
  167. var operations = ['list'];
  168. var ret = {
  169. baseURL: baseURL
  170. };
  171. ret = generate.mixin(ret, operations);
  172. return ret;
  173. }
  174. /**
  175. * Exposes the namespace for webhook and webhook event functionalities
  176. *
  177. * https://developer.paypal.com/webapps/developer/docs/api/#notifications
  178. * @return {Object} notification functions
  179. */
  180. function notification() {
  181. return {
  182. webhook: webhook(),
  183. webhookEvent: webhookEvent(),
  184. webhookEventType: webhookEventType()
  185. };
  186. }
  187. module.exports = notification;