Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@shopify/koa-shopify-webhooks

Package Overview
Dependencies
Maintainers
19
Versions
75
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shopify/koa-shopify-webhooks - npm Package Compare versions

Comparing version 2.6.3 to 2.6.4

8

build/cjs/index.js

@@ -8,3 +8,3 @@ "use strict";

enumerable: true,
get: function get() {
get: function () {
return _receive.receiveWebhook;

@@ -15,3 +15,3 @@ }

enumerable: true,
get: function get() {
get: function () {
return _register.registerWebhook;

@@ -22,3 +22,3 @@ }

enumerable: true,
get: function get() {
get: function () {
return _register.DeliveryMethod;

@@ -29,3 +29,3 @@ }

enumerable: true,
get: function get() {
get: function () {
return _types.WebhookHeader;

@@ -32,0 +32,0 @@ }

@@ -8,4 +8,2 @@ "use strict";

var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _crypto = require("crypto");

@@ -25,71 +23,37 @@

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function receiveWebhook({
secret,
path,
onReceived = noop
}) {
async function receiveWebhookMiddleware(ctx, next) {
const hmac = ctx.get(_types.WebhookHeader.Hmac);
const topic = ctx.get(_types.WebhookHeader.Topic);
const domain = ctx.get(_types.WebhookHeader.Domain);
const {
rawBody
} = ctx.request;
const generatedHash = (0, _crypto.createHmac)('sha256', secret).update(rawBody, 'utf8').digest('base64');
const graphqlTopic = topic.toUpperCase().replace(/\//g, '_');
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function receiveWebhook(_ref) {
var secret = _ref.secret,
path = _ref.path,
_ref$onReceived = _ref.onReceived,
onReceived = _ref$onReceived === void 0 ? noop : _ref$onReceived;
function receiveWebhookMiddleware(_x, _x2) {
return _receiveWebhookMiddleware.apply(this, arguments);
if ((0, _safeCompare.default)(generatedHash, hmac)) {
ctx.res.statusCode = _network.StatusCode.Accepted;
ctx.state.webhook = {
topic: graphqlTopic,
domain,
payload: JSON.parse(rawBody)
};
await onReceived(ctx);
await next();
} else {
ctx.res.statusCode = _network.StatusCode.Forbidden;
}
}
function _receiveWebhookMiddleware() {
_receiveWebhookMiddleware = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee(ctx, next) {
var hmac, topic, domain, rawBody, generatedHash, graphqlTopic;
return _regenerator["default"].wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
hmac = ctx.get(_types.WebhookHeader.Hmac);
topic = ctx.get(_types.WebhookHeader.Topic);
domain = ctx.get(_types.WebhookHeader.Domain);
rawBody = ctx.request.rawBody;
generatedHash = (0, _crypto.createHmac)('sha256', secret).update(rawBody, 'utf8').digest('base64');
graphqlTopic = topic.toUpperCase().replace(/\//g, '_');
if (!(0, _safeCompare["default"])(generatedHash, hmac)) {
_context.next = 15;
break;
}
ctx.res.statusCode = _network.StatusCode.Accepted;
ctx.state.webhook = {
topic: graphqlTopic,
domain: domain,
payload: JSON.parse(rawBody)
};
_context.next = 11;
return onReceived(ctx);
case 11:
_context.next = 13;
return next();
case 13:
_context.next = 16;
break;
case 15:
ctx.res.statusCode = _network.StatusCode.Forbidden;
case 16:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return _receiveWebhookMiddleware.apply(this, arguments);
}
var middleware = (0, _koaCompose["default"])([(0, _koaBodyparser["default"])(), receiveWebhookMiddleware]);
return path ? (0, _koaMount["default"])(path, middleware) : middleware;
const middleware = (0, _koaCompose.default)([(0, _koaBodyparser.default)(), receiveWebhookMiddleware]);
return path ? (0, _koaMount.default)(path, middleware) : middleware;
}
function noop() {}

@@ -9,4 +9,2 @@ "use strict";

var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _network = require("@shopify/network");

@@ -16,11 +14,3 @@

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
var ApiVersion;
let ApiVersion;
exports.ApiVersion = ApiVersion;

@@ -40,3 +30,3 @@

var DeliveryMethod;
let DeliveryMethod;
exports.DeliveryMethod = DeliveryMethod;

@@ -49,46 +39,25 @@

function registerWebhook(_x) {
return _registerWebhook.apply(this, arguments);
async function registerWebhook({
address,
topic,
accessToken,
shop,
apiVersion,
deliveryMethod = DeliveryMethod.Http
}) {
const response = await fetch(`https://${shop}/admin/api/${apiVersion}/graphql.json`, {
method: _network.Method.Post,
body: buildQuery(topic, address, deliveryMethod),
headers: {
[_types.WebhookHeader.AccessToken]: accessToken,
[_network.Header.ContentType]: 'application/graphql'
}
});
const result = await response.json();
return {
success: isSuccess(result, deliveryMethod),
result
};
}
function _registerWebhook() {
_registerWebhook = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee(_ref) {
var _headers;
var address, topic, accessToken, shop, apiVersion, _ref$deliveryMethod, deliveryMethod, response, result;
return _regenerator["default"].wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
address = _ref.address, topic = _ref.topic, accessToken = _ref.accessToken, shop = _ref.shop, apiVersion = _ref.apiVersion, _ref$deliveryMethod = _ref.deliveryMethod, deliveryMethod = _ref$deliveryMethod === void 0 ? DeliveryMethod.Http : _ref$deliveryMethod;
_context.next = 3;
return fetch("https://".concat(shop, "/admin/api/").concat(apiVersion, "/graphql.json"), {
method: _network.Method.Post,
body: buildQuery(topic, address, deliveryMethod),
headers: (_headers = {}, _defineProperty(_headers, _types.WebhookHeader.AccessToken, accessToken), _defineProperty(_headers, _network.Header.ContentType, 'application/graphql'), _headers)
});
case 3:
response = _context.sent;
_context.next = 6;
return response.json();
case 6:
result = _context.sent;
return _context.abrupt("return", {
success: isSuccess(result, deliveryMethod),
result: result
});
case 8:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return _registerWebhook.apply(this, arguments);
}
function isSuccess(result, deliveryMethod) {

@@ -105,4 +74,4 @@ switch (deliveryMethod) {

function buildQuery(topic, address, deliveryMethod) {
var mutationName;
var webhookSubscriptionArgs;
let mutationName;
let webhookSubscriptionArgs;

@@ -112,3 +81,3 @@ switch (deliveryMethod) {

mutationName = 'webhookSubscriptionCreate';
webhookSubscriptionArgs = "{callbackUrl: \"".concat(address, "\"}");
webhookSubscriptionArgs = `{callbackUrl: "${address}"}`;
break;

@@ -118,7 +87,19 @@

mutationName = 'eventBridgeWebhookSubscriptionCreate';
webhookSubscriptionArgs = "{arn: \"".concat(address, "\"}");
webhookSubscriptionArgs = `{arn: "${address}"}`;
break;
}
return "\n mutation webhookSubscriptionCreate {\n ".concat(mutationName, "(topic: ").concat(topic, ", webhookSubscription: ").concat(webhookSubscriptionArgs, ") {\n userErrors {\n field\n message\n }\n webhookSubscription {\n id\n }\n }\n }\n ");
return `
mutation webhookSubscriptionCreate {
${mutationName}(topic: ${topic}, webhookSubscription: ${webhookSubscriptionArgs}) {
userErrors {
field
message
}
webhookSubscription {
id
}
}
}
`;
}

@@ -7,3 +7,3 @@ "use strict";

exports.WebhookHeader = void 0;
var WebhookHeader;
let WebhookHeader;
exports.WebhookHeader = WebhookHeader;

@@ -10,0 +10,0 @@

@@ -1,9 +0,3 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var receive_1 = require("./receive");
Object.defineProperty(exports, "receiveWebhook", { enumerable: true, get: function () { return receive_1.receiveWebhook; } });
var register_1 = require("./register");
Object.defineProperty(exports, "registerWebhook", { enumerable: true, get: function () { return register_1.registerWebhook; } });
Object.defineProperty(exports, "DeliveryMethod", { enumerable: true, get: function () { return register_1.DeliveryMethod; } });
var types_1 = require("./types");
Object.defineProperty(exports, "WebhookHeader", { enumerable: true, get: function () { return types_1.WebhookHeader; } });
export { receiveWebhook } from './receive';
export { registerWebhook, DeliveryMethod } from './register';
export { WebhookHeader } from './types';

@@ -1,54 +0,35 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.receiveWebhook = void 0;
var tslib_1 = require("tslib");
var crypto_1 = require("crypto");
var safe_compare_1 = tslib_1.__importDefault(require("safe-compare"));
var koa_bodyparser_1 = tslib_1.__importDefault(require("koa-bodyparser"));
var koa_mount_1 = tslib_1.__importDefault(require("koa-mount"));
var koa_compose_1 = tslib_1.__importDefault(require("koa-compose"));
var network_1 = require("@shopify/network");
var types_1 = require("./types");
function receiveWebhook(_a) {
var secret = _a.secret, path = _a.path, _b = _a.onReceived, onReceived = _b === void 0 ? noop : _b;
function receiveWebhookMiddleware(ctx, next) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var hmac, topic, domain, rawBody, generatedHash, graphqlTopic;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
hmac = ctx.get(types_1.WebhookHeader.Hmac);
topic = ctx.get(types_1.WebhookHeader.Topic);
domain = ctx.get(types_1.WebhookHeader.Domain);
rawBody = ctx.request.rawBody;
generatedHash = crypto_1.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
graphqlTopic = topic.toUpperCase().replace(/\//g, '_');
if (!safe_compare_1.default(generatedHash, hmac)) return [3 /*break*/, 3];
ctx.res.statusCode = network_1.StatusCode.Accepted;
ctx.state.webhook = {
topic: graphqlTopic,
domain: domain,
payload: JSON.parse(rawBody),
};
return [4 /*yield*/, onReceived(ctx)];
case 1:
_a.sent();
return [4 /*yield*/, next()];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
ctx.res.statusCode = network_1.StatusCode.Forbidden;
_a.label = 4;
case 4: return [2 /*return*/];
}
});
});
import { createHmac } from 'crypto';
import safeCompare from 'safe-compare';
import bodyParser from 'koa-bodyparser';
import mount from 'koa-mount';
import compose from 'koa-compose';
import { StatusCode } from '@shopify/network';
import { WebhookHeader } from './types';
export function receiveWebhook({ secret, path, onReceived = noop, }) {
async function receiveWebhookMiddleware(ctx, next) {
const hmac = ctx.get(WebhookHeader.Hmac);
const topic = ctx.get(WebhookHeader.Topic);
const domain = ctx.get(WebhookHeader.Domain);
const { rawBody } = ctx.request;
const generatedHash = createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
const graphqlTopic = topic.toUpperCase().replace(/\//g, '_');
if (safeCompare(generatedHash, hmac)) {
ctx.res.statusCode = StatusCode.Accepted;
ctx.state.webhook = {
topic: graphqlTopic,
domain,
payload: JSON.parse(rawBody),
};
await onReceived(ctx);
await next();
}
else {
ctx.res.statusCode = StatusCode.Forbidden;
}
}
var middleware = koa_compose_1.default([koa_bodyparser_1.default(), receiveWebhookMiddleware]);
return path ? koa_mount_1.default(path, middleware) : middleware;
const middleware = compose([bodyParser(), receiveWebhookMiddleware]);
return path ? mount(path, middleware) : middleware;
}
exports.receiveWebhook = receiveWebhook;
function noop() { }

@@ -1,8 +0,4 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerWebhook = exports.DeliveryMethod = exports.ApiVersion = void 0;
var tslib_1 = require("tslib");
var network_1 = require("@shopify/network");
var types_1 = require("./types");
var ApiVersion;
import { Method, Header } from '@shopify/network';
import { WebhookHeader } from './types';
export var ApiVersion;
(function (ApiVersion) {

@@ -18,34 +14,20 @@ ApiVersion["April19"] = "2019-04";

ApiVersion["Unversioned"] = "unversioned";
})(ApiVersion = exports.ApiVersion || (exports.ApiVersion = {}));
var DeliveryMethod;
})(ApiVersion || (ApiVersion = {}));
export var DeliveryMethod;
(function (DeliveryMethod) {
DeliveryMethod["Http"] = "http";
DeliveryMethod["EventBridge"] = "eventbridge";
})(DeliveryMethod = exports.DeliveryMethod || (exports.DeliveryMethod = {}));
function registerWebhook(_a) {
var address = _a.address, topic = _a.topic, accessToken = _a.accessToken, shop = _a.shop, apiVersion = _a.apiVersion, _b = _a.deliveryMethod, deliveryMethod = _b === void 0 ? DeliveryMethod.Http : _b;
return tslib_1.__awaiter(this, void 0, void 0, function () {
var response, result;
var _c;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0: return [4 /*yield*/, fetch("https://" + shop + "/admin/api/" + apiVersion + "/graphql.json", {
method: network_1.Method.Post,
body: buildQuery(topic, address, deliveryMethod),
headers: (_c = {},
_c[types_1.WebhookHeader.AccessToken] = accessToken,
_c[network_1.Header.ContentType] = 'application/graphql',
_c),
})];
case 1:
response = _d.sent();
return [4 /*yield*/, response.json()];
case 2:
result = _d.sent();
return [2 /*return*/, { success: isSuccess(result, deliveryMethod), result: result }];
}
});
})(DeliveryMethod || (DeliveryMethod = {}));
export async function registerWebhook({ address, topic, accessToken, shop, apiVersion, deliveryMethod = DeliveryMethod.Http, }) {
const response = await fetch(`https://${shop}/admin/api/${apiVersion}/graphql.json`, {
method: Method.Post,
body: buildQuery(topic, address, deliveryMethod),
headers: {
[WebhookHeader.AccessToken]: accessToken,
[Header.ContentType]: 'application/graphql',
},
});
const result = await response.json();
return { success: isSuccess(result, deliveryMethod), result };
}
exports.registerWebhook = registerWebhook;
function isSuccess(result, deliveryMethod) {

@@ -64,15 +46,27 @@ switch (deliveryMethod) {

function buildQuery(topic, address, deliveryMethod) {
var mutationName;
var webhookSubscriptionArgs;
let mutationName;
let webhookSubscriptionArgs;
switch (deliveryMethod) {
case DeliveryMethod.Http:
mutationName = 'webhookSubscriptionCreate';
webhookSubscriptionArgs = "{callbackUrl: \"" + address + "\"}";
webhookSubscriptionArgs = `{callbackUrl: "${address}"}`;
break;
case DeliveryMethod.EventBridge:
mutationName = 'eventBridgeWebhookSubscriptionCreate';
webhookSubscriptionArgs = "{arn: \"" + address + "\"}";
webhookSubscriptionArgs = `{arn: "${address}"}`;
break;
}
return "\n mutation webhookSubscriptionCreate {\n " + mutationName + "(topic: " + topic + ", webhookSubscription: " + webhookSubscriptionArgs + ") {\n userErrors {\n field\n message\n }\n webhookSubscription {\n id\n }\n }\n }\n ";
return `
mutation webhookSubscriptionCreate {
${mutationName}(topic: ${topic}, webhookSubscription: ${webhookSubscriptionArgs}) {
userErrors {
field
message
}
webhookSubscription {
id
}
}
}
`;
}

@@ -1,5 +0,2 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebhookHeader = void 0;
var WebhookHeader;
export var WebhookHeader;
(function (WebhookHeader) {

@@ -10,2 +7,2 @@ WebhookHeader["AccessToken"] = "X-Shopify-Access-Token";

WebhookHeader["Domain"] = "X-Shopify-Shop-Domain";
})(WebhookHeader = exports.WebhookHeader || (exports.WebhookHeader = {}));
})(WebhookHeader || (WebhookHeader = {}));

@@ -8,4 +8,12 @@ # Changelog

## [2.6.2] - 2021-03-03
<!-- ## Unreleased -->
## 2.6.4 - 2021-04-13
### Changed
- Removed dependency on tslib, as we no-longer compile with `tsc`. [#1829](https://github.com/Shopify/quilt/pull/1829)
## 2.6.2 - 2021-03-03
### Fixed

@@ -15,3 +23,3 @@

## [2.6.0] - 2020-12-18
## 2.6.0 - 2020-12-18

@@ -22,11 +30,11 @@ ### Added

## [2.5.3] - 2020-12-08
## 2.5.3 - 2020-12-08
- The `ApiVersion` enum now has an `October20` option. [#1697](https://github.com/Shopify/quilt/pull/1697)
## [2.5.2] - 2020-10-20
## 2.5.2 - 2020-10-20
- Added `tslib@^1.14.1` in the list of dependencies. [#1657](https://github.com/Shopify/quilt/pull/1657)
## [2.5.1] - 2020-09-28
## 2.5.1 - 2020-09-28

@@ -37,15 +45,15 @@ ### Added

## [2.5.0] - 2020-08-20
## 2.5.0 - 2020-08-20
- Add an option to register EventBridge webhooks to `registerWebhook`.
## [2.4.0] - 2020-02-19
## 2.4.0 - 2020-02-19
- The `ApiVersion` enum now has an `January20` and `April20` options
## [2.3.0] - 2020-01-27
## 2.3.0 - 2020-01-27
- Add [webhooks for billing](https://help.shopify.com/en/api/guides/billing-api#webhooks-for-billing) to topics
## [2.2.0] - 2019-11-08
## 2.2.0 - 2019-11-08

@@ -60,7 +68,7 @@ ### Added

## [2.1.0] - 2019-10-03
## 2.1.0 - 2019-10-03
- The `ApiVersion` enum now has an `October19` option
## [2.0.0] - 2019-09-26
## 2.0.0 - 2019-09-26

@@ -71,15 +79,15 @@ _Breaking change_

## [1.1.4] - 2019-03-29
## 1.1.4 - 2019-03-29
- Check success via valid webhookSubscription field
## [1.1.3] - 2019-03-22
## 1.1.3 - 2019-03-22
- Return a GraphQL formatted topic
## [1.1.2]
## 1.1.2
- Added 1.1.1 version to CHANGELOG
## [1.1.1]
## 1.1.1

@@ -90,3 +98,3 @@ ### Fixed

## [1.1.0]
## 1.1.0

@@ -97,3 +105,3 @@ ### Changed

## [1.0.1]
## 1.0.1

@@ -104,3 +112,3 @@ ### Fixed

## [1.0.0]
## 1.0.0

@@ -107,0 +115,0 @@ ### Added

@@ -1,1 +0,1 @@

module.exports = require("./build/node/index.js");
module.exports = require("./build/cjs/index.js");
{
"name": "@shopify/koa-shopify-webhooks",
"version": "2.6.3",
"version": "2.6.4",
"license": "MIT",

@@ -8,5 +8,2 @@ "description": "Receive webhooks from Shopify with ease",

"types": "index.d.ts",
"scripts": {
"build": "tsc --p tsconfig.json"
},
"sideEffects": false,

@@ -28,3 +25,3 @@ "publishConfig": {

"dependencies": {
"@shopify/network": "^1.6.3",
"@shopify/network": "^1.6.4",
"@types/koa": "^2.0.0",

@@ -34,4 +31,3 @@ "koa-bodyparser": "^4.2.1",

"koa-mount": "^4.0.0",
"safe-compare": "^1.1.3",
"tslib": "^1.14.1"
"safe-compare": "^1.1.3"
},

@@ -38,0 +34,0 @@ "devDependencies": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc