# Spendable credits

`Spendable Credit` is an immutable, nontransferable service-credit profile.
It is separate from the existing `Credit Token` standard. A sign-in balance is
never proof that a service request has been paid: the holder must consume whole
credits, then the provider must verify and durably accept the receipt.

## Create and use

The application creation form is `/mint/spendable-credit`. Existing collections
show provider, service, expiry, remaining credits and Purchase/Consume tabs.
Use a provider-issued request ID and the exact requested whole-credit amount.

```sh
bb build spendable-credit --payment-denom USDC \
  --provider bb1YOUR_PROVIDER --service-id images \
  --price-per-pack 1000000 --credits-per-pack 10 --uri ipfs://YOUR_METADATA
bb spendable-credits show COLLECTION_ID
bb spendable-credits purchase COLLECTION_ID --creator bb1YOUR_WALLET --units 2
bb spendable-credits consume COLLECTION_ID --creator bb1YOUR_WALLET \
  --units 3 --request-id PROVIDER_ISSUED_REQUEST_ID
```

Replace the address and ID placeholders. These commands prepare unsigned
transactions by default; normal CLI review/sign/deploy options apply. Price is
in payment base units (1,000,000 for one USDC); credits and packs are whole units.
`--expires-at` is an optional inclusive Unix-millisecond deadline. Both purchase
and consumption stop afterward, while prior confirmed receipts remain claimable.

SDK equivalents are `buildSpendableCredit`, `inspectSpendableCredit`,
`buildPurchaseSpendableCreditsMsg` and `buildConsumeSpendableCreditsMsg`.
The builder MCP exposes this builder through `build_standard` and the show,
purchase and consume actions through the standard-action registry. All surfaces
use the same immutable-profile validation and unsigned transaction builders.

## Authorization and lifecycle

- Paid mint adds credits; each holder-authorized burn removes credits on chain.
- The provider cannot debit a customer's wallet. There is no standing consent
  to revoke, peer transfer, withdrawal, or automatic refund in this profile.
- Price, provider, service, expiry and approval permissions are frozen. Create
  a new collection to offer different terms.
- A consumption memo contains the public provider, service and request ID.
  The signed creator, burn recipient, collection and amount bind the receipt.
- The chain prevents overspending a balance. The provider's durable unique
  request and receipt claims prevent granting the same entitlement twice.
  The chain does not enforce uniqueness of a service request ID: burning again
  can spend more credits without buying another fulfillment of that request.
- If submission or delivery is ambiguous, reconcile the same transaction and
  request. Never submit another consumption merely because a response was lost.

## Provider integration

See the runnable [provider example](https://github.com/BitBadges/bitbadgesjs/tree/main/packages/bitbadgesjs-sdk/examples/spendable-credit-provider).
It authenticates the customer, issues private request credentials, reads trusted
chain responses, and atomically records an entitlement in SQLite. It verifies
native Cosmos transfers or direct/single-message-batch EVM precompile calls.
EVM verification additionally requires the matching successful tokenization
execution event; receipt status alone would accept calls to disabled precompiles.

The returned entitlement ID is an idempotency key for actual service delivery.
An on-chain burn and an external HTTP side effect are not one atomic operation.
The service must implement idempotent delivery/reconciliation and disclose its
failure/support policy before consumption. Never accept client-supplied receipts,
infer payment from an arbitrary burn, or return private fulfillment data to a
caller who merely knows a public transaction hash.

## Purchase options

Creators can offer up to 20 immutable purchase options. Each option sets
`pricePerPack` (payment base units), `creditsPerPack` (whole service units), and
`purchaseType` (`fixed` or `scaled`). A fixed option purchases exactly one pack.
A scaled option accepts positive whole packs and may set `maxPacks`; omitting
it means unlimited within the protocol's uint64 limits on both payment and credit
totals. Limits apply per purchase, not per wallet or lifetime sales. Fractional
packs are not supported; purchases never round fractional service units.

Pass `purchaseOptions` to `buildSpendableCredit`, or `--purchase-options` with a
JSON array to `bb build spendable-credit`. Do not combine it with the legacy
single-option `pricePerPack` / `creditsPerPack` fields. Existing single-option
collections and calls remain supported as unlimited scaled purchases.

`inspectSpendableCredit` returns `purchaseOptions` with approval IDs and exact
limits. For multiple options, select an approval ID explicitly:
`bb spendable-credits purchase COLLECTION_ID --creator bb1YOUR_WALLET --units 2 --approval-id spendable-purchase-2`.
SDK callers use `quoteSpendableCreditPurchase(collection, packs, approvalId)`
for exact payment and credit totals, then
`buildPurchaseSpendableCreditsMsg(collection, wallet, packs, approvalId)`.
All options share the immutable provider, service, payment asset, and expiry;
consumption authorization and receipt verification are unchanged.
