Create an invoice
POST the amount, currency and your own order reference. You get back an invoice with a checkout link and an id to store against the order.
Developers
Create an invoice, redirect the customer, handle a webhook. That is the whole integration — the rest is optional.
Integration flow
POST the amount, currency and your own order reference. You get back an invoice with a checkout link and an id to store against the order.
Redirect to the checkout link, or open it in a modal. The page handles rates, the payment window, partial payments and expiry for you.
Verify the signature, look for InvoiceSettled, and fulfil
the order. Retries are automatic until you return a 2xx.
Reference
Examples in the shapes you are most likely to need. Swap in your store id and API key from the dashboard.
// Create an invoice for an order
const response = await fetch(
"https://app.dcogate.org/api/v1/stores/STORE_ID/invoices",
{
method: "POST",
headers: {
"Authorization": "token YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: "149.00",
currency: "USD",
metadata: {
orderId: "A-10427",
buyerEmail: "[email protected]"
},
checkout: {
redirectURL: "https://yourshop.com/thanks",
expirationMinutes: 30
}
})
}
);
const invoice = await response.json();
// Store invoice.id against the order, then send the customer on
await orders.update("A-10427", { invoiceId: invoice.id });
redirect(invoice.checkoutLink);
{
"id": "inv_8Fq2mKp4Zx",
"storeId": "str_4Rt9Vn",
"status": "New",
"amount": "149.00",
"currency": "USD",
"checkoutLink": "https://app.dcogate.org/i/inv_8Fq2mKp4Zx",
"createdTime": 1771027200,
"expirationTime": 1771029000,
"metadata": {
"orderId": "A-10427",
"buyerEmail": "[email protected]"
}
}
import crypto from "node:crypto";
// Always verify the signature before trusting the payload
function verify(rawBody, header, secret) {
const digest = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(digest),
Buffer.from(header)
);
}
app.post("/webhooks/dcogate", (req, res) => {
const signature = req.header("DCOGate-Sig");
if (!verify(req.rawBody, signature, process.env.WEBHOOK_SECRET)) {
return res.sendStatus(400);
}
if (req.body.type === "InvoiceSettled") {
fulfilOrder(req.body.metadata.orderId);
}
// Return 2xx or we will retry
res.sendStatus(200);
});
<!-- Drop this anywhere. No JavaScript required. -->
<form
method="POST"
action="https://app.dcogate.org/api/v1/invoices"
>
<input type="hidden" name="storeId" value="STORE_ID" />
<input type="hidden" name="price" value="24.00" />
<input type="hidden" name="currency" value="USD" />
<input type="hidden" name="redirectUrl" value="https://yourshop.com/thanks" />
<button type="submit">Pay with crypto</button>
</form>
curl -X POST \
https://app.dcogate.org/api/v1/stores/STORE_ID/invoices \
-H "Authorization: token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "149.00",
"currency": "USD",
"metadata": { "orderId": "A-10427" }
}'
Webhook events
An invoice was opened and is waiting for payment.
A payment appeared on the network but is not yet confirmed.
Payment confirmed and final. This is the one to fulfil orders on.
The payment window closed without full payment.
The full amount arrived and is awaiting confirmations.
The payment could not be accepted — underpaid or too late.
Good practice
Compare the HMAC signature against your secret with a timing-safe check before you read the payload. An unverified webhook is just an HTTP request from anyone.
Webhooks retry, and a delivery can arrive twice. Key your fulfilment on the invoice id so a repeat delivery is a no-op rather than a second shipment.
InvoiceReceivedPayment means a transaction was seen, not
that it confirmed. Ship on InvoiceSettled.
Anything you attach comes back on every webhook and every export, which turns reconciliation into a lookup instead of an investigation.
Create a store, point it at your own wallet and take your first payment today. No contracts, no monthly minimum, no cut of your first fifty thousand dollars.