Skip to main content

Events

:::info Source Sourced from services/billing-service/EVENT_SCHEMAS.md in the documentation repo. :::

1. Envelope

Standard. retentionClass = regulated (7-year cold archive for tax compliance).

2. Events Published

2.1 billing.subscription.created.v1

interface SubscriptionCreatedV1 { subscriptionId: SubscriptionId; tenantId: TenantId; planId: string; state: string; trialEnd?: ISODate; currentPeriod: { start: ISODate; end: ISODate }; itemQuantities: Record<string, number>; }

2.2 billing.subscription.changed.v1

interface SubscriptionChangedV1 { subscriptionId: SubscriptionId; tenantId: TenantId; previousState: string; newState: string; changedAt: ISODate; reason?: string; }

2.3 billing.subscription.canceled.v1

interface SubscriptionCanceledV1 { subscriptionId: SubscriptionId; tenantId: TenantId; canceledAt: ISODate; effectiveAt: ISODate; reason?: string; }

2.4 billing.invoice.created.v1, .finalized.v1, .paid.v1, .voided.v1

interface InvoicePaidV1 { invoiceId: InvoiceId; tenantId: TenantId; customerId: string; total: Money; paidAt: ISODate; paymentId: PaymentId; }

2.5 billing.payment.succeeded.v1 (integration event)

interface PaymentSucceededV1 {
paymentId: PaymentId;
orderId?: OrderId;
invoiceId?: InvoiceId;
tenantId: TenantId;
amount: Money;
currency: ISO4217;
processor: string;
processorRef: string;
succeededAt: ISODate;
}

2.6 billing.payment.failed.v1

interface PaymentFailedV1 { paymentId: PaymentId; orderId?: OrderId; tenantId: TenantId; amount: Money; reason: string; processorCode?: string; failedAt: ISODate; }

2.7 billing.payment.refunded.v1

interface PaymentRefundedV1 { paymentId: PaymentId; refundId: string; tenantId: TenantId; amount: Money; reason: string; refundedAt: ISODate; }

2.8 billing.dunning.*

interface DunningStartedV1 { processId: ULID; subscriptionId: SubscriptionId; tenantId: TenantId; startedAt: ISODate; }
interface DunningStageAdvancedV1 { processId: ULID; stage: string; advancedAt: ISODate; }
interface DunningResolvedV1 { processId: ULID; resolution: string; resolvedAt: ISODate; }

2.9 billing.payout.initiated.v1 / .completed.v1 / .failed.v1

interface PayoutCompletedV1 { payoutId: ULID; providerTenantId: TenantId; amount: Money; processorRef: string; completedAt: ISODate; }

2.10 billing.tax.calculated.v1

For audit / analytics, tax computation at order time.

3. EP-6 — Marketplace checkout (Ghasi-EdTech implementation, 2026-04)

The following types are emitted on billing.orders flows (outbox, buyer tenantId context). Payload shapes match @ghasi/event-envelope envelopes in code (services/billing-service). Amounts are integer micro-units; currency ISO 4217.

Event type (wire)When
marketplace.order.placed.v1Order created (pending_payment).
billing.payment.succeeded.v1Sandbox pay succeeds (paid).
billing.payment.refunded.v1Refund completes after enrollment revoke handoff.
marketplace.order.refunded.v1Same refund saga (buyer-visible order outcome).
marketplace.coupon.redeemed.v1Coupon applied on order create + redemption row written.
billing.payout.scheduled.v1Reserved — emit when payout scheduler exists (US-32).
billing.payout.paid.v1Reserved — emit when payout completion exists (US-32).

3.1 marketplace.order.placed.v1 (billing outbox)

interface MarketplaceOrderPlacedV1Payload {
orderId: string;
listingId: string;
courseId: string;
buyerTenantId: string;
providerTenantId: string;
totalAmountMicros: number;
currency: string;
}

3.2 billing.payment.succeeded.v1 (checkout)

interface BillingPaymentSucceededV1Payload {
orderId: string;
paymentRef: string;
amountMicros: number;
currency: string;
}

3.3 billing.payment.refunded.v1

interface BillingPaymentRefundedV1Payload {
orderId: string;
amountMicros: number;
currency: string;
}

3.4 marketplace.order.refunded.v1

interface MarketplaceOrderRefundedV1Payload {
orderId: string;
listingId: string;
providerTenantId: string;
}

3.5 marketplace.coupon.redeemed.v1

interface MarketplaceCouponRedeemedV1Payload {
orderId: string;
couponId: string;
discountMicros: number;
}

Note: Canonical subscription/invoice events in §2 remain the long-term billing model; §3 documents the EP-6 marketplace order path coexisting with that roadmap.

4. Events Consumed

  • marketplace.order.placed.v1 → create PaymentIntent (future PSP; today order is created in billing directly).
  • tenant.org.provisioned.v1 → create customer + trial subscription (if plan includes trial).
  • gdpr.subject_request.received.v1 → anonymize PII in customer records (legal hold on billing retained).

5. Versioning

  • v1 schemas additive.
  • Money field always in micro-units + ISO4217 (never change unit).
  • Processor-specific metadata in separate processorMetadata JSONB field.

6. Idempotency

  • Consumer inbox dedups.
  • Stripe webhooks: event.id is PK in inbox.
  • Saga steps idempotent on order-id.

7. Outbox / Inbox

Standard; regulated class → 7-year cold archive.

8. Partition Key

  • tenantId for subscription/invoice events.
  • orderId for payment events (groups with marketplace saga).

9. DLQ

billing.dlq. Poison messages alerted; manual reconciliation tool for stuck payments.