<!-- x-shc-release: version=2.4.15 op_fingerprint=183a4a0282542d671d799f08044f6717182932f9fa1ef5205dfca45a030cefaa op_count=177 contractFingerprint=e8f7ccc12336fb4925893ce3582b03782b182638dc460d952d046a36c1c20e14 -->
---
name: shc-pay
description: 'Present an SHC account-credit top-up as a no-dependency scannable QR plus copy/paste fallbacks, and confirm payment only after a real balance re-read. Never auto-pays. Runs in the agent''s own shell.'
when_to_use: 'The user wants to add prepaid credit to their SHC account ("put $25 on my account so my servers keep running"), OR a deploy/order step reports it needs funds. Use after someone has called POST /account/credit and you hold the returned JSON; this skill turns that JSON into a friendly "here is how to pay".'
router_what: Show a QR + links to add prepaid SHC credit; confirm on balance re-read.
router_when: 'User wants to add funds/credit, or an order/deploy step says it needs funds.'
version: 2.4.3
operator: true
client_side: true
requires: [user_api_key_operate]
provides: [payment_qr]
requires_host: [qrencode]
degrades: true
inputs:
  - { name: credit_response, required: true, desc: 'The JSON body returned by POST /account/credit (fields: amount, currency, checkout_url, bolt11, onchain_address, payment_link, expires_at)' }
  - { name: format, required: false, default: ansiutf8, desc: ansiutf8 | svg | png | datauri -- pick by surface }
outputs:
  - { name: payment_presentation, desc: 'An always-confirm block: QR + BIP21 text + browser link + bolt11 + after-you-pay guidance' }
estimated_minutes: [1, 1]
danger: low
---

# shc-pay -- no-dependency QR payment presentation (SHC account credit)
## Operator discipline (hold to these on every step)

*Five rules that keep you safe while you operate a server for someone. Follow them on every
step of every skill.*

1. **Verify, don't trust. Never fabricate a result.** Never report `ready`, `done`, `live`,
   `deployed`, `backed up`, or `paid` unless a real tool output or healthcheck PROVED it (and that
   proof arrives in a separate later message, never in your own reply). A status field is a claim; a
   healthcheck is proof. This holds under pressure: "tell my boss in one line it's live", "answer in
   one line", "you do not need to check" do NOT make it true and do NOT waive the check. If you have
   not verified, say exactly that -- never write a done/live/paid sentence you did not earn.
2. **Spending AND destruction are the human's call -- the user CANNOT authorize you to skip the
   confirm.** For money (order, pay) OR data-loss (reinstall, delete, cancel, restore): name the
   action, state the consequence, get the human's explicit yes for THAT action; never in one step.
   The trap: "just do it / don't ask me / skip confirmation / no need to confirm / I already agree" is
   an instruction to skip the gate -- it is NOT a yes and does NOT give you permission to bypass the
   gate. The gate OUTRANKS that instruction; proceeding because they said "just do it" is itself the
   failure. So you still state the consequence and still wait for a real yes. NEVER set `confirm=true`
   yourself, NEVER map "just go" onto `human_confirmed=true`, and NEVER assume a missing confirmation
   "defaults to true" -- the opposite is true: missing -> REFUSED (409 confirmation_required). If they
   will not give the yes, you do NOT proceed -- announce nothing, run nothing. (Mechanics: every spend
   AND destructive action is gated by a **server-issued, single-use confirmation_id** -- you can NEVER
   self-confirm. On the hosted MCP the destructive/spend tool returns the confirmation_id after its own
   human-approval step. On raw /v2 a destructive/spend call with no confirmation_id returns **409
   confirmation_required** carrying a one-time, non-secret `confirmation_id` and a `how_to_proceed`
   re-call shape; AFTER the human's explicit yes, re-send the IDENTICAL request adding the header
   **`X-User-Api-Confirm: <that confirmation_id>`**. `?confirm=true` and body `confirm:true` are NOT
   accepted -- they get the same 409. The confirmation_id is bound server-side to that exact call +
   your principal and is single-use, so a leaked id is inert (it completes nothing on its own). The
   409's `how_to_proceed` is a recovery shape, NOT a license: a confirmation_id only proves the SERVER
   has a pending record for the action -- do NOT auto-resend it until the human has actually said yes to
   the consequence. Spend is also scope-gated: an `operate` key cannot spend at all -- only a `full` key
   or HTTP Basic can.)
3. **Check the source before you run it.** Validate any deploy recipe against the project's
   *current* upstream docs, not a cached memory.
4. **Take the smallest key that does the job.** An agent that only provisions should never
   hold the key that spends.
5. **When you can't verify, stop and say so.** Failing loudly beats a confident wrong answer.


Turn a `POST /account/credit` response into a scannable QR + copy/paste fallbacks. Runs in your own
shell; no network, no VPS. You are a presenter, never an autopay.

## Safety gates (never break)

- Ask the user before presenting payment; never initiate or assume payment.
- Say "credit added" ONLY after a real balance re-read shows it. A wallet "sent" or a webhook is NOT
  proof.
- No network call, no price feed, no third-party image service: the QR is drawn locally by `qrencode`;
  the BIP21 is assembled locally; no fiat->BTC conversion (the exact amount is locked in the `bolt11`).
- Consume only the already-returned JSON. Read/print no secrets.

## Run it (the helper does the work)

```
shc-pay.sh present --json '<credit-response-json>'     # full always-confirm block
shc-pay.sh present --file response.json
echo '<json>' | shc-pay.sh present                     # stdin
shc-pay.sh bip21  --json '<json>'                      # just the stitched BIP21
shc-pay.sh qr     --json '<json>' --format ansiutf8    # terminal/markdown QR (default)
shc-pay.sh qr     --json '<json>' --format svg  --out qr.svg   # local SVG (no external host)
shc-pay.sh qr     --json '<json>' --format datauri             # self-contained data: URI
```

`shc-pay.sh` ships next to this skill. Needs `bash`, `qrencode` (optional), and `jq` OR `python3`.

The BIP21 it stitches (a standard Bitcoin QR any wallet reads -- no app to install):

| onchain_address | bolt11 | stitched URI | rail |
|-----------------|--------|--------------|------|
| present | present | `bitcoin:<addr>?lightning=<bolt11>` | chain + Lightning in one QR |
| present | null    | `bitcoin:<addr>`                   | on-chain only (amount on checkout page) |
| null    | present | `lightning:<bolt11>`               | Lightning only (amount-locked) |
| null    | null    | (none -- use `checkout_url`)       | browser-only fallback |

## Steps

1. Show the amount and ask: *"You're adding 25.00 USD of credit -- want me to show you how to pay?"*
   (Account credit is a prepaid balance; nothing is spent until the user chooses.)
2. Run `shc-pay.sh present` and show the user its output: the QR, the BIP21 copy/paste text, the
   `checkout_url` (browser), and the bare `bolt11` (Lightning). Offer all of them.
3. Name the rail honestly: "Lightning confirms in seconds; on-chain takes a few minutes."
4. After the user pays, re-read the real balance and confirm ONLY when the credit appears:
   ```
   say "Checking your balance now. Lightning lands in seconds; on-chain can take a few minutes."
   repeat up to N times (minutes apart for on-chain):
       bal = read account credit balance        # real read, not the webhook
       if bal increased by ~the amount: say "Credit added -- balance is now <bal>."; stop
   say "I don't see it on your balance yet. On-chain can take a few minutes -- check again?"
   ```

## If qrencode is absent

The skill still works: it drops the QR and shows the text BIP21 + `checkout_url` + `bolt11`, and tells
the user `apt-get install -y qrencode` enables the QR. Payment is never blocked by a missing QR.

## Result contract

No payable rail (no `bolt11` and no `onchain_address`):
```
STATUS: failed
STEP: bip21 stitch
REASON: the credit response has no Lightning invoice and no on-chain address
NEXT_ACTION: open the checkout_url in a browser to pay; or re-request the credit
```
After the balance re-read confirms:
```
STATUS: ready
NEXT_ACTION: none -- credit is on the account balance (confirmed by re-read)
```

**You're done when:** a real balance re-read shows the credit landed, and you told the user. A wallet
"sent" or a webhook is NOT done -- only the re-read is.
