{"_id":"@davidilie/telemetry-stripe","_rev":"4-e3aacd8543b66e1e5822b43ea58a84df","name":"@davidilie/telemetry-stripe","dist-tags":{"latest":"0.5.0"},"versions":{"0.2.0":{"name":"@davidilie/telemetry-stripe","version":"0.2.0","keywords":["analytics","commerce","observability","opentelemetry","stripe","telemetry"],"author":{"url":"https://davidilie.com","name":"David Ilie","email":"david@davidilie.com"},"license":"Apache-2.0","_id":"@davidilie/telemetry-stripe@0.2.0","maintainers":[{"name":"davidilie","email":"david@davidilie.com"}],"homepage":"https://github.com/DavidIlie/davidapps-telemetry#readme","bugs":{"url":"https://github.com/DavidIlie/davidapps-telemetry/issues"},"dist":{"shasum":"a37878b627ae2499b8a4b4f4db3d17636ad8b3cb","tarball":"https://registry.npmjs.org/@davidilie/telemetry-stripe/-/telemetry-stripe-0.2.0.tgz","fileCount":9,"integrity":"sha512-RcqqBmE0hbCMAjpHhPECllueqz/9HkOxw3an2tiN01UeXdY5NuhCnKIhVmx1YQ4bvBkwhvrBONXQiywKWw46TQ==","signatures":[{"sig":"MEUCIFtWsmd3juWgNRL6ztRHpnqAP7dYh/GAarzAJIsKUfsGAiEA2JqW2uX0AlNpa+YI6kLfm4rwYLWcK6fLch9+A/O55qc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@davidilie%2ftelemetry-stripe@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":51428},"main":"./dist/index.cjs","type":"module","_from":"file:/home/runner/work/_temp/npm-packages/davidilie-telemetry-stripe-0.2.0.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"tsc -p tsconfig.json","test":"vitest run","build":"tsdown src/index.ts --format esm,cjs --dts --tsconfig tsconfig.build.json","check":"tsc -p tsconfig.json","clean":"rm -rf dist coverage"},"_npmUser":{"name":"davidilie","email":"david@davidilie.com"},"_resolved":"/home/runner/work/_temp/npm-packages/davidilie-telemetry-stripe-0.2.0.tgz","_integrity":"sha512-RcqqBmE0hbCMAjpHhPECllueqz/9HkOxw3an2tiN01UeXdY5NuhCnKIhVmx1YQ4bvBkwhvrBONXQiywKWw46TQ==","repository":{"url":"git+https://github.com/DavidIlie/davidapps-telemetry.git","type":"git","directory":"packages/stripe"},"_npmVersion":"11.19.0","description":"Strict Stripe webhook events and amount measurements for DavidApps telemetry.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","dependencies":{"@davidilie/telemetry-core":"0.2.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/telemetry-stripe_0.2.0_1786901121480_0.5859045203346112","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@davidilie/telemetry-stripe","version":"0.3.0","keywords":["analytics","commerce","observability","opentelemetry","stripe","telemetry"],"author":{"url":"https://davidilie.com","name":"David Ilie","email":"david@davidilie.com"},"license":"Apache-2.0","_id":"@davidilie/telemetry-stripe@0.3.0","maintainers":[{"name":"davidilie","email":"david@davidilie.com"}],"homepage":"https://github.com/DavidIlie/davidapps-telemetry#readme","bugs":{"url":"https://github.com/DavidIlie/davidapps-telemetry/issues"},"dist":{"shasum":"4375da752c0a7b46e4e73c1c8d9c3ec919146807","tarball":"https://registry.npmjs.org/@davidilie/telemetry-stripe/-/telemetry-stripe-0.3.0.tgz","fileCount":9,"integrity":"sha512-WKXlfUt1+ePAaDMLlNg0ILVufzwcZ6tA7ReHpAiSyR6xHgJK96jFlXFRZ/wwJR+DDNG9ppD2RLF3osVxCkJxQA==","signatures":[{"sig":"MEUCIDhmx6Fj87ZqRX5D9eplX0Ecxr41Vw2j0ELSVgxaKs8RAiEAs/5SJQBQzklrmAZmKmEpjPUJzrCMiHP2qSS3BN+QIow=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIC+PFvyfEIsPmSHlnAiLUI7+G7lA3nRmOkODwHK5L8w+AiBljo7zDT/xt0fVil8R70LbmhesZVnXVOwj6uUnERveWw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@davidilie%2ftelemetry-stripe@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":51276},"main":"./dist/index.cjs","type":"module","_from":"file:/home/runner/work/_temp/npm-packages/davidilie-telemetry-stripe-0.3.0.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"tsc -p tsconfig.json","test":"vitest run","build":"tsdown src/index.ts --format esm,cjs --dts --tsconfig tsconfig.build.json","check":"tsc -p tsconfig.json","clean":"rm -rf dist coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"43bab322-3d27-44b7-8ae9-1aac1f7cfd3a"}},"_resolved":"/home/runner/work/_temp/npm-packages/davidilie-telemetry-stripe-0.3.0.tgz","_integrity":"sha512-WKXlfUt1+ePAaDMLlNg0ILVufzwcZ6tA7ReHpAiSyR6xHgJK96jFlXFRZ/wwJR+DDNG9ppD2RLF3osVxCkJxQA==","repository":{"url":"git+https://github.com/DavidIlie/davidapps-telemetry.git","type":"git","directory":"packages/stripe"},"_npmVersion":"11.19.0","description":"Strict Stripe webhook events and amount measurements for DavidApps telemetry.","directories":{},"sideEffects":false,"_nodeVersion":"24.21.0","dependencies":{"@davidilie/telemetry-core":"0.3.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/telemetry-stripe_0.3.0_1790676130786_0.23734548722917337","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@davidilie/telemetry-stripe","version":"0.4.0","keywords":["analytics","commerce","observability","opentelemetry","stripe","telemetry"],"author":{"url":"https://davidilie.com","name":"David Ilie","email":"david@davidilie.com"},"license":"Apache-2.0","_id":"@davidilie/telemetry-stripe@0.4.0","maintainers":[{"name":"davidilie","email":"david@davidilie.com"}],"homepage":"https://github.com/DavidIlie/davidapps-telemetry#readme","bugs":{"url":"https://github.com/DavidIlie/davidapps-telemetry/issues"},"dist":{"shasum":"047c84dcf776cc12d661877ce5006b973098cea1","tarball":"https://registry.npmjs.org/@davidilie/telemetry-stripe/-/telemetry-stripe-0.4.0.tgz","fileCount":9,"integrity":"sha512-KoaBqfBqS0DA/Nyv23zc5D7r2SJ+ztoZorn1aFVT5YRE5eILyx1bpZfbZHZ4lv0yq6xURyn6aUoYQ39WHRDSlw==","signatures":[{"sig":"MEQCIDhLHIhAaYWSZd6Eb/QLWo+CsHbqQy2SG1Nfqw8ycmswAiAdComXX64MkrtVoJQYTRxGMIDjcxpib3gOKjka3jd9+g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCEGMp+j05U4CApieZgSk5zZKQcB+lwQ2UZ8QsWxD/hogIgNywoiCgx59VGJ9CnOBikbEvFdhhAfCpjbxwJj3aRkg8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@davidilie%2ftelemetry-stripe@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":51276},"main":"./dist/index.cjs","type":"module","_from":"file:/home/runner/work/_temp/npm-packages/davidilie-telemetry-stripe-0.4.0.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"tsc -p tsconfig.json","test":"vitest run","build":"tsdown src/index.ts --format esm,cjs --dts --tsconfig tsconfig.build.json","check":"tsc -p tsconfig.json","clean":"rm -rf dist coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"43bab322-3d27-44b7-8ae9-1aac1f7cfd3a"}},"_resolved":"/home/runner/work/_temp/npm-packages/davidilie-telemetry-stripe-0.4.0.tgz","_integrity":"sha512-KoaBqfBqS0DA/Nyv23zc5D7r2SJ+ztoZorn1aFVT5YRE5eILyx1bpZfbZHZ4lv0yq6xURyn6aUoYQ39WHRDSlw==","repository":{"url":"git+https://github.com/DavidIlie/davidapps-telemetry.git","type":"git","directory":"packages/stripe"},"_npmVersion":"11.19.0","description":"Strict Stripe webhook events and amount measurements for DavidApps telemetry.","directories":{},"sideEffects":false,"_nodeVersion":"24.21.0","dependencies":{"@davidilie/telemetry-core":"0.4.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/telemetry-stripe_0.4.0_1790697732016_0.8562568574965177","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"_id":"@davidilie/telemetry-stripe@0.5.0","bugs":{"url":"https://github.com/davidapps-org/davidapps-telemetry/issues"},"dist":{"shasum":"a6675b26f50821f87deca50fd497a5537d71587c","tarball":"https://registry.npmjs.org/@davidilie/telemetry-stripe/-/telemetry-stripe-0.5.0.tgz","fileCount":9,"integrity":"sha512-ZDrnELS7aWDCmUJ8MSAA0TLcQW/w9i9hmtkGDPKOfdABsGtPn3WwTJf/oye4VyZnEBVXkJCQL0aq1Nw5etZMFQ==","signatures":[{"sig":"MEUCIQC3pudUV9kB99xgTTLwtPXcfxZBd9v9ZDJ9+Odzc620vwIgVNIjb1v9nl77Om2JERgD+1n56nhaQNMEwoFOataiVUI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDJ/5d+jNFho6Oq1NjIiQtebUegk3uV2FZGN4V5Qz22swIgHP/Q3D5406AKnBv0H4canCLbEmR7mI8vdi+nuIC5t1o="}],"unpackedSize":51289},"main":"./dist/index.cjs","name":"@davidilie/telemetry-stripe","type":"module","_from":"file:/home/runner/work/_temp/npm-packages/davidilie-telemetry-stripe-0.5.0.tgz","types":"./dist/index.d.mts","author":{"url":"https://davidilie.com","name":"David Ilie","email":"david@davidilie.com"},"module":"./dist/index.mjs","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"license":"Apache-2.0","scripts":{"lint":"tsc -p tsconfig.json","test":"vitest run","build":"tsdown src/index.ts --format esm,cjs --dts --tsconfig tsconfig.build.json","check":"tsc -p tsconfig.json","clean":"rm -rf dist coverage"},"version":"0.5.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"4a8e4d3b-e60b-401f-b427-2f4ba9140347"}},"homepage":"https://github.com/davidapps-org/davidapps-telemetry#readme","keywords":["analytics","commerce","observability","opentelemetry","stripe","telemetry"],"_resolved":"/home/runner/work/_temp/npm-packages/davidilie-telemetry-stripe-0.5.0.tgz","_integrity":"sha512-ZDrnELS7aWDCmUJ8MSAA0TLcQW/w9i9hmtkGDPKOfdABsGtPn3WwTJf/oye4VyZnEBVXkJCQL0aq1Nw5etZMFQ==","repository":{"url":"git+https://github.com/davidapps-org/davidapps-telemetry.git","type":"git","directory":"packages/stripe"},"_npmVersion":"11.19.0","description":"Strict Stripe webhook events and amount measurements for DavidApps telemetry.","directories":{},"maintainers":[{"name":"davidilie","email":"david@davidilie.com"}],"sideEffects":false,"_nodeVersion":"24.21.0","dependencies":{"@davidilie/telemetry-core":"0.5.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/","provenance":false},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/telemetry-stripe_0.5.0_1790757573314_0.4055556037526564"}}},"time":{"created":"2026-08-16T17:25:21.292Z","modified":"2026-09-30T08:39:33.587Z","0.2.0":"2026-08-16T17:25:21.621Z","0.3.0":"2026-09-29T10:02:10.879Z","0.4.0":"2026-09-29T16:02:12.112Z","0.5.0":"2026-09-30T08:39:33.382Z"},"bugs":{"url":"https://github.com/davidapps-org/davidapps-telemetry/issues"},"author":{"url":"https://davidilie.com","name":"David Ilie","email":"david@davidilie.com"},"license":"Apache-2.0","homepage":"https://github.com/davidapps-org/davidapps-telemetry#readme","keywords":["analytics","commerce","observability","opentelemetry","stripe","telemetry"],"repository":{"url":"git+https://github.com/davidapps-org/davidapps-telemetry.git","type":"git","directory":"packages/stripe"},"description":"Strict Stripe webhook events and amount measurements for DavidApps telemetry.","maintainers":[{"name":"davidilie","email":"david@davidilie.com"}],"readme":"# `@davidilie/telemetry-stripe`\n\nA deliberately small Stripe webhook mapper for DavidApps telemetry. It turns a\nstrict allowlist of already verified Stripe webhook events into stable commerce\nevents and optional integer amount measurements. It does not make Stripe API\nrequests and has no dependency on the Stripe runtime SDK.\n\n```bash\npnpm add @davidilie/telemetry-stripe @davidilie/telemetry-node stripe\n```\n\n## Use\n\nVerify the signature using Stripe's SDK and the untouched raw request body,\nthen atomically claim the event ID in your own datastore before recording it:\n\n```ts\nimport { recordStripeWebhookEvent } from \"@davidilie/telemetry-stripe\";\n\nconst event = stripe.webhooks.constructEvent(\n  rawBody,\n  request.headers.get(\"stripe-signature\")!,\n  process.env.STRIPE_WEBHOOK_SECRET!,\n);\n\n// Stripe retries deliveries. `claimWebhook` must return false for an event ID\n// that this application has already processed.\nif (await claimWebhook(event.id)) {\n  recordStripeWebhookEvent(telemetry, event);\n  await telemetry.flush();\n}\n```\n\n`stripe` is an application dependency used for signature verification; this\npackage deliberately does not depend on or initialize it. `telemetry` is the\nclient already created by `@davidilie/telemetry-node` or\n`@davidilie/telemetry-next`. `claimWebhook` is application-owned persistent\nstorage with a unique constraint or atomic set-if-absent operation—an in-memory\nset does not deduplicate across replicas or restarts.\n\nRead the request body exactly once as text or bytes. Parsing and reserializing\nJSON before `constructEvent` changes the signed bytes and breaks verification.\nReturn a non-2xx response when verification or authoritative business handling\nfails so Stripe can retry. Telemetry recording itself is intentionally\nbest-effort and must never decide whether an order, subscription, or refund\nsucceeded.\n\nThe returned result is explicit:\n\n```ts\n{ recorded: true, eventName: \"commerce.payment.succeeded\", measured: true }\n{ recorded: false, reason: \"unsupported_event\" }\n{ recorded: false, reason: \"invalid_event\" }\n```\n\n## Supported events\n\n| Stripe webhook | Normalized event | Amount measurement |\n| --- | --- | --- |\n| `checkout.session.completed` | `commerce.checkout.completed` | `commerce.checkout.amount` when `payment_status` is `paid` |\n| `payment_intent.succeeded` | `commerce.payment.succeeded` | `commerce.payment.amount` |\n| `payment_intent.payment_failed` | `commerce.payment.failed` | `commerce.payment.amount` |\n| `charge.succeeded` | `commerce.sale.succeeded` | `commerce.sale.amount` |\n| `charge.failed` | `commerce.sale.failed` | `commerce.sale.amount` |\n| `charge.refunded` | `commerce.refund.completed` | none; Stripe's `amount_refunded` is cumulative |\n| `refund.created` | `commerce.refund.created` | `commerce.refund.amount` |\n| `invoice.paid` | `commerce.invoice.paid` | `commerce.invoice.amount` |\n| `invoice.payment_failed` | `commerce.invoice.payment_failed` | `commerce.invoice.amount` |\n| `customer.subscription.created` | `commerce.subscription.started` | none |\n| `customer.subscription.updated` | `commerce.subscription.changed` | none |\n| `customer.subscription.deleted` | `commerce.subscription.ended` | none |\n| `charge.dispute.created` | `commerce.dispute.opened` | `commerce.dispute.amount` |\n| `charge.dispute.closed` | `commerce.dispute.closed` | `commerce.dispute.amount` |\n\nMeasurements use the fixed unit `minor_currency_unit`. For example, `1234` is\nEUR 12.34 for a two-decimal currency. The mapper emits an amount only when it is\na non-negative safe integer and a three-letter currency is present. Dashboards\nmust group by `commerce.currency`; never sum different currencies together.\n\nThe Node adapter records measurements as spans by default. If the application\nenables its OpenTelemetry histogram mode, explicitly allow only these bounded\nmetric attributes:\n\n```ts\nmetricAttributeAllowlist: [\n  \"commerce.event.family\",\n  \"stripe.event.type\",\n  \"commerce.mode\",\n  \"commerce.outcome\",\n  \"commerce.currency\",\n  \"commerce.status\",\n]\n```\n\n## Avoid double counting\n\nDeduplicating Stripe event IDs prevents retry duplicates, but a single payment\ncan still produce a Checkout Session, PaymentIntent, Charge, and Invoice event.\nChoose one canonical event family for each dashboard total. For example, use\n`payment_intent.succeeded` for payment revenue, or `invoice.paid` for invoice\ncollections—do not sum both. Use `refund.created` as the canonical per-refund\namount. `charge.refunded` is lifecycle-only because its `amount_refunded` value\nis cumulative and can otherwise overcount partial refunds.\n\nThis package is not a ledger. It does not reconcile missed events, calculate\nMRR/LTV, convert currencies, or persist webhook delivery state.\n\n## Grafana dashboard contract\n\nFor amount panels, configure the Node/Next client with OpenTelemetry histogram\nmeasurements and the exact bounded allowlist shown above. A Prometheus-compatible\nbackend then exposes histogram count and sum series for the selected canonical\nfamily. Use the increase of the `_sum` series over the dashboard range, grouped\nby currency and mode; never combine currencies or add multiple payment families\ntogether.\n\nKeep these panels separate:\n\n- successful payment amount from `payment_intent.succeeded`, or invoice amount\n  from `invoice.paid`—choose one for revenue;\n- refund amount from `refund.created`;\n- dispute amount from `charge.dispute.created`;\n- event counts and failures from the normalized `commerce.*` events/traces.\n\nConfirm the exact metric and label names in Grafana Explore after the first\ncanary because the collector/backend controls OpenTelemetry-to-Prometheus name\nnormalization. The source measurement names and attributes in this package are\nthe stable contract.\n\n## Privacy boundary\n\nThe mapper reads and exports only:\n\n- a supported event type;\n- live or test mode;\n- a strictly allowlisted status;\n- a valid three-letter currency; and\n- one event-specific integer amount.\n\nIt never exports the raw payload, event/object/customer IDs, email, metadata,\ndescriptions, failure messages, URLs, or arbitrary Stripe fields. Unsupported\nevents are no-ops.\n","readmeFilename":"README.md"}