Docs — integrate in 60 seconds
Everything you need to accept Bitcoin through LibrePay: hosted checkout, WooCommerce, one HTML snippet, or a clean REST API with signed webhooks. All amounts settle directly to your wallet — or instantly to your own Lightning node.
1 · Supported platforms
LibrePay meets your stack where it is: a hosted link for DMs and email, an HTML button for static sites, a WooCommerce plugin for WordPress, and a first-class REST API for everything else. All rails end in the same place — your wallet.
Hosted checkout link
Email, DMs, social bios, PDF invoices
HTML snippet
Static sites, landing pages, WordPress (no backend)
WooCommerce
WordPress stores — orders & statuses automated
REST API + webhooks
Any stack: SaaS, apps, POS, custom checkouts
2 · Hosted checkout — zero code
Create an invoice in the dashboard (or via API), share the /pay/<id> link or its QR anywhere — Instagram bio, invoice PDF, WhatsApp, email. The page is public-by-link, mobile-first, language-aware and branded with your store name and color.
3 · HTML snippet — one line
Drop the snippet into any site (WordPress, Shopify page, static site). The button opens the hosted checkout in a popup and works with zero backend.
<!-- LibrePay payment button -->
<script src="https://your-domain.com/librepay.js"></script>
<button
class="librepay-button"
data-amount-sats="42000"
data-order-id="order-1042"
data-description="Cairo Coffee">
Pay 42,000 sats
</button>4 · WooCommerce (WordPress)
Official plugin — orders, statuses and webhooks handled for you.
- In WordPress: Plugins → Add New → Upload Plugin → choose the zip → Install → Activate.
- WooCommerce → Settings → Payments → “Bitcoin (LibrePay)” → Manage.
- Paste your API key (Dashboard → API Keys) and your webhook secret. Enable only if your site runs HTTPS.
- Choose when orders are marked paid: on first confirmation (faster delivery) or on settlement (maximum safety).
- Test with a small live order. The order note records the invoice id and payment txid for audit.
Your webhook URL (paste it in LibrePay → Dashboard → Webhooks, then copy the secret into the plugin settings):
https://your-store.com/wc-api/librepay_webhook5 · REST API
Create an API key in Dashboard → API Keys, then call the v1 API server-side. Raw keys are shown once — store them safely.
curl -X POST https://your-domain.com/api/v1/invoices \
-H "Authorization: Bearer lp_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"amountSats": 42000,
"orderId": "order-1042",
"description": "Cairo Coffee — 3 bags",
"expiresInMinutes": 15,
"metadata": { "customer_ref": "c-88" }
}'6 · Webhooks
Add an endpoint in Dashboard → Webhooks. LibrePay signs every delivery with HMAC-SHA256 using the endpoint secret shown once at creation. Verify before trusting:
import crypto from "node:crypto";
export function verifyWebhook(secret: string, rawBody: string, header: string) {
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header || ""));
}Example delivery:
POST https://your-server.com/webhooks/librepay
X-LibrePay-Event: invoice.confirmed
X-LibrePay-Signature: sha256=7f2a…(HMAC-SHA256 of raw body with endpoint secret)
{
"event": "invoice.confirmed",
"invoiceId": "inv_x7Kd2mPq9L",
"timestamp": 1737000000000,
"data": {
"invoice": {
"id": "inv_x7Kd2mPq9L",
"status": "confirmed",
"amountSats": "42000",
"receivedSats": "42000",
"txid": "a1b2c3…",
"confirmations": 2,
"orderId": "order-1042"
}
}
}| waiting | Invoice created, one-time stealth address derived (+ bolt11 if the merchant runs a Phoenixd node). Waiting for buyer. |
| detected | On-chain transaction seen in mempool (0 confirmations). Webhook: invoice.detected. |
| confirmed | Reached the merchant-configured N confirmations (default 2). Webhook: invoice.confirmed. |
| settled | On-chain: deep confirmation (≥6, or N+4). Lightning: INSTANT, no blocks. Webhook: invoice.settled (data.paymentMethod = onchain | lightning). |
| expired | 15-minute window passed with no payment (default, configurable 5–120 min). |
| underpaid | Confirmed transaction paid less than the requested amount. Webhook: invoice.underpaid. |
| refund.requested | Buyer asked for a refund on the invoice page. Webhook: refund.requested. |
| refunding | Merchant approved — refund amount locked, send guide displayed in the dashboard. |
| refunded | Merchant sent the refund from THEIR wallet and pasted the txid. Webhook: refund.completed. |
| declined | Merchant refused the request (optional reason shown to the buyer). Webhook: refund.declined. |
Failed deliveries retry with exponential backoff (1m, 5m, 15m, 1h, 6h, 24h — 7 attempts total), then park as dead in the delivery log.
7 · Lightning (Phoenixd) in 5 steps
Connect YOUR own Phoenixd node (ACINQ's minimal Lightning server) and every new invoice gets a second ⚡ tab: the buyer can pay on-chain or instantly over Lightning. Your keys, your channels, your node — the same non-custodial promise.
- Rent any small VPS and open TCP 9740 (phoenixd's API port) — or keep it private and front it with a reverse proxy.
- Download phoenixd from ACINQ's GitHub releases, unzip, and run it once to generate your node keys and http-password.
- Put phoenixd behind HTTPS (Caddy: two lines of config with an automatic certificate) and note the URL.
- Dashboard → Settings → Lightning: paste the URL and the http-password, press “Connect & test node”.
- Done. Every new invoice now shows the ⚡ Lightning tab; funds arrive to YOUR node in seconds.
phoenix.mystore.com {
reverse_proxy 127.0.0.1:9740
}Then paste https://phoenix.mystore.com + the http-password in Dashboard → Settings → Lightning. We encrypt the password (AES-256-GCM) and only call invoice/create, invoice/lookup and getinfo — never the spending endpoint.8 · Receiving & spending (self-custody)
Your browser generates a BIP39 seed (12 or 24 words) and derives a BIP47-style payment code. Only PUBLIC keys reach our servers, so we can derive one-time stealth addresses for invoices but can never spend. To spend received funds, open Dashboard → Wallet, unlock, and use Export spending keys — it computes per-invoice WIF keys locally (ECDH stealth reveal) for import into Electrum, Sparrow, or any wallet. Prefer external wallets? Use Watch-only mode with an xpub/zpub instead.
9 · Refunds — manual by design
LibrePay cannot refund programmatically — and that is the point: if we could push a refund, we could also push a withdrawal. When a buyer requests a refund on the invoice page, you approve it in Dashboard → Invoices, the amount is locked, and a send guide shows the destination address, the exact sats and a QR. You send from your wallet and paste the txid — the buyer then sees a Refunded badge with an on-chain link. Configure refunds (on/off, request window) in Dashboard → Settings. Tip: prefer the buyer's fresh address over the payment-origin address — exchanges may not credit refunds.