@actuate-media/plugin-commerce 0.0.14 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @actuate-media/plugin-commerce
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d278add: Verify Stripe webhook signatures and lock in the checkout money-mapping.
8
+
9
+ The webhook handler previously "verified" a payload by only checking that the
10
+ signature and secret strings were non-empty before parsing it — so a forged POST
11
+ with any non-empty `Stripe-Signature` would be processed as a real event (a fake
12
+ `payment_intent.succeeded` is a direct fraud risk). `handleStripeWebhook` now
13
+ performs real HMAC-SHA256 verification via a new exported `verifyStripeSignature`
14
+ (a dependency-free equivalent of `stripe.webhooks.constructEvent`: signed-payload
15
+ HMAC, constant-time compare, multi-`v1` key rotation, and timestamp replay
16
+ tolerance).
17
+
18
+ The money-critical checkout mapping (dollar→integer-cents rounding for line items
19
+ and shipping, discounts, metadata) is extracted into a pure, exported
20
+ `buildCheckoutSessionParams` so it is unit-tested independently of the
21
+ (still-stubbed) Stripe API call. Adds 15 behavioral tests covering signature
22
+ accept/reject/replay/rotation, event routing, and cents/shipping/discount mapping.
23
+
3
24
  ## 0.0.14
4
25
 
5
26
  ### Patch Changes
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=checkout.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/checkout.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { buildCheckoutSessionParams } from '../stripe/checkout.js';
3
+ const base = { successUrl: '/success', cancelUrl: '/cancel' };
4
+ const lineItem = (over = {}) => ({
5
+ productId: 'p1',
6
+ name: 'Widget',
7
+ quantity: 1,
8
+ unitPrice: 5,
9
+ ...over,
10
+ });
11
+ describe('buildCheckoutSessionParams', () => {
12
+ it('converts dollar unit prices to integer cents (float-safe)', () => {
13
+ const params = buildCheckoutSessionParams({
14
+ ...base,
15
+ lineItems: [lineItem({ unitPrice: 19.99, quantity: 2 })],
16
+ });
17
+ const li = params.line_items[0];
18
+ expect(li.price_data.unit_amount).toBe(1999); // 19.99*100 = 1998.999… → round
19
+ expect(li.quantity).toBe(2);
20
+ expect(params.mode).toBe('payment');
21
+ expect(params.success_url).toBe('/success');
22
+ });
23
+ it('rounds a value that floats below the integer (10.1 → 1010c)', () => {
24
+ const params = buildCheckoutSessionParams({
25
+ ...base,
26
+ lineItems: [lineItem({ unitPrice: 10.1 })],
27
+ });
28
+ expect(params.line_items[0].price_data.unit_amount).toBe(1010);
29
+ });
30
+ it('includes variant metadata + image only when present', () => {
31
+ const withVariant = buildCheckoutSessionParams({
32
+ ...base,
33
+ lineItems: [lineItem({ variantId: 'v1', image: 'https://img/x.png' })],
34
+ });
35
+ const pd = withVariant.line_items[0].price_data.product_data;
36
+ expect(pd.metadata).toEqual({ productId: 'p1', variantId: 'v1' });
37
+ expect(pd.images).toEqual(['https://img/x.png']);
38
+ const bare = buildCheckoutSessionParams({ ...base, lineItems: [lineItem()] });
39
+ const pd2 = bare.line_items[0].price_data.product_data;
40
+ expect(pd2.metadata).toEqual({ productId: 'p1' });
41
+ expect(pd2.images).toEqual([]);
42
+ });
43
+ it('maps shipping options to fixed_amount cents', () => {
44
+ const params = buildCheckoutSessionParams({
45
+ ...base,
46
+ lineItems: [lineItem()],
47
+ shippingOptions: [{ id: 's1', label: 'Express', amount: 12.5 }],
48
+ });
49
+ const so = params.shipping_options[0];
50
+ expect(so.shipping_rate_data.fixed_amount).toEqual({ amount: 1250, currency: 'usd' });
51
+ expect(so.shipping_rate_data.display_name).toBe('Express');
52
+ });
53
+ it('adds discounts + customer_email only when provided', () => {
54
+ const full = buildCheckoutSessionParams({
55
+ ...base,
56
+ lineItems: [lineItem()],
57
+ discountCode: 'SAVE10',
58
+ customerEmail: 'a@b.com',
59
+ });
60
+ expect(full.discounts).toEqual([{ coupon: 'SAVE10' }]);
61
+ expect(full.customer_email).toBe('a@b.com');
62
+ const bare = buildCheckoutSessionParams({ ...base, lineItems: [lineItem()] });
63
+ expect(bare.discounts).toBeUndefined();
64
+ expect(bare.customer_email).toBeUndefined();
65
+ expect(bare.shipping_options).toBeUndefined();
66
+ });
67
+ });
68
+ //# sourceMappingURL=checkout.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout.test.js","sourceRoot":"","sources":["../../src/__tests__/checkout.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=webhooks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/webhooks.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,71 @@
1
+ import { createHmac } from 'node:crypto';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { handleStripeWebhook, verifyStripeSignature } from '../stripe/webhooks.js';
4
+ const SECRET = 'whsec_test';
5
+ const NOW = 1_700_000_000_000; // fixed ms for deterministic replay checks
6
+ const NOW_SEC = Math.floor(NOW / 1000);
7
+ function sign(payload, ts = NOW_SEC, secret = SECRET) {
8
+ const sig = createHmac('sha256', secret).update(`${ts}.${payload}`).digest('hex');
9
+ return `t=${ts},v1=${sig}`;
10
+ }
11
+ describe('verifyStripeSignature', () => {
12
+ it('accepts a valid signature within the tolerance window', () => {
13
+ const payload = JSON.stringify({ type: 'x' });
14
+ expect(() => verifyStripeSignature(payload, sign(payload), SECRET, { now: NOW })).not.toThrow();
15
+ });
16
+ it('rejects a tampered payload', () => {
17
+ const header = sign('{"amount":100}');
18
+ expect(() => verifyStripeSignature('{"amount":999}', header, SECRET, { now: NOW })).toThrow(/mismatch/);
19
+ });
20
+ it('rejects a signature made with a different secret', () => {
21
+ const payload = '{}';
22
+ expect(() => verifyStripeSignature(payload, sign(payload, NOW_SEC, 'whsec_other'), SECRET, { now: NOW })).toThrow(/mismatch/);
23
+ });
24
+ it('rejects an out-of-tolerance timestamp (replay protection)', () => {
25
+ const payload = '{}';
26
+ expect(() => verifyStripeSignature(payload, sign(payload, NOW_SEC - 10_000), SECRET, { now: NOW })).toThrow(/tolerance/);
27
+ });
28
+ it('rejects a missing signature or missing secret', () => {
29
+ expect(() => verifyStripeSignature('{}', '', SECRET, { now: NOW })).toThrow(/Missing webhook signature/);
30
+ expect(() => verifyStripeSignature('{}', sign('{}'), '', { now: NOW })).toThrow(/Missing webhook signing secret/);
31
+ });
32
+ it('rejects a malformed signature header', () => {
33
+ expect(() => verifyStripeSignature('{}', 'garbage', SECRET, { now: NOW })).toThrow(/Malformed/);
34
+ });
35
+ it('accepts when any of multiple v1 signatures matches (key rotation)', () => {
36
+ const payload = '{}';
37
+ const good = createHmac('sha256', SECRET).update(`${NOW_SEC}.${payload}`).digest('hex');
38
+ const header = `t=${NOW_SEC},v1=deadbeef,v1=${good}`;
39
+ expect(() => verifyStripeSignature(payload, header, SECRET, { now: NOW })).not.toThrow();
40
+ });
41
+ });
42
+ describe('handleStripeWebhook', () => {
43
+ function evt(type, object = {}) {
44
+ const payload = JSON.stringify({ type, data: { object } });
45
+ return { payload, header: sign(payload) };
46
+ }
47
+ it('routes each known event type to handled=true', async () => {
48
+ for (const type of [
49
+ 'checkout.session.completed',
50
+ 'payment_intent.succeeded',
51
+ 'charge.refunded',
52
+ ]) {
53
+ const { payload, header } = evt(type);
54
+ const res = await handleStripeWebhook(payload, header, SECRET, { now: NOW });
55
+ expect(res).toMatchObject({ handled: true, event: type });
56
+ }
57
+ });
58
+ it('returns handled=false for an unknown but authentic event', async () => {
59
+ const { payload, header } = evt('customer.created');
60
+ const res = await handleStripeWebhook(payload, header, SECRET, { now: NOW });
61
+ expect(res).toEqual({ handled: false, event: 'customer.created' });
62
+ });
63
+ it('does NOT process a forged event (invalid signature)', async () => {
64
+ // A fake "payment succeeded" — this must be rejected, not handled.
65
+ const payload = JSON.stringify({ type: 'payment_intent.succeeded', data: { object: {} } });
66
+ const res = await handleStripeWebhook(payload, `t=${NOW_SEC},v1=forged`, SECRET, { now: NOW });
67
+ expect(res.handled).toBe(false);
68
+ expect(res.error).toMatch(/verification failed/i);
69
+ });
70
+ });
71
+ //# sourceMappingURL=webhooks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.test.js","sourceRoot":"","sources":["../../src/__tests__/webhooks.test.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,16 @@ export interface CheckoutSession {
24
24
  url: string;
25
25
  expiresAt: Date;
26
26
  }
27
+ /**
28
+ * Build the Stripe Checkout Session params from cart line items and options.
29
+ *
30
+ * Pure and money-critical: it converts dollar amounts to integer cents
31
+ * (`Math.round(x * 100)`) for every line item and shipping rate. Extracted from
32
+ * `createCheckoutSession` so this mapping is unit-testable independently of the
33
+ * (still-stubbed) Stripe API call — a cents-rounding or field-shape bug here is
34
+ * a real overcharge/undercharge once the SDK is wired.
35
+ */
36
+ export declare function buildCheckoutSessionParams(options: CheckoutSessionOptions): Record<string, unknown>;
27
37
  /** Creates a Stripe Checkout session from cart line items and configuration. */
28
38
  export declare function createCheckoutSession(options: CheckoutSessionOptions): Promise<CheckoutSession>;
29
39
  //# sourceMappingURL=checkout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../src/stripe/checkout.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,gBAAgB,EAAE,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,IAAI,CAAA;CAChB;AAED,gFAAgF;AAChF,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC,CAgD1B"}
1
+ {"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../src/stripe/checkout.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,gBAAgB,EAAE,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,IAAI,CAAA;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,sBAAsB,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwCzB;AAED,gFAAgF;AAChF,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC,CAa1B"}
@@ -1,5 +1,13 @@
1
- /** Creates a Stripe Checkout session from cart line items and configuration. */
2
- export async function createCheckoutSession(options) {
1
+ /**
2
+ * Build the Stripe Checkout Session params from cart line items and options.
3
+ *
4
+ * Pure and money-critical: it converts dollar amounts to integer cents
5
+ * (`Math.round(x * 100)`) for every line item and shipping rate. Extracted from
6
+ * `createCheckoutSession` so this mapping is unit-testable independently of the
7
+ * (still-stubbed) Stripe API call — a cents-rounding or field-shape bug here is
8
+ * a real overcharge/undercharge once the SDK is wired.
9
+ */
10
+ export function buildCheckoutSessionParams(options) {
3
11
  const stripeLineItems = options.lineItems.map((item) => ({
4
12
  price_data: {
5
13
  currency: 'usd',
@@ -34,7 +42,13 @@ export async function createCheckoutSession(options) {
34
42
  if (options.discountCode) {
35
43
  sessionParams['discounts'] = [{ coupon: options.discountCode }];
36
44
  }
45
+ return sessionParams;
46
+ }
47
+ /** Creates a Stripe Checkout session from cart line items and configuration. */
48
+ export async function createCheckoutSession(options) {
49
+ const sessionParams = buildCheckoutSessionParams(options);
37
50
  // TODO: Call Stripe SDK — stripe.checkout.sessions.create(sessionParams)
51
+ void sessionParams;
38
52
  const expiresAt = new Date();
39
53
  expiresAt.setMinutes(expiresAt.getMinutes() + 30);
40
54
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"checkout.js","sourceRoot":"","sources":["../../src/stripe/checkout.ts"],"names":[],"mappings":"AA8BA,gFAAgF;AAChF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAA+B;IAE/B,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvD,UAAU,EAAE;YACV,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE;gBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;gBACtC,QAAQ,EAAE;oBACR,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACzD;aACF;YACD,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;SAC9C;QACD,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC,CAAC,CAAA;IAEH,MAAM,aAAa,GAA4B;QAC7C,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,eAAe;QAC3B,WAAW,EAAE,OAAO,CAAC,UAAU;QAC/B,UAAU,EAAE,OAAO,CAAC,SAAS;QAC7B,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5E,CAAA;IAED,IAAI,OAAO,CAAC,eAAe,EAAE,MAAM,EAAE,CAAC;QACpC,aAAa,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACxE,kBAAkB,EAAE;gBAClB,YAAY,EAAE,GAAG,CAAC,KAAK;gBACvB,IAAI,EAAE,cAAc;gBACpB,YAAY,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;aACxE;SACF,CAAC,CAAC,CAAA;IACL,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACzB,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IACjE,CAAC;IAED,yEAAyE;IACzE,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;IAC5B,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAA;IAEjD,OAAO;QACL,EAAE,EAAE,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE;QAC3B,GAAG,EAAE,2CAA2C,IAAI,CAAC,GAAG,EAAE,EAAE;QAC5D,SAAS;KACV,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"checkout.js","sourceRoot":"","sources":["../../src/stripe/checkout.ts"],"names":[],"mappings":"AA8BA;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAA+B;IAE/B,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvD,UAAU,EAAE;YACV,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE;gBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;gBACtC,QAAQ,EAAE;oBACR,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACzD;aACF;YACD,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;SAC9C;QACD,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC,CAAC,CAAA;IAEH,MAAM,aAAa,GAA4B;QAC7C,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,eAAe;QAC3B,WAAW,EAAE,OAAO,CAAC,UAAU;QAC/B,UAAU,EAAE,OAAO,CAAC,SAAS;QAC7B,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5E,CAAA;IAED,IAAI,OAAO,CAAC,eAAe,EAAE,MAAM,EAAE,CAAC;QACpC,aAAa,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACxE,kBAAkB,EAAE;gBAClB,YAAY,EAAE,GAAG,CAAC,KAAK;gBACvB,IAAI,EAAE,cAAc;gBACpB,YAAY,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;aACxE;SACF,CAAC,CAAC,CAAA;IACL,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACzB,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IACjE,CAAC;IAED,OAAO,aAAa,CAAA;AACtB,CAAC;AAED,gFAAgF;AAChF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAA+B;IAE/B,MAAM,aAAa,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAA;IAEzD,yEAAyE;IACzE,KAAK,aAAa,CAAA;IAClB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;IAC5B,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAA;IAEjD,OAAO;QACL,EAAE,EAAE,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE;QAC3B,GAAG,EAAE,2CAA2C,IAAI,CAAC,GAAG,EAAE,EAAE;QAC5D,SAAS;KACV,CAAA;AACH,CAAC"}
@@ -3,6 +3,26 @@ export interface WebhookResult {
3
3
  event: string;
4
4
  error?: string;
5
5
  }
6
+ export interface VerifyOptions {
7
+ /** Reject events whose timestamp is further than this from now. Default 300s. */
8
+ toleranceSeconds?: number;
9
+ /** Current time in ms — injectable for deterministic tests. Default Date.now(). */
10
+ now?: number;
11
+ }
12
+ /**
13
+ * Verify a Stripe webhook signature (the `Stripe-Signature` header) against the
14
+ * raw request body using the endpoint's signing secret. This is a
15
+ * dependency-free equivalent of `stripe.webhooks.constructEvent`:
16
+ *
17
+ * signed_payload = `${t}.${rawBody}`
18
+ * expected = HMAC-SHA256(signed_payload, secret) // hex
19
+ * valid = any provided v1 == expected (constant-time) AND |now - t| ≤ tolerance
20
+ *
21
+ * Throws on any failure. Multiple `v1` signatures (key rotation) are supported.
22
+ * Without this, a forged POST with any non-empty signature would be processed —
23
+ * a fake "payment succeeded" is a direct money/fraud risk.
24
+ */
25
+ export declare function verifyStripeSignature(payload: string, signatureHeader: string, secret: string, options?: VerifyOptions): void;
6
26
  /** Verifies and processes a Stripe webhook event payload. */
7
- export declare function handleStripeWebhook(payload: string, signature: string, secret: string): Promise<WebhookResult>;
27
+ export declare function handleStripeWebhook(payload: string, signature: string, secret: string, options?: VerifyOptions): Promise<WebhookResult>;
8
28
  //# sourceMappingURL=webhooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/stripe/webhooks.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAqCD,6DAA6D;AAC7D,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,aAAa,CAAC,CAiCxB"}
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/stripe/webhooks.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAUD,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,IAAI,CAkCN;AAgCD,6DAA6D;AAC7D,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,aAAa,CAAC,CA8BxB"}
@@ -1,3 +1,57 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ /** Default replay-protection window, matching Stripe's SDK (5 minutes). */
3
+ const DEFAULT_TOLERANCE_SECONDS = 300;
4
+ /**
5
+ * Verify a Stripe webhook signature (the `Stripe-Signature` header) against the
6
+ * raw request body using the endpoint's signing secret. This is a
7
+ * dependency-free equivalent of `stripe.webhooks.constructEvent`:
8
+ *
9
+ * signed_payload = `${t}.${rawBody}`
10
+ * expected = HMAC-SHA256(signed_payload, secret) // hex
11
+ * valid = any provided v1 == expected (constant-time) AND |now - t| ≤ tolerance
12
+ *
13
+ * Throws on any failure. Multiple `v1` signatures (key rotation) are supported.
14
+ * Without this, a forged POST with any non-empty signature would be processed —
15
+ * a fake "payment succeeded" is a direct money/fraud risk.
16
+ */
17
+ export function verifyStripeSignature(payload, signatureHeader, secret, options = {}) {
18
+ if (!secret)
19
+ throw new Error('Missing webhook signing secret');
20
+ if (!signatureHeader)
21
+ throw new Error('Missing webhook signature');
22
+ let timestamp;
23
+ const signatures = [];
24
+ for (const part of signatureHeader.split(',')) {
25
+ const eq = part.indexOf('=');
26
+ if (eq === -1)
27
+ continue;
28
+ const key = part.slice(0, eq).trim();
29
+ const value = part.slice(eq + 1).trim();
30
+ if (key === 't')
31
+ timestamp = value;
32
+ else if (key === 'v1')
33
+ signatures.push(value);
34
+ }
35
+ if (!timestamp || signatures.length === 0) {
36
+ throw new Error('Malformed webhook signature header');
37
+ }
38
+ const timestampSec = Number(timestamp);
39
+ if (!Number.isFinite(timestampSec))
40
+ throw new Error('Invalid webhook timestamp');
41
+ const nowSec = Math.floor((options.now ?? Date.now()) / 1000);
42
+ const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
43
+ if (Math.abs(nowSec - timestampSec) > tolerance) {
44
+ throw new Error('Webhook timestamp outside tolerance (possible replay)');
45
+ }
46
+ const expected = createHmac('sha256', secret).update(`${timestamp}.${payload}`).digest('hex');
47
+ const expectedBuf = Buffer.from(expected);
48
+ const matches = signatures.some((sig) => {
49
+ const sigBuf = Buffer.from(sig);
50
+ return sigBuf.length === expectedBuf.length && timingSafeEqual(sigBuf, expectedBuf);
51
+ });
52
+ if (!matches)
53
+ throw new Error('Webhook signature mismatch');
54
+ }
1
55
  async function handleCheckoutCompleted(session) {
2
56
  const customerEmail = session['customer_email'];
3
57
  const paymentIntentId = session['payment_intent'];
@@ -19,13 +73,10 @@ async function handleChargeRefunded(charge) {
19
73
  console.info(`[commerce:webhook] Charge refunded — charge=${chargeId} intent=${paymentIntentId} refunded=${amountRefunded}`);
20
74
  }
21
75
  /** Verifies and processes a Stripe webhook event payload. */
22
- export async function handleStripeWebhook(payload, signature, secret) {
76
+ export async function handleStripeWebhook(payload, signature, secret, options = {}) {
23
77
  let event;
24
78
  try {
25
- // TODO: Replace with stripe.webhooks.constructEvent(payload, signature, secret)
26
- if (!signature || !secret) {
27
- throw new Error('Missing webhook signature or secret');
28
- }
79
+ verifyStripeSignature(payload, signature, secret, options);
29
80
  event = JSON.parse(payload);
30
81
  }
31
82
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/stripe/webhooks.ts"],"names":[],"mappings":"AAWA,KAAK,UAAU,uBAAuB,CAAC,OAAgC;IACrE,MAAM,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAuB,CAAA;IACrE,MAAM,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAuB,CAAA;IACvE,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAuB,CAAA;IAEjE,8CAA8C;IAC9C,OAAO,CAAC,IAAI,CACV,iDAAiD,aAAa,YAAY,eAAe,UAAU,WAAW,EAAE,CACjH,CAAA;AACH,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,aAAsC;IAC1E,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAuB,CAAA;IAC1D,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAuB,CAAA;IAE5D,oDAAoD;IACpD,OAAO,CAAC,IAAI,CAAC,iDAAiD,QAAQ,WAAW,MAAM,EAAE,CAAC,CAAA;AAC5F,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,MAA+B;IACjE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAuB,CAAA;IACnD,MAAM,cAAc,GAAG,MAAM,CAAC,iBAAiB,CAAuB,CAAA;IACtE,MAAM,eAAe,GAAG,MAAM,CAAC,gBAAgB,CAAuB,CAAA;IAEtE,wEAAwE;IACxE,OAAO,CAAC,IAAI,CACV,+CAA+C,QAAQ,WAAW,eAAe,aAAa,cAAc,EAAE,CAC/G,CAAA;AACH,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAe,EACf,SAAiB,EACjB,MAAc;IAEd,IAAI,KAAkB,CAAA;IAEtB,IAAI,CAAC;QACH,gFAAgF;QAChF,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;QACD,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAA;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,gCAAgC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;SAC1F,CAAA;IACH,CAAC;IAED,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,4BAA4B;YAC/B,MAAM,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAChD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;QAE7C,KAAK,0BAA0B;YAC7B,MAAM,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAC/C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;QAE7C,KAAK,iBAAiB;YACpB,MAAM,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;QAE7C;YACE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;IAChD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/stripe/webhooks.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actuate-media/plugin-commerce",
3
- "version": "0.0.14",
3
+ "version": "0.1.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",