Evidence-based maturity assessment — what IS
Billing execution table, 6-state machine, triple-layer idempotency, immutability trigger, lifecycle event store
Authoritative workflow writes, BillingWorkflowConfig, WorkflowWriteContext, transition coverage, read compliance
Provider-agnostic webhook handler, async settlement flow, signature verification, reconciliation worker
Payment instrument entity, 3D Secure, risk assessment, PCI-DSS compliance, settlement reconciliation reports
| Capability | Score | Weight | Status | Evidence Hint |
|---|---|---|---|---|
|
Billing Executions Schema
billing_executions table with tenant_id, entity_contract_id, plan_contract_id, amount, currency, Citus-ready indexes
|
0
1
2
3
4
5
|
15 | Institutional | Migration: 20260327_create_billing_executions.sql |
|
6-State Execution Machine
created -> pending -> processing -> completed|failed. Pending settlement: processing -> pending. Cancel: created|pending -> canceled
|
0
1
2
3
4
5
|
20 | Institutional | Migration: 20260401_billing_status_machine_upgrade.sql with CHECK constraint |
|
Triple-Layer Idempotency
App-level check + DB ON CONFLICT (idempotency_key partial unique) + WHERE status guard. Pending included in duplicate detection.
|
0
1
2
3
4
5
|
20 | Institutional | SubscriptionBillingService::executePayment — 3 layers verified |
|
DB Immutability Enforcement
Trigger: terminal row protection (completed/failed/canceled), valid-only transitions, processing->pending allowed for async
|
0
1
2
3
4
5
|
15 | Institutional | Migration: 20260401_billing_immutability_trigger.sql |
|
Append-Only Lifecycle Event Store
BillingExecutionEventRepository — every state transition records type, old_status, new_status, metadata, user_id, correlation_id
|
0
1
2
3
4
5
|
15 | Institutional | |
|
Transactional Outbox Pattern
Dual-write: execution state + outbox event in same DB transaction. Events carry payment_rail, payment_provider, idempotency_key.
|
0
1
2
3
4
5
|
15 | Institutional |
| Capability | Score | Weight | Status | Evidence Hint |
|---|---|---|---|---|
|
Authoritative Workflow Write Path
recordWorkflowEventTransactional() writes to billing.contract_term_datetimes inside DB transaction. AUTHORITATIVE_WRITE_ENABLED=true.
|
0
1
2
3
4
5
|
25 | Institutional | PostgreSQLBillingExecutionRepository::recordWorkflowEventTransactional — fail-closed, asserts active transaction |
|
BillingWorkflowConfig Canonical Key
Centralized config: workflow key wf:billing.execution, 8 phase codes, target table, default source. Single source of truth.
|
0
1
2
3
4
5
|
15 | Institutional | BillingWorkflowConfig.php — 8 phase codes, wf:billing.execution key |
|
Stateless Context Propagation
WorkflowWriteContext VO: serviceDefault(), webhook(provider), user(userId), system(). Passed explicitly at every call site.
|
0
1
2
3
4
5
|
20 | Institutional | |
|
Full Transition Coverage
All 9 repository transition methods emit workflow events: markPending, markProcessing, markCompleted, markFailed, markCanceled, confirmFromPending, rejectFromPending, settleFromProcessing, reconcile
|
0
1
2
3
4
5
|
25 | Institutional | BillingWorkflowComplianceProvider verifies all 9 transitions |
|
Historical Event Backfill
Backfill migration creates contract_term_datetimes rows for all existing executions with deterministic SHA-256 transition IDs
|
0
1
2
3
4
5
|
15 | Institutional | Migration: 20260401_billing_workflow_event_backfill.sql |
| Capability | Score | Weight | Status | Evidence Hint |
|---|---|---|---|---|
|
Provider-Agnostic Webhook Handler
/api/v1/billing/webhook/{provider} — idempotent, extracts event_type/reference_id, dispatches confirmation/rejection
|
0
1
2
3
4
5
|
25 | Hardened | WebhookAction.php — returns 200 for terminal, 409 for non-pending |
|
Webhook Signature Verification
Per-provider HMAC-SHA256 verification of webhook payload. Skeleton in place; needs provider-specific signing keys.
|
0
1
2
3
4
5
|
20 | Partial | WebhookAction has verifySignature placeholder — needs per-provider HMAC implementation |
|
Pending Reconciliation Worker
Polls findPendingForReconciliation(), calls checkStatus() per rail, transitions pending->completed or pending->failed
|
0
1
2
3
4
5
|
25 | Hardened | bin/billing/billing-reconciliation-worker.php — idempotent, timeout-based, dry-run support |
|
Transient Failure Retry Strategy
Exponential backoff for isRetryable() failures. Max retries per rail, dead-letter after exhaustion.
|
0
1
2
3
4
5
|
15 | Missing | |
|
Billing Structured Observability
billing.execution.result + billing.rail.execution structured log events with duration_ms, rail, provider, correlation_id
|
0
1
2
3
4
5
|
15 | Institutional |
| Capability | Score | Weight | Status | Evidence Hint |
|---|---|---|---|---|
|
Payment Instrument Entity (Contract Pattern)
Model payment methods as contract pattern entities with tokenized storage via contract_term_attrs
|
0
1
2
3
4
5
|
25 | Missing | |
|
3D Secure Authentication Flow
3DS challenge flow for card payments: enroll check, challenge redirect, result verification
|
0
1
2
3
4
5
|
20 | Missing | |
|
Risk Assessment and Velocity Check
Pre-execution risk scoring: velocity limits, amount thresholds, fraud signals, device fingerprinting
|
0
1
2
3
4
5
|
20 | Missing | |
|
Settlement Reconciliation Reports
Daily/weekly reconciliation between internal records and provider settlement files. Discrepancy flagging.
|
0
1
2
3
4
5
|
20 | Missing | |
|
Financial Compliance Audit Trail
PCI-DSS Level 1 evidence: all cardholder data access logged, tokenization enforced, audit trail for regulatory inquiry
|
0
1
2
3
4
5
|
15 | Missing |