Quickstart
Accept your first Bitcoin payment in 15 minutes. This guide walks you through the exact flow your integration will use in production.
What you'll build
1. Get API key → 2. Create invoice → 3. Show QR code
↓
5. Handle webhook ← 4. Customer pays ←─────────┘
Prerequisites
- A Coinsnap account with a store created
- Your Store ID and API Key from the dashboard
Step 1 — Get your API key
- Log in to app.coinsnap.io
- Go to Settings → Store
- Scroll to the API Keys section, enter a label, and click Create API key
- Copy the key from the table
Your Store ID is on the same page, or in the URL when viewing your store.
Store ID: 7CVKXVxM7BtbkMEie8yoNeR8EetExpQhJUYEFY3ftfwR
API Key: cs_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Step 2 — Create an invoice
An invoice is a Bitcoin payment request. You create one for a specific amount, and Coinsnap generates a Lightning invoice and Bitcoin address for your customer.
- Node.js
- PHP
- Python
- cURL
const response = await fetch(
'https://app.coinsnap.io/api/v1/stores/YOUR_STORE_ID/invoices',
{
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 10.00, // in your store's fiat currency (e.g. EUR)
currency: 'EUR',
orderId: 'order-123', // your internal order reference
buyerEmail: 'customer@example.com',
redirectUrl: 'https://yoursite.com/thank-you',
}),
}
);
const invoice = await response.json();
console.log(invoice.id); // inv_xxxxxxxxxxxx
console.log(invoice.checkoutLink); // https://app.coinsnap.io/i/...
$payload = [
'amount' => 10.00,
'currency' => 'EUR',
'orderId' => 'order-123',
'buyerEmail' => 'customer@example.com',
'redirectUrl' => 'https://yoursite.com/thank-you',
];
$ch = curl_init('https://app.coinsnap.io/api/v1/stores/YOUR_STORE_ID/invoices');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-api-key: YOUR_API_KEY',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$invoice = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $invoice['id']; // inv_xxxxxxxxxxxx
echo $invoice['checkoutLink']; // https://app.coinsnap.io/i/...
import requests
invoice = requests.post(
'https://app.coinsnap.io/api/v1/stores/YOUR_STORE_ID/invoices',
headers={
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
json={
'amount': 10.00,
'currency': 'EUR',
'orderId': 'order-123',
'buyerEmail': 'customer@example.com',
'redirectUrl': 'https://yoursite.com/thank-you',
},
).json()
print(invoice['id']) # inv_xxxxxxxxxxxx
print(invoice['checkoutLink']) # https://app.coinsnap.io/i/...
curl -X POST \
'https://app.coinsnap.io/api/v1/stores/YOUR_STORE_ID/invoices' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"amount": 10.00,
"currency": "EUR",
"orderId": "order-123",
"buyerEmail": "customer@example.com",
"redirectUrl": "https://yoursite.com/thank-you"
}'
Response
{
"id": "inv_4Kz9mXpQ2rNvBtYwLs8cDf",
"status": "New",
"amount": 10.00,
"currency": "EUR",
"checkoutLink": "https://app.coinsnap.io/i/4Kz9mXpQ2rNvBtYwLs8cDf",
"lightningInvoice": "lnbc100u1p...",
"onchainAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"bip21": "bitcoin:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh?amount=0.00010000",
"createdAt": 1705312800
}
Step 3 — Display the payment UI
The simplest approach is to redirect your customer to checkoutLink. Coinsnap hosts the payment page, including QR code display and real-time status updates.
// Redirect customer to the hosted checkout page
window.location.href = invoice.checkoutLink;
The page automatically handles:
- Lightning QR code display
- On-chain fallback
- Real-time payment detection
- Redirect to your
redirectUrlafter payment
Step 4 — Handle the webhook
When the customer pays, Coinsnap sends a POST request to your webhook endpoint. This is how you confirm payment server-side — never rely on the redirect alone.
First, register your endpoint in the dashboard:
- Go to Coinsnap Dashboard → Webhooks
- Click Add Webhook and enter your endpoint URL (e.g.
https://yoursite.com/webhooks/coinsnap) - Select the Settled event at minimum
- Click Save — copy the Signing Secret shown and store it as
COINSNAP_WEBHOOK_SECRET
Then handle the request in your server:
import crypto from 'crypto';
import express from 'express';
const app = express();
app.use(express.raw({ type: 'application/json' }));
app.post('/webhooks/coinsnap', async (req, res) => {
const signature = req.headers['x-coinsnap-sig'] ?? '';
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.COINSNAP_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
const sigBuf = Buffer.from(signature.padEnd(expected.length));
const expBuf = Buffer.from(expected);
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
if (event.type === 'Settled') {
const invoiceId = event.invoiceId;
const orderId = event.metadata?.orderId;
await markOrderAsPaid(orderId, invoiceId);
}
res.status(200).send('OK');
});
<?php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_COINSNAP_SIG'] ?? '';
$secret = getenv('COINSNAP_WEBHOOK_SECRET');
if (!hash_equals('sha256=' . hash_hmac('sha256', $payload, $secret), $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
if ($event['type'] === 'Settled') {
$invoiceId = $event['invoiceId'];
$orderId = $event['metadata']['orderId'] ?? null;
// mark order as paid in your DB
}
http_response_code(200);
echo 'OK';
Step 5 — Test it end to end
- Start your server and make sure your webhook endpoint is reachable (use ngrok or
npx cloudflared tunnel --url http://localhost:3000if testing locally) - Create a test invoice through your flow and complete the payment using a Lightning wallet (e.g. Phoenix, Breez)
- Check your server logs — you should see the
Settledwebhook arrive - Verify your order status updated in the database
If the webhook doesn't arrive:
- Check Dashboard → Webhooks → [your webhook] → Deliveries for delivery attempts and error responses
- Make sure your server is returning
200 OK - Confirm the signing secret is correct and you're reading the raw request body (not parsed JSON)
You're done
Your integration covers:
- Create an invoice for any fiat amount
- Redirect customers to the hosted payment page
- Verify payments server-side via signed webhooks
Next steps
- Webhook events reference — all event types and payload schemas
- Verify webhook signatures — detailed security guide
- Accept Bitcoin payments guide — full production-ready integration
- API Reference — complete endpoint documentation