# Payment State and Tracking

A payment collection needs more than one boolean. Track each obligation's progress, its time window, the current payer's eligibility, and available evidence independently. Collection summaries are derived from those facts.

## Progress and Lifecycle

| Fact | Interpretation |
| --- | --- |
| No received units/payments, with complete evidence | Unpaid |
| Some finite units/payments received | Partially paid until the obligation's configured target is met |
| Finite target reached | Paid, even after the payment window closes |
| Before opening time | Scheduled; not yet payable |
| Inside the inclusive payment window | Open lifecycle; payable additionally requires remaining capacity and payer eligibility |
| Past cutoff with an outstanding amount | Expired with an outstanding amount; prior payments remain recorded |
| Past an informational due date | Overdue if outstanding; it may still be payable |
| A reusable link has prior payments | Usage history; the link does not become finally paid |
| Tracker or payout evidence is unavailable | Unknown; do not substitute unpaid or zero |

For multi-obligation invoices, one obligation can be paid while another is scheduled or expired. Show the underlying obligations when a collection-level label would obscure that distinction. A partial receipt count is an amount of units, not a count of contributing people.

## Tracker Identity

Use the tracker identity configured by the approval: collection ID, approval level, approver where applicable, approval ID, amount/count tracker ID, tracker type, and relevant address. Include reset semantics where the standard configures resets. Reusing an unrelated tracker or omitting one identity field can report the wrong progress.

Fixed payment caps use payment counts; partial targets use cumulative receipt amounts. Distinct-payer obligations additionally use per-initiator counts. A public link's overall count measures uses without imposing a finite completion target.

The indexer reads configured trackers and builds the projection from authoritative documents. Incomplete reads must fail/retry or remain explicitly unknown. A missing API response is not evidence that an existing obligation has never been paid.

## Amounts and Participants

For a fixed obligation, each successful payment contributes the specified payout amounts. For a partial obligation, multiply each payout by received receipt units. Sum exact integer base units only within the same denomination.

Keep requested face amount, collected amount, and remaining amount separate. Legacy rows can have a known face amount with unknown payment history. Showing the face amount as collected would mislead managers.

Participants include named eligible payers and payout recipients. Public payer status is an explicit property, not an empty roster. An index row can help discover the collection but cannot enumerate every person who may use a public link. Actual contributors belong in payment activity.

## Activity and Confirmation

Use successful chain execution and its payment events to identify receipts. Keep obligation references, transaction identity, initiator, receipt units, and denomination-specific payouts together. Failed transactions must not increase paid progress.

An SDK action builds a message. A browser callback may report a submitted transaction. Neither alone proves the payment executed successfully. Confirm the hash on the selected chain, then allow the indexer to catch up before expecting the dashboard projection to change.

If an event or rebuild fails, durable retry must preserve the last valid projection and eventually converge to chain evidence. A delayed worker must not overwrite a newer projection or acknowledge a replacement queue generation. These are indexer requirements, not extra payment permissions.

## Retry Decisions

| Situation | Next step |
| --- | --- |
| User rejects the wallet request before submission | Preserve the proposal; ask again only when the user requests it |
| Wallet reports a hash | Check execution result on the same network |
| Browser closes or CLI times out | Treat as uncertain; inspect wallet and chain before retrying |
| Chain rejects due to exhausted capacity | Refresh the obligation; explain that another payment consumed the slot |
| Chain succeeds but dashboard is stale | Wait for/requery the indexer; do not pay again |
| Receipt evidence is incomplete | Report unknown; retain transaction identity for reconciliation |

A finite invoice may reject duplicate payment after its cap is consumed. A reusable link deliberately allows repeats. Neither should be treated as a general idempotency key for arbitrary client retries.

## Reconciliation Record

Keep a durable application record for each attempted action. This is an **application-owned example**, not a BitBadges API response or an on-chain standard field:

```json
{
  "network": "mainnet",
  "collectionId": "46",
  "obligationId": "payment-1",
  "requestedUnits": "1",
  "transactionHash": null,
  "outcome": "unknown",
  "retrySafe": false
}
```

Replace the illustrative collection ID and persist the actual expected signer and payout terms before handing off to a wallet. Fill in the hash when available and reconcile against execution and obligation trackers. Keep an unknown attempt in this state until evidence resolves it; do not infer failure from a missing callback or replace a missing tracker with a zero balance. The CLI's own result envelope and fields are documented in [browser deployment](https://docs.bitbadges.io/cli/deploy).
