Mivora

API de paiement

Encaisse Bitcoin et Litecoin sur ton site. L'argent va directement du client à ton wallet : Mivora ne reçoit que ta clé publique, surveille la blockchain et prévient ton site. Sans ta phrase de récupération, personne ne peut dépenser tes paiements, pas même Mivora.

Shield entreprises · Shield for business →

Démarrer

Le plus simple : dans l'appli Mivora, ouvre Espace vendeur et crée ta boutique. Tu obtiens ta clé API et ton secret de signature, et l'écran te permet de récupérer les paiements reçus. Les fonds arrivent sur un compte dédié (m/84'/0'/1' en Bitcoin, m/84'/2'/1' en Litecoin).

Avec un autre wallet, enregistre sa clé de compte BIP84 (zpub) :

curl https://api.sovrawallet.eu/v1/merchants \
  -H 'content-type: application/json' \
  -d '{"name":"My shop","keys":{"btc":"zpub..."},"webhookUrl":"https://myshop.example/mivora"}'

La réponse contient apiKey et webhookSecret, montrés une seule fois, ainsi que firstAddress : vérifie qu'elle correspond à la première adresse de réception de ton wallet avant d'encaisser.

Ne donne jamais une clé privée (zprv) ni ta phrase de récupération : l'API n'en a pas besoin et les refuse.

Créer une facture

curl https://api.sovrawallet.eu/v1/invoices \
  -H 'authorization: Bearer mvk_...' \
  -H 'content-type: application/json' \
  -H 'idempotency-key: order-1042' \
  -d '{"coin":"btc","amount":"49.90","currency":"eur","orderId":"1042","expiresInMinutes":60}'
ChampDétail
coinbtc ou ltc
amountTexte décimal. En crypto si currency est absent, sinon dans la devise.
currencyFacultatif : eur, usd, gbp, chf, cad, aud, jpy, brl, pln, try. Le cours est figé à la création.
orderId, noteFacultatifs. orderId n'est jamais montré au client, note oui.
expiresInMinutesDe 5 à 10080. Par défaut : 60.
confirmationsDe 1 à 12. Par défaut : 2 en Bitcoin, 6 en Litecoin.

Renvoie ton client vers payUrl (page de paiement avec QR code), ou affiche toi-même address et uri. Avec Idempotency-Key, un même appel répété renvoie la même facture.

Autres routes : GET /v1/invoices/:id, GET /v1/invoices?status=paid&limit=20, GET /v1/merchants/me, POST /v1/merchants/me (nom, webhook), DELETE /v1/merchants/me (supprime la boutique).

Ta page de paiement peut aussi lire une facture sans clé : GET /v1/public/invoices/:id ne renvoie que ce que le client a le droit de voir.

Gérer ta clé API

Le plus simple : dans Espace vendeur, les boutons Nouvelle clé API et Supprimer la clé API. Si tu restaures ton wallet sur un autre appareil, appuie sur J'ai déjà une boutique avec ce wallet : une nouvelle clé est créée avec ta phrase de récupération.

RouteEffet
POST /v1/merchants/me/rotateNouvelle clé et nouveau secret de signature. Les anciens cessent de fonctionner immédiatement.
DELETE /v1/merchants/me/keySupprime la clé. La boutique, ses factures et la surveillance des paiements restent ; l'API refuse tout jusqu'à une nouvelle clé.
POST /v1/merchants/recoverNouvelle clé sans l'ancienne : {"coin":"btc","zpub":"zpub...","ts":<secondes>,"sig":"<hex>"}, où sig est une signature ECDSA secp256k1 compacte (64 octets) du SHA-256 du texte Mivora shop key\n<coin>:<zpub>\n<ts>, faite avec la clé privée du compte (celle de la zpub). Valable 5 minutes, une seule fois.

Statuts

StatutSignification
pendingEn attente. Plusieurs personnes peuvent contribuer : les paiements s'additionnent.
detectedMontant vu sur le réseau, pas assez confirmé. Ne livre pas encore : la transaction peut encore disparaître.
paidConfirmé : tu peux livrer. Statut définitif. late: true si le paiement a suivi l'échéance.
underpaidÉchéance passée avec un montant partiel.
expiredÉchéance passée sans paiement. L'adresse reste surveillée 7 jours : un paiement tardif fait passer la facture à paid.

Notifications (webhook)

À chaque changement de statut, ton adresse https reçoit un POST JSON : {"id":"evt_...","type":"invoice.paid","createdAt":"...","invoice":{...}}. Réponds avec un code 2xx. Sinon, jusqu'à 12 essais espacés sur environ un jour et demi : une même notification peut arriver deux fois, utilise id pour l'ignorer.

Vérifie toujours la signature de l'en-tête x-mivora-signature (t=<horodatage>,v1=<hmac>), calculée sur le corps brut :

import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(secret, header, rawBody) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? '');
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const want = createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest();
  return timingSafeEqual(want, Buffer.from(m[2], 'hex'));
}

Erreurs

Les erreurs utilisent le code HTTP et un corps JSON {"error":"message"}. 400 : requête invalide (avec details par champ). 401 : clé absente ou fausse. 404 : introuvable. 429 : trop de requêtes, patiente un instant puis réessaie. 5xx : problème temporaire, réessaie plus tard avec la même Idempotency-Key.

Limites à connaître