Penny API
Accept crypto on your site with one API call. You create an order, we hand you a hosted payment page, the customer pays, and the money lands directly in your wallet. Penny is non-custodial: we never hold, touch, or forward your funds, and we never see a private key.
/orders with your API key → get back a pay_url → send your customer there → get a signed webhook when they've paid. That's the whole integration.
Quickstart
- Create your account at https://penny.hifriendbot.com/signup/. Approval is instant and your API key is shown once at signup (you can rotate it any time in the dashboard's Developers tab).
- Add your wallet in the dashboard's Wallets tab. Paste whatever your wallet app gives you: a Bitcoin address, an Ethereum-style 0x address, a Solana address, or an xpub. Auto-detect figures out the rest.
- Create your first order with the call below, open the
pay_urlit returns, and pay yourself a dollar to see the whole flow.
curl -X POST https://penny.hifriendbot.com/wp-json/penny/v1/orders \
-H "X-Penny-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 1.00, "external_ref": "hello-world"}'
Payment links (no code at all)
Not a developer? You never have to touch the API. In your dashboard, open Payment links, type an amount, and get a link you can text, email, or put on an invoice. The customer picks a coin and pays; you watch it confirm in the Orders tab. That's a complete crypto checkout with zero setup.
Authentication
Every merchant API call carries your key in the X-Penny-Key header. Keys start with pg_, are stored hashed on our side, and can be rotated in the dashboard (the old key stops working immediately). Keep it server-side; never put it in front-end code.
-H "X-Penny-Key: pg_your_key_here"
Create an order
POST https://penny.hifriendbot.com/wp-json/penny/v1/orders
| Field | Required | Description |
|---|---|---|
amount | yes | USD amount, e.g. 25.00 |
external_ref | no | Your order ID. Echoed back in webhooks so you can match payments to orders. |
success_url | no | Where we send the customer after payment confirms. |
cancel_url | no | Linked if the customer backs out. |
webhook_url | no | We POST signed payment events here (see Webhooks). |
Response:
{
"order_token": "2f2089583b5c1dc0105eee42b249f60f",
"pay_url": "https://penny.hifriendbot.com/pay/2f2089583b.../",
"status": "pending"
}
Redirect your customer to pay_url. PHP example:
$res = wp_remote_post('https://penny.hifriendbot.com/wp-json/penny/v1/orders', [
'headers' => [
'X-Penny-Key' => PENNY_KEY,
'Content-Type' => 'application/json',
],
'body' => json_encode([
'amount' => 25.00,
'external_ref' => 'order-1042',
'success_url' => 'https://yourstore.com/thanks',
'webhook_url' => 'https://yourstore.com/pg-webhook',
]),
]);
$order = json_decode(wp_remote_retrieve_body($res), true);
wp_redirect($order['pay_url']);
Check an order
GET https://penny.hifriendbot.com/wp-json/penny/v1/orders/{order_token} with your API key returns the order's current state (pending, awaiting, seen, confirmed, expired, underpaid), the coin, and the amount received. Poll it if you'd rather not use webhooks.
The hosted pay page
The pay_url page handles everything customer-facing: coin choice, a locked exchange rate, a fresh or dedicated deposit address, QR code, copy buttons, network warnings ("Send USDC on the Base network only"), live status, and auto-redirect to your success_url on confirmation.
- Customers get 90 minutes to pay, with the rate locked the whole time.
- Underpayments within 2% (sender-side fees, rounding) still count as paid.
- Confirmation speed depends on the chain: seconds on Solana, minutes on Base/Polygon/BNB, ~10–60 minutes on Bitcoin.
Webhooks
If you set a webhook_url, we POST JSON to it on payment.seen (detected on chain) and payment.confirmed (confirmation depth reached; safe to fulfill).
{
"event": "payment.confirmed",
"order_token": "2f20...",
"external_ref": "order-1042",
"status": "confirmed",
"currency": "usdc-base",
"amount_received": "25.0000000000",
"fiat_amount": 25.0,
"txid": "0xabc...",
"timestamp": 1751830000
}
Verify the signature. Every webhook carries X-Penny-Signature: an HMAC-SHA256 of the raw request body using your webhook secret (dashboard → Developers). Reject anything that doesn't match:
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_PENNY_SIGNATURE'] ?? '';
if (!hash_equals(hash_hmac('sha256', $raw, PENNY_WEBHOOK_SECRET), $sig)) {
http_response_code(403); exit;
}
$event = json_decode($raw, true);
if ($event['event'] === 'payment.confirmed') {
// mark your order paid, using $event['external_ref']
}
Webhooks are fire-and-forget with no retries yet, so pair them with a GET /orders/{token} reconciliation check for anything critical.
WooCommerce
Create the order with external_ref set to your WooCommerce order ID and point webhook_url at your store. On payment.confirmed, matching Woo orders are completed automatically. A one-click Woo plugin is on the roadmap; the API above works today.
Coins & networks
| Coin | Network | Typical confirmation |
|---|---|---|
| Bitcoin (BTC) | Bitcoin | 10-60 min |
| Ethereum (ETH) | Ethereum | ~3 min |
| USDC (Ethereum) (USDC) | Ethereum | ~3 min |
| USDT (Ethereum) (USDT) | Ethereum | ~3 min |
| DAI (Ethereum) (DAI) | Ethereum | ~3 min |
| PYUSD (PayPal USD) (PYUSD) | Ethereum | ~3 min |
| USDC (Base) (USDC) | Base | ~1 min |
| USDT (Base) (USDT) | Base | ~1 min |
| Polygon (POL) | Polygon | ~2 min |
| BNB (BNB Chain) (BNB) | BNB Chain | ~1 min |
| PulseChain (PLS) | PulseChain | ~4 min |
| Solana (SOL) | Solana | ~15 sec |
The customer picks the coin on the pay page; you don't choose it at order time. Every coin pays out to the wallet you configured for its chain.
Worth knowing: USDC, USDT, and PYUSD balances can be frozen by their issuers (Circle, Tether, and Paxos) on any chain. Native coins (BTC, ETH, SOL, POL, BNB, PLS) and DAI have no freeze mechanism, so merchants in high-scrutiny industries often prefer them.
Fees & billing
No monthly cost. You pay a small percentage (0.99% founding rate; accounts created now keep that rate for life) of completed sales only, billed after the money is already in your wallet. When your balance reaches $25 or is a week old, you get a one-click settle link, and you can pay any time from the dashboard. Load prepaid credit and get 10% extra free. If a bill goes unpaid for 72 hours, new checkouts pause (in-flight payments always finish); paying instantly reactivates you.
Go-live checklist
- Wallet added for every coin family you want to accept (one 0x covers all EVM chains; add Bitcoin and Solana separately).
- API key stored server-side, never in front-end code.
- Webhook endpoint verifies
X-Penny-Signaturebefore trusting anything. - Run one real payment for a dollar and watch it confirm in the dashboard.
- Fulfill on
payment.confirmed, notpayment.seen.
Need a hand? We offer setup and consulting
Not everyone has a developer, and you shouldn't need one to get paid. We offer consulting and full setup services: we'll connect Penny to your store, wire up your wallets the right way, test a live payment with you, and hand you a working checkout.