Démarrage rapide
Un premier paiement en sandbox en 15 minutes. Il vous faut un serveur Node.js, PHP, Python ou Go : les exemples couvrent les quatre.
1. Obtenez vos clés sandbox
- Inscrivez-vous sur le tableau de bord sandbox et activez la 2FA.
- « Clés et webhooks » → « Créer une clé » : conservez le
key_id(ak_…) et le secret. - « Clés et webhooks » → « Ajouter un endpoint » : indiquez l’URL de votre gestionnaire, par exemple
https://shop.example/webhooks/coinetic(en sandbox,http://localhost:3000/…est accepté). Conservez le secret du webhook.
Les secrets ne s’affichent qu’une seule fois. Placez-les dans les variables d’environnement du serveur :
API_KEY=ak_...
API_SECRET=...
WEBHOOK_SECRET=...2. Téléchargez le client
Le client tient en un seul fichier, sans dépendances externes : signature des requêtes, création et lecture des factures, vérification des webhooks. Télécharger pour votre langage →
3. Créez une facture
Le prix est une chaîne, en USD, EUR, USDT ou USDC. Dérivez l’Idempotency-Key du numéro de commande : une nouvelle tentative après un timeout renverra la même facture.
import { CoineticClient } from "./cryptopay.js";
const cp = new CoineticClient({ baseUrl: "https://sandbox.coinetic.money", keyId: process.env.API_KEY, secret: process.env.API_SECRET });
const invoice = await cp.createInvoice({
order_id: "ORDER-1001", // votre numéro de commande, unique par marchand
price_amount: "25.00", // une chaîne, pas un nombre
price_currency: "USD",
return_url: "https://shop.example/orders/ORDER-1001",
metadata: { user_id: "42" },
}, "order-ORDER-1001"); // Idempotency-Key
console.log(invoice.checkout_url); // https://sandbox.coinetic.money/pay/inv_…#t=…require 'Cryptopay.php';
$cp = new \Cryptopay\Client('https://sandbox.coinetic.money', getenv('API_KEY'), getenv('API_SECRET'));
$invoice = $cp->createInvoice([
'order_id' => 'ORDER-1001',
'price_amount' => '25.00',
'price_currency' => 'USD',
'return_url' => 'https://shop.example/orders/ORDER-1001',
'metadata' => ['user_id' => '42'],
], 'order-ORDER-1001');
echo $invoice['checkout_url'];import os
from cryptopay import CoineticClient
cp = CoineticClient("https://sandbox.coinetic.money", os.environ["API_KEY"], os.environ["API_SECRET"])
invoice = cp.create_invoice({
"order_id": "ORDER-1001",
"price_amount": "25.00",
"price_currency": "USD",
"return_url": "https://shop.example/orders/ORDER-1001",
"metadata": {"user_id": "42"},
}, "order-ORDER-1001")
print(invoice["checkout_url"])cp := &cryptopayclient.Client{
BaseURL: "https://sandbox.coinetic.money",
KeyID: os.Getenv("API_KEY"),
Secret: os.Getenv("API_SECRET"),
HTTP: &http.Client{Timeout: 10 * time.Second},
}
inv, err := cp.CreateInvoice(ctx, cryptopayclient.CreateInvoice{
OrderID: "ORDER-1001",
PriceAmount: "25.00",
PriceCurrency: "USD",
ReturnURL: "https://shop.example/orders/ORDER-1001",
Metadata: map[string]string{"user_id": "42"},
}, "order-ORDER-1001")
if err != nil {
return err
}
fmt.Println(inv.CheckoutURL)4. Envoyez le client vers le paiement
Redirigez le navigateur vers invoice.checkout_url (HTTP 303). Le client choisit sa devise et voit l’adresse, le montant et le QR code. En sandbox, des boutons de paiement test s’affichent sous les coordonnées de paiement : « Payer le montant exact », « Sous-paiement de 10 % », « Surpaiement de 10 % », « Moitié du montant », « Payer le solde ».
5. Recevez le webhook
L’ordre est toujours le même : vérifier la signature → écarter les doublons → revérifier le statut via l’API → livrer la commande une seule fois → répondre 2xx.
import express from "express";
import { verifyWebhook } from "./cryptopay.js";
// cp : le client de l’étape 3. alreadyProcessed / markProcessed / fulfil : vos propres fonctions.
const app = express();
// express.raw : la signature porte sur les octets bruts, n’activez pas express.json() sur cette route
app.post("/webhooks/coinetic", express.raw({ type: "application/json" }), async (req, res) => {
let event;
try {
event = verifyWebhook(process.env.WEBHOOK_SECRET, req.headers, req.body);
} catch (e) {
return res.status(e.status ?? 400).end(); // 401 : signature invalide, 400 : timestamp trop ancien
}
try {
const eventId = req.headers["x-webhook-id"];
if (await alreadyProcessed(eventId)) return res.sendStatus(200);
const inv = await cp.getInvoice(event.data.invoice_id); // le statut vient de l’API, pas du webhook
if (inv.status === "paid" || inv.status === "overpaid") await fulfil(inv.order_id);
await markProcessed(eventId);
res.sendStatus(200);
} catch (e) {
console.error(e);
res.sendStatus(500); // hors 2xx, Coinetic relancera la livraison
}
});
app.listen(3000);require 'Cryptopay.php';
$headers = array_change_key_case(getallheaders(), CASE_LOWER);
$raw = file_get_contents('php://input'); // corps brut, avant json_decode
try {
$event = \Cryptopay\verify_webhook(getenv('WEBHOOK_SECRET'), $headers, $raw);
} catch (\Cryptopay\CryptopayException $e) {
http_response_code($e->status);
exit;
}
if (already_processed($headers['x-webhook-id'])) { http_response_code(200); exit; }
$inv = $cp->getInvoice($event['data']['invoice_id']); // le statut vient de l’API
if (in_array($inv['status'], \Cryptopay\PAID_STATUSES, true)) {
fulfil($inv['order_id']);
}
mark_processed($headers['x-webhook-id']);
http_response_code(200);import os
from flask import Flask, request
from cryptopay import verify_webhook, CryptopayError, PAID_STATUSES
app = Flask(__name__)
@app.post("/webhooks/coinetic")
def webhook():
headers = {k.lower(): v for k, v in request.headers.items()}
try:
event = verify_webhook(os.environ["WEBHOOK_SECRET"], headers, request.get_data())
except CryptopayError as e:
return "", e.status
if already_processed(headers["x-webhook-id"]):
return "", 200
inv = cp.get_invoice(event["data"]["invoice_id"]) # le statut vient de l’API
if inv["status"] in PAID_STATUSES:
fulfil(inv["order_id"])
mark_processed(headers["x-webhook-id"])
return "", 200func webhook(w http.ResponseWriter, r *http.Request) {
raw, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1<<20))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if err := cryptopayclient.VerifyWebhook(os.Getenv("WEBHOOK_SECRET"), r.Header, raw, time.Now(), 5*time.Minute); err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
var ev struct {
Data struct{ InvoiceID string `json:"invoice_id"` } `json:"data"`
}
if err := json.Unmarshal(raw, &ev); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
inv, err := cp.GetInvoice(r.Context(), ev.Data.InvoiceID) // le statut vient de l’API
if err != nil {
w.WriteHeader(http.StatusInternalServerError) // Coinetic relancera
return
}
if cryptopayclient.PaidStatuses[inv.Status] {
fulfilOnce(inv.OrderID)
}
w.WriteHeader(http.StatusOK)
}6. Renvoyez le client vers votre site
Après le paiement, la page affiche un bouton « Retour à la boutique » qui mène à votre return_url. Affichez-y le statut de la commande tiré de votre propre base. Si le webhook n’est pas encore arrivé, revérifiez via GET /v1/invoices/{id}.
Et ensuite ?
- Testez tous les scénarios en sandbox : sous-paiement, surpaiement, facture expirée.
- Traitez les autres événements :
underpaid,expired,reorged. - Suivez la checklist de mise en production avant d’encaisser de vrais paiements.