Integration recipes
Copy/paste examples for common backends.
Recipes follow the same pattern: create an invoice with a WOW amount, send the customer to the hosted invoice page, then fulfill your order when the invoice is confirmed.
Conceptual flow
- Create an invoice via POST /api/core/invoices.
- Optional: include checkout_continue_url to show a customer-facing Continue button after confirmation.
- Redirect the customer to invoice_url.
- Wait for invoice.confirmed via webhook (recommended) or polling.
What to store
- Your internal order id in metadata.order_id
- The returned invoice.id
Environment variables
Keep secrets server-side. Never expose API keys or webhook secrets in browser code.
WOWCHECKOUT_API_KEY
Used to call authenticated endpoints.
WOWCHECKOUT_WEBHOOK_SECRET
Used to verify the X-Webhook-Secret header on webhook deliveries.
WOWCHECKOUT_API_BASE_URL
Public base URL, including /api/core: https://wowcheckout.such.software/api/core
BTCPay compatibility (WooCommerce)
Use the official WooCommerce Greenfield plugin with the compatibility endpoints. Keys remain view-only, and invoices stay non-custodial.
Setup steps
- Sign in to WOW Checkout to get your API key.
- Store id: returned by the login response or via GET /api/v1/stores. The stores call returns a single store (one per primary address); use the id field.
- Install the official BTCPay WooCommerce plugin.
- In WooCommerce → BTCPay settings, set:
- Server URL: https://wowcheckout.such.software (the plugin appends /api/v1).
- Store ID: the id returned from the stores call.
- API key: your WOW Checkout API key.
- Payment method: WOW-CHAIN (shows as WOW_CHAIN in WooCommerce).
- Modal checkout is supported; the hosted invoice page is recommended for clarity.
Behavior notes
- Authorization header: Authorization: token <api_key>.
- Status mapping: pending → New, payment detected → Processing, confirmed → final.
- Expired and Invalid statuses map directly.
- Webhook deliveries use the BTCPay-Sig header.
# Verify compatibility endpoints
export WOWCHECKOUT_BTCPAY_URL="https://wowcheckout.such.software/api/v1"
export WOWCHECKOUT_API_KEY="wowcheckout_..."
curl -sS "$WOWCHECKOUT_BTCPAY_URL/stores" \
-H "Authorization: token $WOWCHECKOUT_API_KEY"curl
Create an invoice, then poll status until it is confirmed.
# 1) Create invoice
export WOWCHECKOUT_API_BASE_URL="https://wowcheckout.such.software/api/core"
export WOWCHECKOUT_API_KEY="wowcheckout_..."
curl -sS -X POST "$WOWCHECKOUT_API_BASE_URL/invoices" \
-H "Authorization: ApiKey $WOWCHECKOUT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount_xmr": "0.15",
"confirmation_target": 2,
"metadata": { "order_id": "ORDER-1234" }
}'# 1b) Create invoice from fiat (non-binding conversion)
curl -sS -X POST "$WOWCHECKOUT_API_BASE_URL/invoices" \
-H "Authorization: ApiKey $WOWCHECKOUT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount_fiat": "100.00",
"currency": "USD",
"confirmation_target": 2,
"metadata": { "order_id": "ORDER-1234" }
}'# 2) Poll public status (no auth)
export INVOICE_ID="uuid-from-response"
curl -sS "$WOWCHECKOUT_API_BASE_URL/public/invoice/$INVOICE_ID"Node.js
Example shows creating an invoice with fetch and receiving webhooks with Express.
// create-invoice.mjs
const API_BASE_URL = process.env.WOWCHECKOUT_API_BASE_URL;
const API_KEY = process.env.WOWCHECKOUT_API_KEY;
export async function createInvoice({ amountXmr, orderId }) {
const response = await fetch(`${API_BASE_URL}/invoices`, {
method: "POST",
headers: {
Authorization: `ApiKey ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount_xmr: String(amountXmr),
confirmation_target: 10,
metadata: { order_id: orderId },
}),
});
if (!response.ok) {
const text = await response.text().catch(() => "");
throw new Error(`wowcheckout create invoice failed: ${response.status} ${text}`);
}
return await response.json(); // includes invoice_url
}// webhook-server.mjs
import express from "express";
const WEBHOOK_SECRET = process.env.WOWCHECKOUT_WEBHOOK_SECRET;
const app = express();
app.use(express.json({ type: "application/json" }));
app.post("/wowcheckout/webhook", (req, res) => {
const headerSecret = req.get("x-webhook-secret") ?? "";
if (!WEBHOOK_SECRET || headerSecret !== WEBHOOK_SECRET) {
return res.sendStatus(401);
}
const event = req.body?.event;
const invoice = req.body?.invoice;
const orderId = invoice?.metadata?.order_id;
if (event === "invoice.confirmed" && orderId) {
// Mark your order paid here.
}
return res.sendStatus(204);
});
app.listen(3001, () => {
console.log("Listening on http://localhost:3001/wowcheckout/webhook");
});PHP
Minimal create-invoice example and a webhook receiver endpoint.
<?php
// create_invoice.php
$apiBaseUrl = getenv("WOWCHECKOUT_API_BASE_URL");
$apiKey = getenv("WOWCHECKOUT_API_KEY");
$payload = json_encode([
"amount_xmr" => "0.15",
"confirmation_target" => 10,
"metadata" => ["order_id" => "ORDER-1234"],
]);
$ch = curl_init($apiBaseUrl . "/invoices");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: ApiKey " . $apiKey,
"Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($body === false || $status < 200 || $status >= 300) {
http_response_code(500);
echo "wowcheckout error: HTTP " . $status;
exit;
}
$invoice = json_decode($body, true);
echo $invoice["invoice_url"];<?php
// webhook.php
$webhookSecret = getenv("WOWCHECKOUT_WEBHOOK_SECRET");
$headerSecret = $_SERVER["HTTP_X_WEBHOOK_SECRET"] ?? "";
if (!$webhookSecret || $headerSecret !== $webhookSecret) {
http_response_code(401);
exit;
}
$raw = file_get_contents("php://input");
$payload = json_decode($raw, true);
$event = $payload["event"] ?? null;
$invoice = $payload["invoice"] ?? null;
$orderId = $invoice["metadata"]["order_id"] ?? null;
if ($event === "invoice.confirmed" && $orderId) {
// Mark your order paid here.
}
http_response_code(204);Python
Create an invoice with requests and receive webhooks with FastAPI.
# create_invoice.py
import os
import requests
api_base_url = os.environ["WOWCHECKOUT_API_BASE_URL"].rstrip("/")
api_key = os.environ["WOWCHECKOUT_API_KEY"]
response = requests.post(
f"{api_base_url}/invoices",
headers={"Authorization": f"ApiKey {api_key}"},
json={
"amount_xmr": "0.15",
"confirmation_target": 2,
"metadata": {"order_id": "ORDER-1234"},
},
timeout=10,
)
response.raise_for_status()
invoice = response.json()
print(invoice["invoice_url"])# webhook_server.py
import os
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
webhook_secret = os.environ["WOWCHECKOUT_WEBHOOK_SECRET"]
@app.post("/wowcheckout/webhook", status_code=204)
async def wowcheckout_webhook(payload: dict, x_webhook_secret: str | None = Header(default=None)):
if not x_webhook_secret or x_webhook_secret != webhook_secret:
raise HTTPException(status_code=401, detail="Invalid webhook secret")
event = payload.get("event")
invoice = payload.get("invoice") or {}
metadata = invoice.get("metadata") or {}
order_id = metadata.get("order_id")
if event == "invoice.confirmed" and order_id:
# Mark your order paid here.
pass
return NoneGo
Create an invoice with net/http and receive webhooks with a standard handler.
// create_invoice.go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)
type invoiceResponse struct {
ID string `json:"id"`
InvoiceURL string `json:"invoice_url"`
}
func main() {
apiBaseURL := strings.TrimRight(os.Getenv("WOWCHECKOUT_API_BASE_URL"), "/")
apiKey := os.Getenv("WOWCHECKOUT_API_KEY")
payload := map[string]any{
"amount_xmr": "0.15",
"confirmation_target": 2,
"metadata": map[string]any{
"order_id": "ORDER-1234",
},
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", apiBaseURL+"/invoices", bytes.NewReader(body))
req.Header.Set("Authorization", "ApiKey "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
data, _ := io.ReadAll(resp.Body)
panic(fmt.Sprintf("wowcheckout create invoice failed: %d %s", resp.StatusCode, string(data)))
}
var invoice invoiceResponse
if err := json.NewDecoder(resp.Body).Decode(&invoice); err != nil {
panic(err)
}
fmt.Println(invoice.InvoiceURL)
}// webhook_server.go
package main
import (
"encoding/json"
"net/http"
"os"
)
type webhookPayload struct {
Event string `json:"event"`
Invoice struct {
Metadata map[string]any `json:"metadata"`
} `json:"invoice"`
}
func main() {
webhookSecret := os.Getenv("WOWCHECKOUT_WEBHOOK_SECRET")
http.HandleFunc("/wowcheckout/webhook", func(w http.ResponseWriter, r *http.Request) {
headerSecret := r.Header.Get("X-Webhook-Secret")
if webhookSecret == "" || headerSecret != webhookSecret {
w.WriteHeader(http.StatusUnauthorized)
return
}
var payload webhookPayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if payload.Event == "invoice.confirmed" {
if orderID, ok := payload.Invoice.Metadata["order_id"].(string); ok && orderID != "" {
// Mark your order paid here.
_ = orderID
}
}
w.WriteHeader(http.StatusNoContent)
})
_ = http.ListenAndServe(":3001", nil)
}