@arnaudjnn/billing-tools 0.36.0 → 0.36.1

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.
@@ -4,6 +4,13 @@ export declare const BILLING_WEBHOOK_EVENTS: readonly ["checkout.session.complet
4
4
  export type EnsureWebhookResult = {
5
5
  id: string;
6
6
  url: string;
7
+ /**
8
+ * OTHER endpoint ids registered on the same URL. Stripe allows duplicates and
9
+ * delivers to every one of them, so this is usually an accident worth acting
10
+ * on (harmless for credits, which are idempotent, but it doubles delivery
11
+ * volume and makes logs lie). Pass `pruneDuplicates` to remove them.
12
+ */
13
+ duplicates: string[];
7
14
  /**
8
15
  * The signing secret for STRIPE_WEBHOOK_SECRET.
9
16
  *
@@ -32,5 +39,17 @@ export declare function ensureWebhookEndpoint(opts: {
32
39
  /** Delete and re-create, to mint a new signing secret. Destructive: the old
33
40
  * secret stops verifying as soon as this runs. */
34
41
  recreate?: boolean;
42
+ /**
43
+ * Replace the event list with exactly `events` instead of adding to it.
44
+ *
45
+ * Off by default, and that default is the important one: an endpoint you
46
+ * didn't create may legitimately carry events another consumer depends on, and
47
+ * silently narrowing it in a deploy script turns their handler into a no-op
48
+ * with nothing in the logs. The union can only ever over-deliver, which costs
49
+ * a discarded event.
50
+ */
51
+ exact?: boolean;
52
+ /** Delete other endpoints registered on the same URL. Destructive. */
53
+ pruneDuplicates?: boolean;
35
54
  }): Promise<EnsureWebhookResult>;
36
55
  //# sourceMappingURL=webhook-setup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-setup.d.ts","sourceRoot":"","sources":["../src/webhook-setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAiBjC,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,yCAE4C,CAAC;AAEhF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,wDAAwD;IACxD,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,MAAM,CAAC,EAAE,SAAS,MAAM,CAAC,2BAA2B,CAAC,YAAY,EAAE,CAAC;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;uDACmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAoC/B"}
1
+ {"version":3,"file":"webhook-setup.d.ts","sourceRoot":"","sources":["../src/webhook-setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAiBjC,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,yCAE4C,CAAC;AAEhF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,wDAAwD;IACxD,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,MAAM,CAAC,EAAE,SAAS,MAAM,CAAC,2BAA2B,CAAC,YAAY,EAAE,CAAC;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;uDACmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sEAAsE;IACtE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA8C/B"}
@@ -27,19 +27,30 @@ export async function ensureWebhookEndpoint(opts) {
27
27
  const events = opts.events ?? BILLING_WEBHOOK_EVENTS;
28
28
  // Stripe has no lookup-by-url, so scan. Accounts have a handful of endpoints;
29
29
  // 100 is well past any real count.
30
- const existing = (await stripe.webhookEndpoints.list({ limit: 100 })).data.find((e) => e.url === opts.url);
30
+ const all = (await stripe.webhookEndpoints.list({ limit: 100 })).data;
31
+ const matches = all.filter((e) => e.url === opts.url);
32
+ const [existing, ...rest] = matches;
33
+ let duplicates = rest.map((e) => e.id);
34
+ if (duplicates.length && opts.pruneDuplicates) {
35
+ for (const id of duplicates)
36
+ await stripe.webhookEndpoints.del(id);
37
+ duplicates = [];
38
+ }
31
39
  if (existing && opts.recreate) {
32
40
  await stripe.webhookEndpoints.del(existing.id);
33
41
  }
34
42
  else if (existing) {
35
- const same = existing.enabled_events.length === events.length &&
36
- events.every((e) => existing.enabled_events.includes(e));
43
+ const wanted = opts.exact
44
+ ? [...events]
45
+ : [...new Set([...existing.enabled_events, ...events])];
46
+ const same = wanted.length === existing.enabled_events.length &&
47
+ wanted.every((e) => existing.enabled_events.includes(e));
37
48
  if (!same) {
38
49
  await stripe.webhookEndpoints.update(existing.id, {
39
- enabled_events: [...events],
50
+ enabled_events: wanted,
40
51
  });
41
52
  }
42
- return { id: existing.id, url: existing.url, created: false, updated: !same };
53
+ return { id: existing.id, url: existing.url, duplicates, created: false, updated: !same };
43
54
  }
44
55
  const created = await stripe.webhookEndpoints.create({
45
56
  url: opts.url,
@@ -50,6 +61,7 @@ export async function ensureWebhookEndpoint(opts) {
50
61
  id: created.id,
51
62
  url: created.url,
52
63
  secret: created.secret,
64
+ duplicates,
53
65
  created: true,
54
66
  updated: false,
55
67
  };
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-setup.js","sourceRoot":"","sources":["../src/webhook-setup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,8EAA8E;AAC9E,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,+EAA+E;AAC/E,2EAA2E;AAC3E,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,gFAAgF;AAChF,2EAA2E;AAC3E,kFAAkF;AAClF,6DAA6D;AAE7D,2DAA2D;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,4BAA4B;CACiD,CAAC;AAmBhF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAS3C;IACC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,sBAAsB,CAAC;IAErD,8EAA8E;IAC9E,mCAAmC;IACnC,MAAM,QAAQ,GAAG,CAAC,MAAM,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAC7E,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,CAC1B,CAAC;IAEF,IAAI,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACjD,CAAC;SAAM,IAAI,QAAQ,EAAE,CAAC;QACpB,MAAM,IAAI,GACR,QAAQ,CAAC,cAAc,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;YAChD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;gBAChD,cAAc,EAAE,CAAC,GAAG,MAAM,CAAC;aAC5B,CAAC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC;IAChF,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC;QACnD,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,cAAc,EAAE,CAAC,GAAG,MAAM,CAAC;QAC3B,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,qCAAqC;KACvE,CAAC,CAAC;IACH,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,KAAK;KACf,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"webhook-setup.js","sourceRoot":"","sources":["../src/webhook-setup.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arnaudjnn/billing-tools",
3
- "version": "0.36.0",
3
+ "version": "0.36.1",
4
4
  "description": "The get-paid engine for Stripe + WorkOS apps, for humans and AI agents. Drop-in API-key auth, token/credit + subscription billing, auth.md agent registration, and MPP machine payments, as MCP tools, a REST API, and a CLI. Framework-agnostic, storage-pluggable.",
5
5
  "license": "MIT",
6
6
  "author": "Arnaud Jeannin",