# Pay with the user's card from an agent (MPP): buy directly, or add credit

Everything you buy over the Porkbun API is paid from prepaid **account credit**.
When an order fails with `INSUFFICIENT_FUNDS`, an AI agent can top the account up
with the **user's card** by paying a Porkbun payment link over **MPP**, the
Machine Payments Protocol (an open standard from Stripe and Tempo, mpp.dev). The
card stays in the user's Stripe agent wallet (the Link Agent Wallet): no card
needs to be saved at Porkbun, and the user approves the spend there.

If the agent has a crypto wallet instead, see
[Pay with USDC (x402)](https://porkbun.com/llms/guides/pay-with-usdc-x402).

An agent can also skip the credit and **pay for one purchase directly by card,
with a real hold**: the card is authorized first and charged only if the
purchase succeeds. See the next section.

## Pay for a purchase directly (authorize, then capture)

Add `"payWith": "card"` to `/domain/create`, `/domain/renew`,
`/domain/transfer`, `/closeout/buy` or a paid `/hosting/create`. No account
credit is needed.

1. Every normal check runs first (availability, price, eligibility, the
   monthly spend limit). Then the call answers **HTTP 402** `PAYMENT_REQUIRED`
   with `method: "card"`, a single-use `payUrl` for exactly that purchase and
   price, and a `cardPaymentId`. Nothing is charged. Asking again within the
   link's 30 minutes returns the same link.
2. Pay the `payUrl` with an MPP wallet. With Stripe's Link CLI it takes two
   commands:
   - `link-cli mpp pay <payUrl> -X POST --context "..."` (at least 100
     characters, saying what it is for and the amount) creates a spend
     request (`lsrq_...`) and shows an approval link. The person approves in
     Link.
   - Once it is approved, complete the payment with
     `link-cli mpp pay <payUrl> --spend-request-id <lsrq_id> -X POST`.

   This **authorizes** the card: a hold, not a charge.

   First time with the Link CLI? If it answers `NOT_AUTHENTICATED`, run
   `link-cli auth login` (a one-time device login the person approves), then
   pay again. An agent without a shell (a chat app) can use Link's own MCP
   server instead, where its client can run one: `npx -y @stripe/link-cli --mcp`,
   or `link-cli mcp add` to register it. With neither, card payment is not
   available to it; use credit, or a USDC `payUrl` the person pays in a
   browser.

   The link works until `expiresAt` (30 minutes). If that passes before it is
   paid (a slow approval), send the purchase again **without** `cardPaymentId`
   for a fresh link; do not pay the old one.
3. Repeat the same purchase request with `"cardPaymentId": "<id>"` added.
   - **The purchase succeeds:** the hold is captured; the response includes
     `payment` (`paidWith: "card"`, the amount and the `paymentIntent`).
   - **The purchase fails** (the name was taken, the registry refused it): the
     hold is released and **nothing is charged**. The error includes `payment`
     with `charged: false` and `holdReleased: true`.

If the repeat is refused before the purchase starts, for a reason that will not
change on a retry (the name was taken meanwhile, the price changed, the account
cannot buy), the hold is released at once and the error says so in `payment`
(`charged: false`, `holdReleased: true`). For errors a retry can fix (a rate
limit, an unverified account, Stripe briefly unreachable), the hold is kept so
you can repeat with the same `cardPaymentId`. Repeat the purchase unchanged:
a different `cost` counts as a changed price.

A hold nobody comes back for is released after an hour. Answers to the repeat:

- `PAYMENT_REQUIRED` (402): the link has not been paid yet.
- `PAYMENT_MISMATCH` (409): the request differs from the one that returned the
  link. Repeat it unchanged.
- `PAYMENT_NOT_FOUND` (404): no open card payment with that id (they last two
  hours).
- `PAYMENT_ALREADY_USED` (409): it already paid for a purchase. Do not pay again.
- `PAYMENT_EXPIRED` (409): the link expired unpaid, or the hold was released
  before it was used. Send the purchase without `cardPaymentId` for a new link.
- `PAYMENT_IN_PROGRESS` (409): another request is processing it.

Card top-up limits do not apply here (this is a purchase, not a top-up); the
monthly API spend limit does. Not available on sandbox keys.

## Add credit instead: how a top-up link works

1. Ask Porkbun for a payment link (`POST /account/topupMpp`). Nothing is charged.
2. The agent's MPP client requests the link and gets HTTP **402** with a
   `WWW-Authenticate: Payment` challenge: the amount, `method="stripe"`,
   `intent="charge"`, and Porkbun's Stripe profile.
3. The agent's wallet mints a **Shared Payment Token** for the user's card,
   limited to that amount, and the client retries with it as the credential.
4. Porkbun charges the token and adds the credit immediately. The response has
   the new balance and a `Payment-Receipt` header; the account holder gets a
   receipt email.

## 1. Get a payment link

```
POST /api/json/v3/account/topupMpp
{"apikey":"pk1_...","secretapikey":"sk1_...","amount":2000}
```

`amount` is integer US cents, from 500 ($5.00) to 50000 ($500.00). Send
`"dryRun": true` to check eligibility and limits without creating a link.

```json
{
  "status": "SUCCESS",
  "amount_cents": 2000,
  "payUrl": "https://porkbun.com/pay/topup/...",
  "expiresAt": "2026-10-02T01:10:41Z",
  "protocol": "mpp"
}
```

The link works once, for this account and amount, and expires after 15 minutes.

## 2. Pay it

Any MPP client with a Stripe agent wallet can pay it. With Stripe's Link CLI
(also available as an MCP server for agents):

```
npx @stripe/link-cli auth login
npx @stripe/link-cli mpp pay "<payUrl>" -X POST --context "Adding $20.00 of prepaid credit to my Porkbun account so my agent can register domains."
```

`--context` (at least 100 characters) is shown to the user when Link asks them
to approve the spend. Newer versions of the Link CLI stop after creating the spend request: once
the user approves, run `link-cli mpp pay "<payUrl>" --spend-request-id <lsrq_id> -X POST`
to complete the payment. Tell the user the dollar amount before paying.

A successful payment answers:

```json
{"status":"SUCCESS","credited_cents":2000,"credited":"$20.00","balance_cents":3480,"balance":"$34.80","paymentIntent":"pi_..."}
```

Then retry the order that needed the credit, unchanged.

## From the MCP server

On the full server (`https://mcp.porkbun.com/mcp`, or the npm package):

- **Pay for a purchase directly:** `register_domain`, `renew_domain`,
  `transfer_domain`, `buy_closeout` and `create_hosting` take
  `pay_with_card: true`. The tool returns `PAYMENT_REQUIRED` with the `payUrl`;
  pay it with an MPP wallet tool (Link's CLI or MCP server), then call the same
  tool again with `card_payment_id`.
- **Add credit instead:** `top_up_with_card_mpp` returns a top-up link, which
  the agent pays the same way. It is not offered on the versions in the Claude and
ChatGPT app directories, which do not move money.

## Limits and errors

The same rules as card top-ups (`POST /account/topup`), because this is a card
charge too:

- $5 to $500 per top-up, up to the account's monthly ceiling (its monthly spend
  limit, or $100 a month when none is set; card, MPP and USDC top-ups all count
  toward it), 5 a day and 20 a month (`TOPUP_LIMIT_EXCEEDED`).
- Email and phone verified (`VERIFICATION_REQUIRED`; an agent can verify both:
  https://porkbun.com/llms/guides/verify-the-account); not a bank-transfer-only
  account (`MPP_NOT_AVAILABLE`).
- Not on sandbox keys (`SANDBOX_UNSUPPORTED`): use `/sandbox/topup`.

When the link itself answers 402 a second time, its `detail` says why (the card
was declined, the link was already used, a limit was reached) and nothing was
charged.

## Other ways to add credit

- `POST /account/topup` charges a card already saved at Porkbun (ask first).
- `POST /account/topupCrypto`: USDC on Base, payable over x402.
- On porkbun.com, Account Credit: card, bank transfer or crypto.


---

## More

- Guides (how-tos): https://porkbun.com/llms/guides
- Topic index: https://porkbun.com/llms
- Full reference (one file): https://porkbun.com/llms-full.txt
- OpenAPI spec (full schemas): https://porkbun.com/api/json/v3/spec
- Short overview: https://porkbun.com/llms.txt
- Official MCP server: https://porkbun.com/mcp (`npx -y @porkbunllc/mcp-server`)
- Create API keys: https://porkbun.com/account/api
