# Integrating usevig

You are integrating usevig into a project. Follow this in order. Step 1 costs
money, so handle it according to whatever spending authority you have.

If you have not decided whether this fits, read https://usevig.com/evaluate.md first.

Ask the operator for two things and never invent either:
- the **payout address** their customers' money settles to
- which **chains and tokens** to accept

## 1. Get a live account

Mainnet requires a plan. base-sepolia is free — skip to the note at the end of
this step if you only want to build against testnet first.

    curl -X POST https://usevig.com/v1/signup \
      -H 'Content-Type: application/json' \
      -d '{"plan":"starter","payout_address":"0xTheirs","webhook_url":"https://theirs/hook"}'

Returns:

    {
      "object": "signup",
      "pending_merchant_id": "mch_...",
      "purchase_id": "pur_...",
      "checkout_id": "chk_...",
      "plan": {"tier":"starter","price_usd":49,"period_days":30,
               "included_transactions":11000},
      "status": "pending",
      "checkout_url": "https://usevig.com/pay/c/chk_...",
      "options": [ ... ],
      "status_url": "https://usevig.com/v1/signup/pur_...",
      "webhook_secret": "whsec_..."
    }

Pay the plan by sending the exact `amount` from any entry in `options` to that
entry's `address`, or open `checkout_url`. **This spends real money.**

Then poll `status_url` until `api_key` appears:

    curl https://usevig.com/v1/signup/pur_...

    -> {"paid": true, "plan_status": "active", "api_key": "vig_live_..."}

**Store that key immediately.** It is returned until it is first used, then
never again. Keep it out of version control.

*Free path:* `POST /v1/merchants {"network":"base-sepolia","payout_address":"0x..."}`
returns a `vig_test_` key instantly, no payment. Identical code path.

## 2. Create a charge

    curl -X POST https://usevig.com/v1/checkouts \
      -H "Authorization: Bearer $VIG_KEY" \
      -H 'Content-Type: application/json' \
      -d '{"amount_usd":25,"accept":[{"network":"base","token":"USDC"}],"finality":"confirmed"}'

Returns exactly this shape — do not write defensive code for alternatives:

    {
      "id": "chk_...",
      "object": "checkout",
      "status": "pending",
      "amount_usd": 25,
      "resource": null,
      "finality": "confirmed",
      "reached": null,
      "finality_detail": {"level","label","approx_seconds","source","reversal_risk"},
      "checkout_url": "https://usevig.com/pay/c/chk_...",
      "expires_at": <unix>,
      "created_at": <unix>,
      "paid_at": null,
      "options": [ ... ],
      "instructions": "...",
      "custody": "..."
    }

Each entry in `options`:

    {
      "network": "base",
      "chain_id": 8453,
      "token": "USDC",
      "token_address": "0x...",
      "decimals": 6,
      "amount": "25000000",
      "address": "0x...",
      "rate_usd": 1,
      "quote_expires_at": <unix>
    }

Send `address` and `amount` to a machine payer; send `checkout_url` to a human.
Omit `accept` to offer every token on the account's network.

## 3. Handle the webhooks

Events: `checkout.paid`, `checkout.underpaid`, `checkout.expired`,
`checkout.reorged`, plus `plan.activated`, `plan.expiring`, `plan.expired`.

Each delivery carries:

    X-Vig-Signature: t=<unix>,v1=<hmac-sha256 hex>
    X-Vig-Event-Id: <id>
    X-Vig-Delivery-Attempt: <n>

The signed message is `<timestamp>.<raw body>`, keyed with the `webhook_secret`
from signup. Compare in constant time and reject a timestamp older than
300s. Deliveries retry, so make handling idempotent on `X-Vig-Event-Id`.

**Fulfil on `checkout.paid` only.** `checkout.underpaid` means the payer sent
less than the quoted amount and must not release the goods; the shortfall is in
the payload. Anything under 1% short of the quote counts as underpaid.

## 4. Choose finality deliberately

- `instant` — In a block, about 2s on base.
- `confirmed` — Confirmed, about 12s on base.
- `safe` — Safe, about 58s on base.
- `finalized` — Final, about 1160s on base.

Lower levels are faster and carry more reversal risk. `confirmed` is a
reasonable default for digital goods; use `finalized` before shipping anything
physical or expensive. Set it per merchant or per checkout. `checkout.reorged`
can still fire after `checkout.paid` if a reorg unwinds the payment — handle it.

## 5. Read decimals from the API, never hardcode

Most supported tokens use 6 decimals. DAI uses 18. Assuming 6 for DAI
understates a charge by a factor of 10^12.

Price in `amount_usd` and let the API compute `amount`. If you must compute it
yourself, read `decimals` from the option or from `GET /v1/config`.

## 6. Collect your funds

Payments accumulate at per-checkout addresses. Sweeping them to the payout
address is a transaction the operator signs and broadcasts, paying that gas.
usevig never calls it.

    curl "https://usevig.com/v1/collect/quote?network=base" -H "Authorization: Bearer $VIG_KEY"

    {
      "object": "collect_quote",
      "network": "base",
      "chain_id": 8453,
      "unswept": [ ... ],
      "total_usd": 37.5,
      "address_count": 3,
      "gas_estimate": {"gas_units": <n>, "note": "..."},
      "transaction": {"to":"0x...","data":"0x...","value":"0x0","gas":"...","chain_id":8453},
      "how_to_broadcast": "...",
      "custody": "..."
    }

`transaction` is **unsigned**. Sign and broadcast it with any wallet or
`eth_sendTransaction`. Several checkouts collect in one transaction, so this
costs one signature rather than one per payment. `transaction` is `null` when
there is nothing to collect.

## Operational facts that will otherwise cost you time

- **A quote expires in 15 minutes; the checkout itself in 60 minutes.** Create
  the checkout when the payer is ready, not far in advance.
- **Overpayment marks the checkout paid** and the excess is collectable. The
  payer is not refunded automatically.
- **The receiving address is a contract that does not exist yet.** That is
  normal — token balances are ledger entries, so funds arrive at an address
  with no code, and the contract is deployed by the collect transaction.
- **Right token, wrong chain is unrecoverable.** The address is derived per
  chain. Show the payer the network as prominently as the amount.
- **A collected address disappears from the next quote automatically.** Since
  you broadcast the sweep yourself, usevig confirms collection from the
  on-chain balance rather than from having seen your transaction.
- **Collection keeps working when a plan lapses.** An unpaid invoice never
  blocks access to money that is already yours.
- **Plans run 30 days with a 3-day grace period.** Renew with
  `POST /v1/plan/renew`; renewing early extends from the current expiry and
  does not rotate the api_key.
- **base-sepolia USDC faucets are captcha-gated.** For testing, TUSD at
  `0x35DA4Ea8430aD45c48702C1fA3dc7237aB83b66d` has an open `mint(address,uint256)`
  anyone can call.

## Errors

Every 4xx body has this shape:

    {"error": "unsupported_token",
     "fix": "NOPE is not supported on base-sepolia; supported: USDC, TUSD"}

`fix` is written to be actionable. **Surface it verbatim** rather than
paraphrasing it into something vaguer.

Ones you will actually hit:

- `402 plan_required` — mainnet without an active plan. Body carries the signup URL.
- `403 network_not_permitted` — a `vig_test_` key asking for a mainnet charge.
- `400 unsupported_token` — that token is not configured on that chain.
- `400 no_rate_available` — the token has no USD quote and cannot be priced.

## Verification checklist

Prove it end to end before reporting success:

1. `GET /v1/me` returns your tier and `plan_status: "active"`.
2. `POST /v1/checkouts` returns a `checkout_url` and at least one option.
3. Sending the exact `amount` to that option's `address` flips the checkout to
   `paid` — verify against `GET /v1/checkouts/<id>`, not against your own logs.
4. Your webhook endpoint received `checkout.paid` and the signature verified.
5. An underpayment does **not** fulfil the order.
6. `GET /v1/collect/quote` returns an unsigned transaction that, once
   broadcast, moves the funds to the payout address.
7. The api_key is stored outside version control.

Do steps 3-6 on base-sepolia first if you want to prove the wiring without
spending real money. Point the same code at a mainnet network afterwards —
only the network name changes.

Full reference: https://usevig.com/llms.txt and https://usevig.com/openapi.json
