hook-sign 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +193 -0
- package/dist/core/hmac.d.ts +3 -0
- package/dist/core/hmac.js +48 -0
- package/dist/express/rawBody.d.ts +2 -0
- package/dist/express/rawBody.js +7 -0
- package/dist/express/shopifyMiddleware.d.ts +7 -0
- package/dist/express/shopifyMiddleware.js +30 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +12 -0
- package/dist/providers/shopify.d.ts +6 -0
- package/dist/providers/shopify.js +10 -0
- package/dist/src/core/hmac.d.ts +3 -0
- package/dist/src/core/hmac.js +15 -0
- package/dist/src/express/rawBody.d.ts +2 -0
- package/dist/src/express/rawBody.js +7 -0
- package/dist/src/express/shopifyMiddleware.d.ts +7 -0
- package/dist/src/express/shopifyMiddleware.js +30 -0
- package/dist/src/providers/shopify.d.ts +6 -0
- package/dist/src/providers/shopify.js +10 -0
- package/dist/test/shopify.test.d.ts +1 -0
- package/dist/test/shopify.test.js +40 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# hook-sign
|
|
2
|
+
|
|
3
|
+
A lightweight webhook signature verification utility for Node.js — with ready-to-use Express middleware.
|
|
4
|
+
Supports Shopify webhook verification (HMAC SHA256) and can be extended for other providers.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- ✅ Verify webhook signature using raw request body (Buffer)
|
|
11
|
+
- ✅ Shopify webhook verification (`X-Shopify-Hmac-Sha256`)
|
|
12
|
+
- ✅ Express middleware included
|
|
13
|
+
- ✅ Timing-safe signature comparison (`crypto.timingSafeEqual`)
|
|
14
|
+
- ✅ TypeScript support
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install hook-sign
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or with Yarn:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
yarn add hook-sign
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### ⚠️ Important Note (Must Read)
|
|
31
|
+
|
|
32
|
+
Webhook providers sign the raw request body bytes, not the parsed JSON object. You must capture the raw request body in Express using:
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
express.json({ verify: rawBodySaver })
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If you skip this, signature verification will fail.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Quick Start (Shopify + Express)
|
|
43
|
+
|
|
44
|
+
### 1. Setup Express raw body capture
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
import express from "express";
|
|
48
|
+
import { rawBodySaver, shopifyWebhookMiddleware } from "hook-sign";
|
|
49
|
+
|
|
50
|
+
const app = express();
|
|
51
|
+
|
|
52
|
+
// Capture raw body (IMPORTANT)
|
|
53
|
+
app.use(
|
|
54
|
+
express.json({
|
|
55
|
+
verify: rawBodySaver,
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Add Shopify webhook route
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
app.post(
|
|
64
|
+
"/webhooks/shopify",
|
|
65
|
+
shopifyWebhookMiddleware({
|
|
66
|
+
secret: process.env.SHOPIFY_SECRET, // Your Shopify App API Secret Key
|
|
67
|
+
}),
|
|
68
|
+
(req, res) => {
|
|
69
|
+
// ✅ Signature verified successfully
|
|
70
|
+
|
|
71
|
+
// Your webhook payload
|
|
72
|
+
console.log("Shopify Webhook Payload:", req.body);
|
|
73
|
+
|
|
74
|
+
res.status(200).send("OK");
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
app.listen(5000, () => console.log("Server running on http://localhost:5000"));
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 3. Environment Variable
|
|
82
|
+
|
|
83
|
+
Your secret should be stored in `.env`:
|
|
84
|
+
|
|
85
|
+
```env
|
|
86
|
+
SHOPIFY_SECRET=your_shopify_app_api_secret_key
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
> ✅ **Shopify secret** = Shopify App API Secret Key (not access token)
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Manual Verification (Without Express Middleware)
|
|
94
|
+
|
|
95
|
+
If you don't want middleware, you can manually verify the signature:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
import { verifyShopifyWebhook } from "hook-sign";
|
|
99
|
+
|
|
100
|
+
const isValid = verifyShopifyWebhook({
|
|
101
|
+
rawBody: req.rawBody, // Buffer
|
|
102
|
+
secret: process.env.SHOPIFY_SECRET,
|
|
103
|
+
hmacHeader: req.headers["x-shopify-hmac-sha256"],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (!isValid) return res.status(401).send("Invalid signature");
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## API Reference
|
|
112
|
+
|
|
113
|
+
### `verifyShopifyWebhook({ rawBody, secret, hmacHeader })`
|
|
114
|
+
|
|
115
|
+
Returns `true` if Shopify HMAC signature is valid.
|
|
116
|
+
|
|
117
|
+
**Example:**
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
verifyShopifyWebhook({
|
|
121
|
+
rawBody: Buffer.from("..."),
|
|
122
|
+
secret: "my_secret",
|
|
123
|
+
hmacHeader: "base64_signature",
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `shopifyWebhookMiddleware({ secret, headerName?, onError? })`
|
|
128
|
+
|
|
129
|
+
Express middleware that automatically validates Shopify signature.
|
|
130
|
+
|
|
131
|
+
**Options:**
|
|
132
|
+
|
|
133
|
+
- `secret` (required): Shopify App Secret Key
|
|
134
|
+
- `headerName` (optional): Custom header name (default: `x-shopify-hmac-sha256`)
|
|
135
|
+
- `onError` (optional): Custom error handler
|
|
136
|
+
|
|
137
|
+
### `rawBodySaver(req, res, buf)`
|
|
138
|
+
|
|
139
|
+
Express verify function used in `express.json()` to store raw body buffer:
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
app.use(express.json({ verify: rawBodySaver }));
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Example: Custom Error Response
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
app.post(
|
|
151
|
+
"/webhooks/shopify",
|
|
152
|
+
shopifyWebhookMiddleware({
|
|
153
|
+
secret: process.env.SHOPIFY_SECRET,
|
|
154
|
+
onError: (req, res) =>
|
|
155
|
+
res.status(401).json({ ok: false, message: "Invalid Shopify signature" }),
|
|
156
|
+
}),
|
|
157
|
+
(req, res) => res.status(200).send("OK")
|
|
158
|
+
);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Troubleshooting
|
|
164
|
+
|
|
165
|
+
### ❌ Error: `RAW_BODY_MISSING`
|
|
166
|
+
|
|
167
|
+
**✅ Fix:** Ensure you added raw body capture before your routes:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
app.use(express.json({ verify: rawBodySaver }));
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### ❌ Signature mismatch even with correct secret
|
|
174
|
+
|
|
175
|
+
**Most common causes:**
|
|
176
|
+
|
|
177
|
+
- Using parsed JSON instead of raw body
|
|
178
|
+
- Running a second body parser that modifies the payload
|
|
179
|
+
- Using wrong Shopify secret (must be App API Secret Key)
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Security Tips
|
|
184
|
+
|
|
185
|
+
- Never store Shopify secret in database
|
|
186
|
+
- Keep secret only in `.env` / server environment variables
|
|
187
|
+
- Always respond quickly to webhooks (Shopify expects 200 OK)
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.computeHmac = computeHmac;
|
|
37
|
+
exports.timingSafeEqual = timingSafeEqual;
|
|
38
|
+
const crypto = __importStar(require("crypto"));
|
|
39
|
+
function computeHmac(rawBody, secret, algorithm = "sha256", encoding = "base64") {
|
|
40
|
+
return crypto.createHmac(algorithm, secret).update(rawBody).digest(encoding);
|
|
41
|
+
}
|
|
42
|
+
function timingSafeEqual(a, b) {
|
|
43
|
+
const aBuf = Buffer.from(a);
|
|
44
|
+
const bBuf = Buffer.from(b);
|
|
45
|
+
if (aBuf.length !== bBuf.length)
|
|
46
|
+
return false;
|
|
47
|
+
return crypto.timingSafeEqual(aBuf, bBuf);
|
|
48
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from "express";
|
|
2
|
+
export type ShopifyMiddlewareOptions = {
|
|
3
|
+
secret: string;
|
|
4
|
+
headerName?: string;
|
|
5
|
+
onError?: (req: Request, res: Response) => void;
|
|
6
|
+
};
|
|
7
|
+
export declare function shopifyWebhookMiddleware(options: ShopifyMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => void | Response<any, Record<string, any>>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shopifyWebhookMiddleware = shopifyWebhookMiddleware;
|
|
4
|
+
const shopify_1 = require("../providers/shopify");
|
|
5
|
+
function shopifyWebhookMiddleware(options) {
|
|
6
|
+
const headerName = options.headerName ?? "x-shopify-hmac-sha256";
|
|
7
|
+
return (req, res, next) => {
|
|
8
|
+
const rawBody = req.rawBody;
|
|
9
|
+
if (!rawBody) {
|
|
10
|
+
// If raw body missing, dev configured body parser wrong
|
|
11
|
+
return res.status(400).json({
|
|
12
|
+
ok: false,
|
|
13
|
+
error: "RAW_BODY_MISSING",
|
|
14
|
+
message: "Raw body is missing. Configure express.json({ verify }) using rawBodySaver.",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const hmacHeader = req.headers[headerName];
|
|
18
|
+
const ok = (0, shopify_1.verifyShopifyWebhook)({
|
|
19
|
+
rawBody,
|
|
20
|
+
secret: options.secret,
|
|
21
|
+
hmacHeader,
|
|
22
|
+
});
|
|
23
|
+
if (!ok) {
|
|
24
|
+
if (options.onError)
|
|
25
|
+
return options.onError(req, res);
|
|
26
|
+
return res.status(401).json({ ok: false, error: "INVALID_SIGNATURE" });
|
|
27
|
+
}
|
|
28
|
+
return next();
|
|
29
|
+
};
|
|
30
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shopifyWebhookMiddleware = exports.rawBodySaver = exports.verifyShopifyWebhook = exports.timingSafeEqual = exports.computeHmac = void 0;
|
|
4
|
+
var hmac_1 = require("./core/hmac");
|
|
5
|
+
Object.defineProperty(exports, "computeHmac", { enumerable: true, get: function () { return hmac_1.computeHmac; } });
|
|
6
|
+
Object.defineProperty(exports, "timingSafeEqual", { enumerable: true, get: function () { return hmac_1.timingSafeEqual; } });
|
|
7
|
+
var shopify_1 = require("./providers/shopify");
|
|
8
|
+
Object.defineProperty(exports, "verifyShopifyWebhook", { enumerable: true, get: function () { return shopify_1.verifyShopifyWebhook; } });
|
|
9
|
+
var rawBody_1 = require("./express/rawBody");
|
|
10
|
+
Object.defineProperty(exports, "rawBodySaver", { enumerable: true, get: function () { return rawBody_1.rawBodySaver; } });
|
|
11
|
+
var shopifyMiddleware_1 = require("./express/shopifyMiddleware");
|
|
12
|
+
Object.defineProperty(exports, "shopifyWebhookMiddleware", { enumerable: true, get: function () { return shopifyMiddleware_1.shopifyWebhookMiddleware; } });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyShopifyWebhook = verifyShopifyWebhook;
|
|
4
|
+
const hmac_1 = require("../core/hmac");
|
|
5
|
+
function verifyShopifyWebhook({ rawBody, secret, hmacHeader, }) {
|
|
6
|
+
if (!hmacHeader)
|
|
7
|
+
return false;
|
|
8
|
+
const expected = (0, hmac_1.computeHmac)(rawBody, secret, "sha256", "base64");
|
|
9
|
+
return (0, hmac_1.timingSafeEqual)(expected, hmacHeader.trim());
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeHmac = computeHmac;
|
|
4
|
+
exports.timingSafeEqual = timingSafeEqual;
|
|
5
|
+
const crypto = require("crypto");
|
|
6
|
+
function computeHmac(rawBody, secret, algorithm = "sha256", encoding = "base64") {
|
|
7
|
+
return crypto.createHmac(algorithm, secret).update(rawBody).digest(encoding);
|
|
8
|
+
}
|
|
9
|
+
function timingSafeEqual(a, b) {
|
|
10
|
+
const aBuf = Buffer.from(a);
|
|
11
|
+
const bBuf = Buffer.from(b);
|
|
12
|
+
if (aBuf.length !== bBuf.length)
|
|
13
|
+
return false;
|
|
14
|
+
return crypto.timingSafeEqual(aBuf, bBuf);
|
|
15
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from "express";
|
|
2
|
+
export type ShopifyMiddlewareOptions = {
|
|
3
|
+
secret: string;
|
|
4
|
+
headerName?: string;
|
|
5
|
+
onError?: (req: Request, res: Response) => void;
|
|
6
|
+
};
|
|
7
|
+
export declare function shopifyWebhookMiddleware(options: ShopifyMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => void | Response<any, Record<string, any>>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shopifyWebhookMiddleware = shopifyWebhookMiddleware;
|
|
4
|
+
const shopify_1 = require("../providers/shopify");
|
|
5
|
+
function shopifyWebhookMiddleware(options) {
|
|
6
|
+
const headerName = options.headerName ?? "x-shopify-hmac-sha256";
|
|
7
|
+
return (req, res, next) => {
|
|
8
|
+
const rawBody = req.rawBody;
|
|
9
|
+
if (!rawBody) {
|
|
10
|
+
// If raw body missing, dev configured body parser wrong
|
|
11
|
+
return res.status(400).json({
|
|
12
|
+
ok: false,
|
|
13
|
+
error: "RAW_BODY_MISSING",
|
|
14
|
+
message: "Raw body is missing. Configure express.json({ verify }) using rawBodySaver.",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const hmacHeader = req.headers[headerName];
|
|
18
|
+
const ok = (0, shopify_1.verifyShopifyWebhook)({
|
|
19
|
+
rawBody,
|
|
20
|
+
secret: options.secret,
|
|
21
|
+
hmacHeader,
|
|
22
|
+
});
|
|
23
|
+
if (!ok) {
|
|
24
|
+
if (options.onError)
|
|
25
|
+
return options.onError(req, res);
|
|
26
|
+
return res.status(401).json({ ok: false, error: "INVALID_SIGNATURE" });
|
|
27
|
+
}
|
|
28
|
+
return next();
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyShopifyWebhook = verifyShopifyWebhook;
|
|
4
|
+
const hmac_1 = require("../core/hmac");
|
|
5
|
+
function verifyShopifyWebhook({ rawBody, secret, hmacHeader, }) {
|
|
6
|
+
if (!hmacHeader)
|
|
7
|
+
return false;
|
|
8
|
+
const expected = (0, hmac_1.computeHmac)(rawBody, secret, "sha256", "base64");
|
|
9
|
+
return (0, hmac_1.timingSafeEqual)(expected, hmacHeader.trim());
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const supertest_1 = require("supertest");
|
|
5
|
+
const express_1 = require("express");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const rawBody_1 = require("../src/express/rawBody");
|
|
8
|
+
const shopifyMiddleware_1 = require("../src/express/shopifyMiddleware");
|
|
9
|
+
(0, vitest_1.describe)("Shopify Express middleware", () => {
|
|
10
|
+
(0, vitest_1.it)("should allow request with valid signature", async () => {
|
|
11
|
+
const secret = "my_secret";
|
|
12
|
+
const app = (0, express_1.default)();
|
|
13
|
+
app.use(express_1.default.json({ verify: rawBody_1.rawBodySaver }));
|
|
14
|
+
app.post("/webhooks/shopify", (0, shopifyMiddleware_1.shopifyWebhookMiddleware)({ secret }), (req, res) => res.status(200).send("ok"));
|
|
15
|
+
const payload = { order_id: 999 };
|
|
16
|
+
// IMPORTANT: sign the exact JSON string
|
|
17
|
+
const raw = Buffer.from(JSON.stringify(payload), "utf8");
|
|
18
|
+
const hmac = crypto_1.default
|
|
19
|
+
.createHmac("sha256", secret)
|
|
20
|
+
.update(raw)
|
|
21
|
+
.digest("base64");
|
|
22
|
+
const res = await (0, supertest_1.default)(app)
|
|
23
|
+
.post("/webhooks/shopify")
|
|
24
|
+
.set("X-Shopify-Hmac-Sha256", hmac)
|
|
25
|
+
.send(payload);
|
|
26
|
+
(0, vitest_1.expect)(res.statusCode).toBe(200);
|
|
27
|
+
(0, vitest_1.expect)(res.text).toBe("ok");
|
|
28
|
+
});
|
|
29
|
+
(0, vitest_1.it)("should reject request with invalid signature", async () => {
|
|
30
|
+
const secret = "my_secret";
|
|
31
|
+
const app = (0, express_1.default)();
|
|
32
|
+
app.use(express_1.default.json({ verify: rawBody_1.rawBodySaver }));
|
|
33
|
+
app.post("/webhooks/shopify", (0, shopifyMiddleware_1.shopifyWebhookMiddleware)({ secret }), (req, res) => res.status(200).send("ok"));
|
|
34
|
+
const res = await (0, supertest_1.default)(app)
|
|
35
|
+
.post("/webhooks/shopify")
|
|
36
|
+
.set("X-Shopify-Hmac-Sha256", "INVALID")
|
|
37
|
+
.send({ order_id: 999 });
|
|
38
|
+
(0, vitest_1.expect)(res.statusCode).toBe(401);
|
|
39
|
+
});
|
|
40
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hook-sign",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Webhook signature verification utilities + Express middleware",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"test": "vitest"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"webhook",
|
|
17
|
+
"shopify",
|
|
18
|
+
"hmac",
|
|
19
|
+
"signature",
|
|
20
|
+
"express"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/rakib-dev1/webhook-sign"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"express": ">=4"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/express": "^4.17.25",
|
|
32
|
+
"@types/node": "^20.19.30",
|
|
33
|
+
"@types/supertest": "^2.0.0",
|
|
34
|
+
"express": "^4.22.1",
|
|
35
|
+
"supertest": "^6.3.0",
|
|
36
|
+
"typescript": "^5.9.3",
|
|
37
|
+
"vitest": "^1.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|