A payment provider may confirm a charge late, send the same event more than once, or leave an operation partially processed. Therefore, “paid” and “has access” are not equivalent. If an account’s permission depends directly on the latest response received from a payment API, a transient failure can block a customer who did pay or enable another whose charge ultimately failed.
With subscription states in PHP SaaS, the goal is not to store a single label in a table. It is to build a recoverable process: every decision must have evidence, an owner, a valid transition, and a way to reconcile when new data arrives.
Define product rules before the technical model

The data model does not resolve business ambiguities. Before designing entities or webhooks, product, finance, and operations teams must agree on what happens in each relevant situation.
- Sign-up: is access granted before the first payment is confirmed, after authorization, or only after settlement?
- Renewal: when does the grace period begin, and which capabilities are retained during it?
- Non-payment: are there automatic retries, notifications, partial restrictions, or full suspension?
- Cancellation: does access end immediately or at the end of the already contracted period?
- Refund or dispute: does it require an immediate block, manual review, or revocation once an outcome is confirmed?
- Reactivation: does it restore the exact previous plan, create a new commercial cycle, or require operational validation?
It is also advisable to distinguish a cancellation requested by the customer from an effective cancellation. The former expresses intent; the latter changes the future right of access. Mixing them leads to confusing interfaces and automations that are difficult to correct.
Separate contract, billing, and effective access
A maintainable architecture represents at least four concepts. The account identifies the owner and its members. The commercial contract describes the plan, agreed price, renewal date, and decision to cancel. The billing cycle represents a specific obligation for a period, its amount, and its outcome. Finally, enabled capabilities materialize what the account can do within the product.
This separation prevents a payment provider from becoming the single source of truth for the entire SaaS. A cycle may be pending while the contract remains valid during a grace period. At the same time, an account may retain read access but be unable to create new resources. Capabilities make it possible to express this decision without forcing a false binary between active and inactive.
In PHP, an application can expose an authorization service that queries a local capability projection, such as canCreateProject or canExportData. That projection is updated when commercial or billing facts change; it does not need to call the provider on every request. This reduces latency, external dependency, and scattered conditionals across controllers, queues, and scheduled tasks.
Model transitions, owners, and evidence
Avoid a single status field with values added as incidents arise. It is preferable to declare states per aggregate and permitted transitions. For example, a billing cycle can move from open to payment_pending, paid, failed, refunded, or disputed. Not every transition is reversible, nor can every actor perform it.
Every change must store a date, source, external identifier when one exists, and evidence. The source may be an internal order, a validated webhook, a reconciliation query, or an authorized manual action. A support correction must not silently overwrite history: it must be recorded as a distinct decision, with a reason and the responsible operator.
Priority when information conflicts
Define which evidence prevails. A redirect screen after payment should not confirm a cycle: it is used to inform the user, not as final evidence. A signed and verified webhook usually provides a stronger signal, but it may arrive late. An authenticated query to the provider during reconciliation can clarify missing events. If two sources disagree, the system must move to review or to a defined pending state, not arbitrarily choose the most recent data.
Process late, duplicate, and incomplete events
Event reception must be idempotent. Store a stable identifier for the external event and a hash or reference for the relevant payload. If it is received again, respond without repeating the business effect. This is especially important if a payment event triggers the issuance of a document, an extension of the period, or a notification.
Processing must separate reception from application. First, validate the signature, schema, and origin; then, durably store the received event; finally, process a task that attempts to apply the transition. If the process fails after persisting the event, a queue or recovery process can resume it. If it fails before persistence, reconciliation must discover the difference by comparing internal cycles with the external source.
event received → validation → durable record → idempotent application
↓
retry or reconciliationDo not assume delivery order. A refund may arrive before a late confirmation of the original payment. The rules must evaluate the current state, operation references, and the known sequence, placing impossible or ambiguous cases in a review queue. Blindly applying “the last event received” is a common cause of incorrect permissions.
Reconciliation and permissions as a controlled projection
Periodic reconciliation is not a patch; it is part of the design. It must find cycles left open for too long, payments confirmed outside the system, recorded but unprocessed events, duplicate external references, and capabilities that do not match the current contract. When it detects a difference, record the finding and apply a traceable transition instead of directly updating fields.
The capability projection must have explicit rules. For example, a valid contract with an overdue cycle that is still within grace may retain essential features; once grace ends, it may remove write operations. When a late payment is confirmed, the system re-enables the capabilities defined for the plan and preserves the history of the previous restriction.
A permission cache can be useful, but it needs invalidation when the projection changes and an expiration limit. Critical authorization must also not rely only on data stored in the browser. The server must decide using the current capability and the correct account, user, and resource scope.
Back office, auditing, and recovery testing
The support team needs to see, without editing database records, the contract, cycles, external events, applied transitions, current capabilities, and manual actions. It must be able to request a reconciliation, retry a safe event, and open a review. Corrections that alter access or balance require differentiated permissions, a mandatory reason, and an audit trail.
Test the flow as a sequence of failures, not only as a successful payment. Include a confirmed renewal, uncertain payment, duplicates, out-of-order events, a refund, cancellation at the end of the period, and reactivation. Verify both the final result and that no retry creates two periods, two documents, or a double extension of permissions.
A hypothetical case: a cycle expires, the charge remains pending, and the account enters grace with limited capabilities. The confirmation webhook is not processed due to a temporary interruption, but the event remains recorded. An idempotent retry confirms the cycle, extends the contract, and restores the capabilities. If the event had not arrived, reconciliation would find the confirmed external operation and generate the same transition with its own evidence.
Warning signs and checklist

Measure accounts with inconsistent contracts and capabilities, expired cycles without a decision, unprocessed events, exhausted retries, differences detected through reconciliation, and the frequency of manual changes. An increase in manual corrections usually indicates insufficient rules, not just an operational problem.
- Are the contract, billing cycle, and capabilities separate entities?
- Does each transition have an actor, evidence, date, and reason?
- Are external events idempotent and stored before they are applied?
- Is there reconciliation capable of recovering incomplete operations?
- Are permissions calculated from a local projection rather than from a real-time payment response?
- Can support investigate and correct with auditing, without direct changes in production?
- Do tests cover delays, duplicates, disorder, and contradictions?
A recoverable model does not eliminate external failures. It makes them detectable, contained, and correctable without turning a billing incident into a loss of control over access to the product.



