License Operator NATS Events
The License Operator publishes a single JetStream subject whenever a paid license is created through the Stripe webhook pipeline. The publisher automatically creates the stream (work-queue retention, 30‑day max age) and writes one JSON message per issuance. Subscribe with durable consumers if you need guaranteed processing.
| Subject | Publisher | Trigger | Payload |
|---|---|---|---|
licenseoperator.license.issued | Stripe webhook manager | checkout.session.completed or invoice.payment_succeeded produces a new paid license | Signed license envelope plus Stripe identifiers |
subject.stripe.webhook.event | HTTP webhook controller | Every Stripe webhook POST request | Raw payload + signature for asynchronous replay |
licenseoperator.license.issued
The name of the subject can be configured in config.yaml under nats.subject_license_issued. It is recommended to keep the default value unless you have a naming convention to follow.
- When it fires – After a Stripe webhook event is validated, the payment record is persisted, and the license service has produced a signed envelope. Manual API issues (
/authlance/license/internal/issue) do not emit this event; it is exclusive to automated purchases. - Delivery semantics – Uses JetStream with a dedicated stream per subject (name derived by replacing dots with underscores). Messages remain available for 30 days. Acks are handled by your consumer; the operator simply publishes.
- Payload
{
"licenseId": "L-0d7fc1b1",
"email": "owner@example.com",
"payloadB64": "LS0tLUVYQU1QTEUtTElDRU5TRS0tLS0K...",
"signatureB64": "hQEMA5fakeSig==",
"exp": "2026-01-01T00:00:00Z",
"plan": "alpha",
"domain": "example.com",
"group": "acme",
"stripe": {
"customerId": "cus_FAKE123",
"checkoutSessionId": "cs_test_a1b2",
"invoiceId": "in_test_987"
}
}
Field reference
| Field | Description |
|---|---|
licenseId | Identifier returned by the issuance service (L- prefix or UUID). |
email | Primary contact used during checkout or manual issuance. |
payloadB64 | Base64-encoded license payload (PEM body) that downstream services can persist or email. |
signatureB64 | Base64-encoded RSA signature that pairs with payloadB64. |
exp | RFC3339 expiration timestamp that matches the stored license record. |
plan | Plan name resolved after applying coupon overrides and defaults. |
domain | Domain claim embedded in the license payload. |
group | Authlance group that owns the license (matches Stripe metadata group_name). |
stripe.customerId | Stripe customer ID tied to the purchase (may be empty for manual inserts). |
stripe.checkoutSessionId | Session identifier for the hosted checkout that produced the event. |
stripe.invoiceId | Invoice ID associated with the payment. |
Consuming the event
- Create a durable consumer on the stream the license-operator subject name is configured on the config.yaml
nats.subject_license_issued. This is where you could hook your email service or provisioning automation. - Acknowledge messages after you persist or deliver the license artifact to avoid redelivery.
- Optinally validate the signature using the public key that corresponds to the product key indicated in your configuration.
subject.stripe.webhook.event
Incoming Stripe webhook requests are persisted onto a dedicated JetStream work queue before any business logic executes. This shields the pipeline from transient HTTP failures—events are processed by an internal worker that validates the signature and issues licenses asynchronously. The subject can be changed via nats.subject_stripe_webhook_event, but most deployments can keep the default value.
- When it fires – Immediately upon receiving the webhook request, before verification. Duplicate deliveries are possible if Stripe retries.
- Delivery semantics – Work-queue JetStream stream with 30-day retention. The built-in worker acks only after processing succeeds; signature or metadata errors are acked to avoid infinite retries.
- Payload – JSON envelope containing the base64-encoded HTTP body, the
Stripe-Signatureheader, and a timestamp when it was enqueued. It is primarily for internal use, but you can subscribe for diagnostics if desired.