@trigora/sdk 0.2.0 → 0.4.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 +51 -12
- package/dist/defineFlow.d.ts +5 -3
- package/dist/github.d.ts +8 -0
- package/dist/github.js +120 -0
- package/dist/stripe.d.ts +9 -0
- package/dist/stripe.js +147 -0
- package/dist/webhookSignature.d.ts +12 -0
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -25,9 +25,7 @@ export default defineFlow({
|
|
|
25
25
|
id: 'hello',
|
|
26
26
|
trigger: { type: 'manual' },
|
|
27
27
|
async run(event, ctx) {
|
|
28
|
-
await ctx.log.info('Hello from Trigora',
|
|
29
|
-
payload: event.payload,
|
|
30
|
-
});
|
|
28
|
+
await ctx.log.info('Hello from Trigora', event.payload);
|
|
31
29
|
},
|
|
32
30
|
});
|
|
33
31
|
```
|
|
@@ -75,9 +73,7 @@ export default defineFlow({
|
|
|
75
73
|
id: 'hello',
|
|
76
74
|
trigger: { type: 'manual' },
|
|
77
75
|
async run(event, ctx) {
|
|
78
|
-
await ctx.log.info('Triggered manually',
|
|
79
|
-
payload: event.payload,
|
|
80
|
-
});
|
|
76
|
+
await ctx.log.info('Triggered manually', event.payload);
|
|
81
77
|
},
|
|
82
78
|
});
|
|
83
79
|
```
|
|
@@ -115,9 +111,7 @@ export default defineFlow({
|
|
|
115
111
|
id: 'nightly-sync',
|
|
116
112
|
trigger: { type: 'cron', cron: '0 2 * * *' },
|
|
117
113
|
async run(event, ctx) {
|
|
118
|
-
await ctx.log.info('Nightly sync started',
|
|
119
|
-
payload: event.payload,
|
|
120
|
-
});
|
|
114
|
+
await ctx.log.info('Nightly sync started', event.payload);
|
|
121
115
|
},
|
|
122
116
|
});
|
|
123
117
|
```
|
|
@@ -170,15 +164,55 @@ This matches how Trigora uses them:
|
|
|
170
164
|
Flows receive an event object:
|
|
171
165
|
|
|
172
166
|
```ts
|
|
173
|
-
type FlowEvent<TPayload =
|
|
167
|
+
type FlowEvent<TPayload = JsonValue> = {
|
|
174
168
|
id: string;
|
|
175
169
|
type: string;
|
|
176
170
|
timestamp: string;
|
|
177
171
|
payload: TPayload;
|
|
172
|
+
request?: {
|
|
173
|
+
headers: Record<string, string>;
|
|
174
|
+
method: string;
|
|
175
|
+
url: string;
|
|
176
|
+
rawBody: string;
|
|
177
|
+
};
|
|
178
178
|
};
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
In local
|
|
181
|
+
In local manual runs, the payload comes from your JSON payload file when one is provided. In local webhook dev and deployed webhook flows, the payload is the parsed JSON request body.
|
|
182
|
+
|
|
183
|
+
If you do not provide your own payload type, `event.payload` defaults to `JsonValue`.
|
|
184
|
+
|
|
185
|
+
For webhook flows, request metadata may also be available on `event.request`, including headers, method, URL, and the raw request body. `event.request.rawBody` is useful when you want to verify webhook signatures yourself.
|
|
186
|
+
|
|
187
|
+
## Stripe Webhooks
|
|
188
|
+
|
|
189
|
+
Trigora includes a small helper for verifying Stripe webhook signatures:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { verifyStripeWebhook } from '@trigora/sdk/stripe';
|
|
193
|
+
|
|
194
|
+
const stripeEvent = await verifyStripeWebhook(event, {
|
|
195
|
+
secret: ctx.env.STRIPE_WEBHOOK_SECRET,
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
This verifies against `event.request.rawBody` and the `Stripe-Signature` header.
|
|
200
|
+
|
|
201
|
+
Production Stripe webhooks should verify signatures before trusting payloads.
|
|
202
|
+
|
|
203
|
+
## GitHub Webhooks
|
|
204
|
+
|
|
205
|
+
Trigora includes a small helper for verifying GitHub webhook signatures:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import { verifyGitHubWebhook } from '@trigora/sdk/github';
|
|
209
|
+
|
|
210
|
+
const githubEvent = await verifyGitHubWebhook(event, {
|
|
211
|
+
secret: ctx.env.GITHUB_WEBHOOK_SECRET,
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
This verifies against `event.request.rawBody` and the `X-Hub-Signature-256` header.
|
|
182
216
|
|
|
183
217
|
## Context
|
|
184
218
|
|
|
@@ -192,6 +226,8 @@ ctx.log.error('message')
|
|
|
192
226
|
ctx.env
|
|
193
227
|
```
|
|
194
228
|
|
|
229
|
+
In local CLI runs, `ctx.env` includes the process environment after loading `.env` and `.env.local`, with existing shell variables taking precedence.
|
|
230
|
+
|
|
195
231
|
The exact exported type is `FlowContext<TEnv>`.
|
|
196
232
|
|
|
197
233
|
## Exported Types
|
|
@@ -211,7 +247,10 @@ That means most flow authors can stay entirely within `@trigora/sdk`.
|
|
|
211
247
|
## Typical Workflow
|
|
212
248
|
|
|
213
249
|
1. Define a flow with `defineFlow`
|
|
214
|
-
2. Run it locally with
|
|
250
|
+
2. Run it locally with:
|
|
251
|
+
- `trigora trigger hello --payload payload.json` for one-off runs
|
|
252
|
+
- `trigora dev hello --payload payload.json` for manual or payload watch mode
|
|
253
|
+
- `trigora dev stripe-checkout` for a local webhook server
|
|
215
254
|
3. Deploy webhook flows with `trigora deploy`
|
|
216
255
|
4. Manage hosted flows with `trigora flows`
|
|
217
256
|
|
package/dist/defineFlow.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CronFlowDefinition, JsonValue, ManualFlowDefinition, WebhookFlowDefinition } from '@trigora/contracts';
|
|
2
2
|
/**
|
|
3
3
|
* Define a Trigora flow.
|
|
4
4
|
*
|
|
@@ -23,10 +23,12 @@ import type { FlowDefinition, Trigger } from '@trigora/contracts';
|
|
|
23
23
|
* id: 'hello',
|
|
24
24
|
* trigger: { type: 'webhook' },
|
|
25
25
|
* async run(event, ctx) {
|
|
26
|
-
* await ctx.log.info('Received event',
|
|
26
|
+
* await ctx.log.info('Received event', event.payload);
|
|
27
27
|
* return { ok: true };
|
|
28
28
|
* },
|
|
29
29
|
* });
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
export declare function defineFlow<TPayload =
|
|
32
|
+
export declare function defineFlow<TPayload = JsonValue, TEnv extends Record<string, string> = Record<string, string>>(flow: ManualFlowDefinition<TPayload, TEnv>): ManualFlowDefinition<TPayload, TEnv>;
|
|
33
|
+
export declare function defineFlow<TPayload = JsonValue, TEnv extends Record<string, string> = Record<string, string>>(flow: WebhookFlowDefinition<TPayload, TEnv>): WebhookFlowDefinition<TPayload, TEnv>;
|
|
34
|
+
export declare function defineFlow<TPayload = JsonValue, TEnv extends Record<string, string> = Record<string, string>>(flow: CronFlowDefinition<TPayload, TEnv>): CronFlowDefinition<TPayload, TEnv>;
|
package/dist/github.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FlowEvent } from '@trigora/contracts';
|
|
2
|
+
export type VerifyGitHubWebhookOptions = {
|
|
3
|
+
secret: string | undefined;
|
|
4
|
+
};
|
|
5
|
+
export declare class GitHubWebhookVerificationError extends Error {
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function verifyGitHubWebhook<T = unknown>(event: FlowEvent, options: VerifyGitHubWebhookOptions): Promise<T>;
|
package/dist/github.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// src/webhookSignature.ts
|
|
2
|
+
function encodeUtf8(value) {
|
|
3
|
+
return new TextEncoder().encode(value);
|
|
4
|
+
}
|
|
5
|
+
function toArrayBuffer(bytes) {
|
|
6
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
7
|
+
}
|
|
8
|
+
function bytesToHex(bytes) {
|
|
9
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
10
|
+
}
|
|
11
|
+
function getHeaderCaseInsensitive(headers, headerName) {
|
|
12
|
+
const normalizedName = headerName.toLowerCase();
|
|
13
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
14
|
+
if (key.toLowerCase() === normalizedName) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
function isHexString(value) {
|
|
21
|
+
return value.length > 0 && value.length % 2 === 0 && /^[0-9a-f]+$/i.test(value);
|
|
22
|
+
}
|
|
23
|
+
function timingSafeEqualStrings(left, right) {
|
|
24
|
+
const leftBytes = encodeUtf8(left);
|
|
25
|
+
const rightBytes = encodeUtf8(right);
|
|
26
|
+
const length = Math.max(leftBytes.length, rightBytes.length);
|
|
27
|
+
let difference = leftBytes.length === rightBytes.length ? 0 : 1;
|
|
28
|
+
for (let index = 0; index < length; index += 1) {
|
|
29
|
+
difference |= (leftBytes[index] ?? 0) ^ (rightBytes[index] ?? 0);
|
|
30
|
+
}
|
|
31
|
+
return difference === 0;
|
|
32
|
+
}
|
|
33
|
+
async function computeHmacSha256Hex(secret, signedPayload) {
|
|
34
|
+
const subtle = globalThis.crypto?.subtle;
|
|
35
|
+
if (!subtle) {
|
|
36
|
+
throw new Error("Web Crypto API is unavailable in this runtime.");
|
|
37
|
+
}
|
|
38
|
+
const key = await subtle.importKey(
|
|
39
|
+
"raw",
|
|
40
|
+
toArrayBuffer(encodeUtf8(secret)),
|
|
41
|
+
{
|
|
42
|
+
name: "HMAC",
|
|
43
|
+
hash: "SHA-256"
|
|
44
|
+
},
|
|
45
|
+
false,
|
|
46
|
+
["sign"]
|
|
47
|
+
);
|
|
48
|
+
const signature = await subtle.sign("HMAC", key, toArrayBuffer(encodeUtf8(signedPayload)));
|
|
49
|
+
return bytesToHex(new Uint8Array(signature));
|
|
50
|
+
}
|
|
51
|
+
function requireWebhookRequestParts(event, providerName) {
|
|
52
|
+
if (!event.request || typeof event.request.headers !== "object" || event.request.headers === null) {
|
|
53
|
+
throw new Error(`${providerName} webhook request metadata is required.`);
|
|
54
|
+
}
|
|
55
|
+
if (typeof event.request.rawBody !== "string") {
|
|
56
|
+
throw new Error(`${providerName} webhook raw body is required.`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
headers: event.request.headers,
|
|
60
|
+
rawBody: event.request.rawBody
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/github.ts
|
|
65
|
+
var GITHUB_SIGNATURE_HEADER = "x-hub-signature-256";
|
|
66
|
+
var GITHUB_SIGNATURE_PREFIX = "sha256=";
|
|
67
|
+
var GitHubWebhookVerificationError = class extends Error {
|
|
68
|
+
name = "GitHubWebhookVerificationError";
|
|
69
|
+
};
|
|
70
|
+
function createVerificationError(message) {
|
|
71
|
+
return new GitHubWebhookVerificationError(message);
|
|
72
|
+
}
|
|
73
|
+
function parseGitHubSignatureHeader(headerValue) {
|
|
74
|
+
const normalizedValue = headerValue.trim();
|
|
75
|
+
if (!normalizedValue.toLowerCase().startsWith(GITHUB_SIGNATURE_PREFIX)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const signature = normalizedValue.slice(GITHUB_SIGNATURE_PREFIX.length).trim().toLowerCase();
|
|
79
|
+
if (!isHexString(signature)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return signature;
|
|
83
|
+
}
|
|
84
|
+
async function verifyGitHubWebhook(event, options) {
|
|
85
|
+
if (!options.secret || options.secret.trim().length === 0) {
|
|
86
|
+
throw createVerificationError("A GitHub webhook secret is required.");
|
|
87
|
+
}
|
|
88
|
+
let requestParts;
|
|
89
|
+
try {
|
|
90
|
+
requestParts = requireWebhookRequestParts(event, "GitHub");
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw createVerificationError(error instanceof Error ? error.message : String(error));
|
|
93
|
+
}
|
|
94
|
+
const signatureHeader = getHeaderCaseInsensitive(requestParts.headers, GITHUB_SIGNATURE_HEADER);
|
|
95
|
+
if (!signatureHeader) {
|
|
96
|
+
throw createVerificationError("X-Hub-Signature-256 header is required.");
|
|
97
|
+
}
|
|
98
|
+
const parsedSignature = parseGitHubSignatureHeader(signatureHeader);
|
|
99
|
+
if (!parsedSignature) {
|
|
100
|
+
throw createVerificationError("X-Hub-Signature-256 header is invalid.");
|
|
101
|
+
}
|
|
102
|
+
let expectedSignature;
|
|
103
|
+
try {
|
|
104
|
+
expectedSignature = await computeHmacSha256Hex(options.secret, requestParts.rawBody);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw createVerificationError(error instanceof Error ? error.message : String(error));
|
|
107
|
+
}
|
|
108
|
+
if (!timingSafeEqualStrings(parsedSignature, expectedSignature)) {
|
|
109
|
+
throw createVerificationError("GitHub webhook signature did not match.");
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(requestParts.rawBody);
|
|
113
|
+
} catch {
|
|
114
|
+
throw createVerificationError("GitHub webhook body contained invalid JSON.");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export {
|
|
118
|
+
GitHubWebhookVerificationError,
|
|
119
|
+
verifyGitHubWebhook
|
|
120
|
+
};
|
package/dist/stripe.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FlowEvent } from '@trigora/contracts';
|
|
2
|
+
export type VerifyStripeWebhookOptions = {
|
|
3
|
+
secret: string | undefined;
|
|
4
|
+
toleranceSeconds?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare class StripeWebhookVerificationError extends Error {
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function verifyStripeWebhook<T = unknown>(event: FlowEvent, options: VerifyStripeWebhookOptions): Promise<T>;
|
package/dist/stripe.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// src/webhookSignature.ts
|
|
2
|
+
function encodeUtf8(value) {
|
|
3
|
+
return new TextEncoder().encode(value);
|
|
4
|
+
}
|
|
5
|
+
function toArrayBuffer(bytes) {
|
|
6
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
7
|
+
}
|
|
8
|
+
function bytesToHex(bytes) {
|
|
9
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
10
|
+
}
|
|
11
|
+
function getHeaderCaseInsensitive(headers, headerName) {
|
|
12
|
+
const normalizedName = headerName.toLowerCase();
|
|
13
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
14
|
+
if (key.toLowerCase() === normalizedName) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
function isHexString(value) {
|
|
21
|
+
return value.length > 0 && value.length % 2 === 0 && /^[0-9a-f]+$/i.test(value);
|
|
22
|
+
}
|
|
23
|
+
function timingSafeEqualStrings(left, right) {
|
|
24
|
+
const leftBytes = encodeUtf8(left);
|
|
25
|
+
const rightBytes = encodeUtf8(right);
|
|
26
|
+
const length = Math.max(leftBytes.length, rightBytes.length);
|
|
27
|
+
let difference = leftBytes.length === rightBytes.length ? 0 : 1;
|
|
28
|
+
for (let index = 0; index < length; index += 1) {
|
|
29
|
+
difference |= (leftBytes[index] ?? 0) ^ (rightBytes[index] ?? 0);
|
|
30
|
+
}
|
|
31
|
+
return difference === 0;
|
|
32
|
+
}
|
|
33
|
+
async function computeHmacSha256Hex(secret, signedPayload) {
|
|
34
|
+
const subtle = globalThis.crypto?.subtle;
|
|
35
|
+
if (!subtle) {
|
|
36
|
+
throw new Error("Web Crypto API is unavailable in this runtime.");
|
|
37
|
+
}
|
|
38
|
+
const key = await subtle.importKey(
|
|
39
|
+
"raw",
|
|
40
|
+
toArrayBuffer(encodeUtf8(secret)),
|
|
41
|
+
{
|
|
42
|
+
name: "HMAC",
|
|
43
|
+
hash: "SHA-256"
|
|
44
|
+
},
|
|
45
|
+
false,
|
|
46
|
+
["sign"]
|
|
47
|
+
);
|
|
48
|
+
const signature = await subtle.sign("HMAC", key, toArrayBuffer(encodeUtf8(signedPayload)));
|
|
49
|
+
return bytesToHex(new Uint8Array(signature));
|
|
50
|
+
}
|
|
51
|
+
function requireWebhookRequestParts(event, providerName) {
|
|
52
|
+
if (!event.request || typeof event.request.headers !== "object" || event.request.headers === null) {
|
|
53
|
+
throw new Error(`${providerName} webhook request metadata is required.`);
|
|
54
|
+
}
|
|
55
|
+
if (typeof event.request.rawBody !== "string") {
|
|
56
|
+
throw new Error(`${providerName} webhook raw body is required.`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
headers: event.request.headers,
|
|
60
|
+
rawBody: event.request.rawBody
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/stripe.ts
|
|
65
|
+
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
66
|
+
var STRIPE_SIGNATURE_HEADER = "stripe-signature";
|
|
67
|
+
var StripeWebhookVerificationError = class extends Error {
|
|
68
|
+
name = "StripeWebhookVerificationError";
|
|
69
|
+
};
|
|
70
|
+
function createVerificationError(message) {
|
|
71
|
+
return new StripeWebhookVerificationError(message);
|
|
72
|
+
}
|
|
73
|
+
function parseStripeSignatureHeader(headerValue) {
|
|
74
|
+
let timestamp;
|
|
75
|
+
const signatures = [];
|
|
76
|
+
for (const part of headerValue.split(",")) {
|
|
77
|
+
const separatorIndex = part.indexOf("=");
|
|
78
|
+
if (separatorIndex <= 0) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const key = part.slice(0, separatorIndex).trim();
|
|
82
|
+
const value = part.slice(separatorIndex + 1).trim();
|
|
83
|
+
if (key === "t") {
|
|
84
|
+
const parsedTimestamp = Number(value);
|
|
85
|
+
if (!Number.isInteger(parsedTimestamp)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
timestamp = parsedTimestamp;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (key === "v1" && isHexString(value)) {
|
|
92
|
+
signatures.push(value.toLowerCase());
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (timestamp === void 0 || signatures.length === 0) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return { timestamp, signatures };
|
|
99
|
+
}
|
|
100
|
+
async function verifyStripeWebhook(event, options) {
|
|
101
|
+
if (!options.secret || options.secret.trim().length === 0) {
|
|
102
|
+
throw createVerificationError("A Stripe webhook secret is required.");
|
|
103
|
+
}
|
|
104
|
+
let requestParts;
|
|
105
|
+
try {
|
|
106
|
+
requestParts = requireWebhookRequestParts(event, "Stripe");
|
|
107
|
+
} catch (error) {
|
|
108
|
+
throw createVerificationError(error instanceof Error ? error.message : String(error));
|
|
109
|
+
}
|
|
110
|
+
const signatureHeader = getHeaderCaseInsensitive(requestParts.headers, STRIPE_SIGNATURE_HEADER);
|
|
111
|
+
if (!signatureHeader) {
|
|
112
|
+
throw createVerificationError("Stripe-Signature header is required.");
|
|
113
|
+
}
|
|
114
|
+
const parsedHeader = parseStripeSignatureHeader(signatureHeader);
|
|
115
|
+
if (!parsedHeader) {
|
|
116
|
+
throw createVerificationError("Stripe-Signature header is invalid.");
|
|
117
|
+
}
|
|
118
|
+
const toleranceSeconds = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
|
|
119
|
+
if (toleranceSeconds !== 0) {
|
|
120
|
+
const currentTimestamp = Math.floor(Date.now() / 1e3);
|
|
121
|
+
if (Math.abs(currentTimestamp - parsedHeader.timestamp) > toleranceSeconds) {
|
|
122
|
+
throw createVerificationError("Stripe webhook timestamp is outside the allowed tolerance.");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const signedPayload = `${parsedHeader.timestamp}.${requestParts.rawBody}`;
|
|
126
|
+
let expectedSignature;
|
|
127
|
+
try {
|
|
128
|
+
expectedSignature = await computeHmacSha256Hex(options.secret, signedPayload);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw createVerificationError(error instanceof Error ? error.message : String(error));
|
|
131
|
+
}
|
|
132
|
+
const matches = parsedHeader.signatures.some(
|
|
133
|
+
(signature) => timingSafeEqualStrings(signature, expectedSignature)
|
|
134
|
+
);
|
|
135
|
+
if (!matches) {
|
|
136
|
+
throw createVerificationError("Stripe webhook signature did not match.");
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(requestParts.rawBody);
|
|
140
|
+
} catch {
|
|
141
|
+
throw createVerificationError("Stripe webhook body contained invalid JSON.");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export {
|
|
145
|
+
StripeWebhookVerificationError,
|
|
146
|
+
verifyStripeWebhook
|
|
147
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FlowEvent } from '@trigora/contracts';
|
|
2
|
+
export declare function encodeUtf8(value: string): Uint8Array;
|
|
3
|
+
export declare function toArrayBuffer(bytes: Uint8Array): ArrayBuffer;
|
|
4
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
5
|
+
export declare function getHeaderCaseInsensitive(headers: Record<string, string>, headerName: string): string | undefined;
|
|
6
|
+
export declare function isHexString(value: string): boolean;
|
|
7
|
+
export declare function timingSafeEqualStrings(left: string, right: string): boolean;
|
|
8
|
+
export declare function computeHmacSha256Hex(secret: string, signedPayload: string): Promise<string>;
|
|
9
|
+
export declare function requireWebhookRequestParts(event: FlowEvent, providerName: string): {
|
|
10
|
+
headers: Record<string, string>;
|
|
11
|
+
rawBody: string;
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trigora/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/trigora-dev/trigora"
|
|
@@ -28,6 +28,14 @@
|
|
|
28
28
|
".": {
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"default": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./github": {
|
|
33
|
+
"types": "./dist/github.d.ts",
|
|
34
|
+
"default": "./dist/github.js"
|
|
35
|
+
},
|
|
36
|
+
"./stripe": {
|
|
37
|
+
"types": "./dist/stripe.d.ts",
|
|
38
|
+
"default": "./dist/stripe.js"
|
|
31
39
|
}
|
|
32
40
|
},
|
|
33
41
|
"files": [
|
|
@@ -37,7 +45,7 @@
|
|
|
37
45
|
"access": "public"
|
|
38
46
|
},
|
|
39
47
|
"dependencies": {
|
|
40
|
-
"@trigora/contracts": "0.
|
|
48
|
+
"@trigora/contracts": "0.4.1"
|
|
41
49
|
},
|
|
42
50
|
"devDependencies": {
|
|
43
51
|
"tsup": "^8.5.1"
|