Skip to main content

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​

  1. Log in to app.coinsnap.io
  2. Go to Settings → API Keys
  3. Click Create API Key
  4. Copy the key — you won't see it again

Your Store ID is on the same page, or in the URL when viewing your store.

Your credentials
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.

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/...

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 redirectUrl after 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:

  1. Go to Coinsnap Dashboard → Webhooks
  2. Click Add Webhook and enter your endpoint URL (e.g. https://yoursite.com/webhooks/coinsnap)
  3. Select the Settled event at minimum
  4. Click Save — copy the Signing Secret shown and store it as COINSNAP_WEBHOOK_SECRET

Then handle the request in your server:

server.js (Express)
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');
});
webhook.php
<?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​

  1. Start your server and make sure your webhook endpoint is reachable (use ngrok or npx cloudflared tunnel --url http://localhost:3000 if testing locally)
  2. Create a test invoice through your flow and complete the payment using a Lightning wallet (e.g. Phoenix, Breez)
  3. Check your server logs — you should see the Settled webhook arrive
  4. 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​