Aicscan Documentation

Aicscan Pay

Aicscan Pay encaisse des paiements en USDT sans jamais détenir les fonds : le payeur envoie le montant à une adresse de dépôt propre à la facture, et les fonds partent ensuite directement vers votre adresse de versement. Aucun point d’accès de l’API ne déplace de fonds.

L’API Pay suit les mêmes règles que le reste de l’API v1 (authentification, pagination, erreurs, versionnement), avec les différences ci-dessous.

Activer Pay et obtenir des clés#

  1. Dans l’espace client, onglet Pay, le propriétaire active Pay. La double authentification est obligatoire pour toute action Pay.
  2. Le mode test est disponible tout de suite : créez une clé test.
  3. Pour le mode live, lancez la vérification de votre entreprise (KYB). Les clés live ne sont émises qu’une fois le KYB approuvé.
  4. Déclarez votre wallet signataire et votre adresse de versement : l’onglet renvoie, pour chaque chaîne ouverte, le message EIP-712 RegisterMerchant à signer avec ce wallet. Rien n’est envoyé on-chain à ce stade.
Clé Préfixe Environnement
Test aic_test_… testnets, sans KYB
Live aic_live_… réseaux principaux, après KYB

Portées : pay:read (lectures), pay:write (factures, webhooks), pay:refund (préparation de remboursements). Toute portée Pay comprend pay:read. Une clé Alerts reçoit 403 insufficient_scope sur les routes Pay, et une clé Pay sur les routes Alerts. Une clé Pay ne dépend pas de l’offre Alerts de l’espace (pas de plan_required, pas de quota quotidien) ; la limite de débit par seconde s’applique.

Chaque objet appartient à l’environnement de la clé qui l’a créé : une facture test est introuvable (404) avec une clé live.

Idempotency-Key est obligatoire sur tous les POST Pay (400 idempotency_key_required). Utilisez un UUID par opération et renvoyez la même valeur si vous réessayez : la facture n’est jamais créée deux fois. Voir Idempotence.

Créer une facture#

bash
curl https://aicscan.io/v1/invoices \
  -H "Authorization: Bearer $AICSCAN_PAY_KEY" \
  -H "Idempotency-Key: 6f1c2d0e-8b7a-4c55-9e2f-1a3b5c7d9e0f" \
  -H "Content-Type: application/json" \
  -d '{"currency":"EUR","price_e6":"25000000","chain_ids":[56,728126428],"external_ref":"cmd-1042","ttl_seconds":3600}'
Champ Sens
currency USDT, ou une devise d’affichage ouverte (USD, EUR, XOF… voir GET /v1/rates).
price_e6 Prix en millionièmes de la devise, en chaîne : "25000000" = 25,00. Pas plus de décimales que la devise (XOF : aucune).
chain_ids Chaînes proposées au payeur : une adresse de dépôt par chaîne.
variant A (défaut) : une adresse par facture. B : adresse partagée et montant légèrement unique, une seule chaîne.
ttl_seconds Validité, 3 600 s par défaut, de 900 s à 7 jours. Prévoyez long si vos clients paient depuis un exchange.
external_ref, description, metadata Votre référence (unique par environnement), un libellé, un objet JSON libre.

Le cours d’une devise est figé à la création (price.fx_rate_e6, fx_source, fx_as_of) et le montant dû est arrondi au profit du payeur. Les montants de jeton sont toujours des chaînes en unités natives, avec leur forme lisible :

JSON
{ "amount": "27012345", "amount_formatted": "27.012345", "decimals": 6 }

La réponse (201) donne pour chaque chaîne l’adresse de dépôt (deposit_address, base58 T… sur TRON), le montant dû et, sur les chaînes EVM, un lien EIP-681 (payment_uri) à mettre dans un QR code.

Refus fréquents : chain_not_available, amount_below_minimum, amount_above_cap, duplicate_external_ref (409), unique_amount_exhausted (409, variante B : toutes les valeurs uniques sont prises), currency_suspended (409 : la garde USDT/USD a suspendu les factures en devise ; les factures en USDT ne sont pas concernées), kyb_required (403, live).

Statuts d’une facture#

Statut Sens
waiting Créée, rien reçu.
confirming Paiement vu dans un bloc, confirmations en cours.
paid Montant reçu dans la tolérance, confirmé.
underpaid Reçu moins que le dû : un complément reste possible jusqu’à l’expiration.
overpaid Reçu plus que le dû : remboursez l’excédent si besoin.
expired Délai écoulé sans paiement complet.
paid_late Payée après l’expiration (ou après annulation) : à vous d’honorer ou de rembourser.
canceled Annulée par vous avant tout paiement (POST /v1/invoices/{id}/cancel).
held Paiement depuis une adresse sanctionnée : décision manuelle.

Plusieurs transferts vers la même adresse s’additionnent. Les montants minuscules (poussière) sont ignorés. Le détail d’une facture (GET /v1/invoices/{id}) liste ses paiements, ses balayages vers votre adresse de versement et ses remboursements.

Webhooks Pay#

Déclarez un point de réception avec POST /v1/webhook_endpoints (https:// uniquement ; adresses privées et locales refusées). Le secret whsec_… n’est renvoyé qu’une fois. Les envois suivent exactement le mécanisme des webhooks d’Alerts : POST JSON, en-têtes X-Event-Id, X-Timestamp et X-Signature: sha256=HMAC(secret, "{X-Timestamp}.{corps brut}"), rejet au-delà de 300 secondes, seule une réponse 2xx vaut succès, relances pendant 72 heures.

JSON
{
  "id": "evt_7850b21f0ab248a5e16ddedf2c6626e9",
  "type": "invoice.paid",
  "api_version": "1",
  "environment": "live",
  "livemode": true,
  "created_at": "2026-11-20T10:05:00Z",
  "data": {
    "object": { "id": "0193a1d0-1a2b-7c3d-8e4f-5a6b7c8d9e0f", "object": "invoice", "status": "paid" },
    "payment": { "amount": "27012345", "amount_formatted": "27.012345", "decimals": 6, "chain_id": 56,
                 "tx_hash": "0x…", "log_index": 3, "block_number": 45123456, "confirmations": 15 }
  }
}

Types : invoice.created, invoice.confirming, invoice.paid, invoice.underpaid, invoice.overpaid, invoice.expired, invoice.paid_late, invoice.canceled, invoice.reverted, invoice.held, sweep.succeeded, sweep.failed, refund.succeeded, refund.pending, merchant.kyb_updated… L’id (X-Event-Id) est identique à chaque relance : servez-vous-en pour ignorer un doublon. Après une réorganisation de chaîne, invoice.reverted annule un paiement ; un nouvel invoice.paid suit s’il est réinclus.

  • POST /v1/webhook_endpoints/{id}/test envoie un événement webhook_endpoint.test à ce seul point.
  • GET /v1/events et GET /v1/events/{id} donnent le journal et l’état des livraisons ; POST /v1/events/{id}/resend remet un événement en file.

Rembourser#

Un remboursement est un transfert depuis votre propre wallet. POST /v1/refunds (portée pay:refund) renvoie la transaction à signer (transaction.to = contrat du jeton, transaction.data = transfer(to, amount)) ; Aicscan ne l’envoie jamais.

L’adresse d’origine d’un paiement n’est jamais proposée par défaut : c’est souvent le portefeuille d’un exchange, et un remboursement y serait perdu. Saisissez dans to_address l’adresse que le payeur vous a donnée. L’avertissement exchange_address signale une adresse connue d’exchange, origin_address une adresse qui a envoyé un paiement de la facture.

La commission n’est pas remboursée. Le montant est plafonné au reçu moins les remboursements déjà préparés.

Mode test#

Les clés test fonctionnent sur les testnets, avec la même API et les mêmes webhooks ("environment": "test", "livemode": false). Rien de ce que vous créez en test n’apparaît en live, et inversement. Vérifiez qu’une adresse est bien l’une de vos adresses de dépôt avec GET /v1/deposit_addresses/{address}/verify.

Référence complète : Pay : factures, remboursements, webhooks.

Aicscan ne vous demandera jamais de vous connecter à un wallet, de signer une transaction ni d’envoyer des fonds.

aicscan.io · Statut · openapi.yaml