Skip to content

API

Introduction

Pour offrir une expérience facile à utiliser, nous proposons des bibliothèques Python, Perl ou PHP, et nous continuerons d’étoffer ce catalogue.

Des exemples de code sont disponibles dans chaque section. Les onglets disponibles dans le menu vous permettent de basculer vers les différents languages de programmations.

L’API est disponible sur https://api.stancer.com/ et supporte TLS 1.2, HTTP/1.1 et HTTP/2.

INFO

Cette documentation utilise httpie(https://httpie.io/) pour les exemples en shell, et nos libraries pour les exemples en PHP, Python & Perl.

Par simplification des exemples, l’authenfication par votre clé d’API (--auth stest_xxx:) est omise dans les lignes de commande proposées, mais vous devez la fournir à chaque appel.

Concepts clés

L’API est disponible sur https://api.stancer.com et supporte TLS 1.2, HTTP/1.1 et HTTP/2.

La même API peut être utilisée pour accéder aux ressources live traitant les paiements réels et méthodes de paiement, mais aussi des ressources de test pour le développement de vos logiciels et le débogage. Notre service API utilise des clés d’API pour authentifier les requêtes.

Pour obtenir une clé API, vous pouvez vous inscrire sur notre site web. Les clés API sont sous la forme de sprod_xxx pour vos requêtes live et stest_xxx pour vos test.

L’authentification à l’API est effectuée via HTTP Basic Auth. Vous devez utiliser votre clé API comme nom d’utilisateur et ne fournir aucun mot de passe.

Nous vous conseillons d’utiliser le mécanisme d’authentification de base de bibliothèque HTTP, mais si vous devez le gérer manuellement, comme spécifié dans HTTP Basic Auth, vous devez encoder votre clé API en base64 avant d’envoyer l’authentification correspondante dans un en-tête HTTP Authorization. Par exemple, si votre clé API est sprod_xxx, vous devez encoder sprod_xxx: (rappelez-vous, le mot de passe doit être vide) en base64 (ce qui donne c3Byb2RfeHh4Og==) puis envoyer un Autorisation: Basic c3Byb2RfeHh4Og== en-tête HTTP.

Cette documentation se concentre sur les workflows des cas d’utilisation courants. Pour une documentation plus complète ou technique, veuillez consulter notre documentation OpenAPI Swagger ou Redoc.

Si vous avez besoin d’assistance technique sur l’intégration de notre API, vous pouvez rejoindre notre Slack communautaire.

Créer des paiements

Avec la page de paiement de Stancer

shell
$ http https://api.stancer.com/v2/customers/ \
    name="Foo Bar" email=foo.bar@example.org
{
    "id": "cust_xxx",

}

$ http https://api.stancer.com/v2/payments/ \
currency=eur amount=100 customer=cust_xxx description="Test payment" auth=True
{
    "id": "paym_xxx",

}

Pour créer un paiement, utilisez la méthode POST /v2/payment_intents/. Vous devez au moins fournir une devise (eur par défaut) et un montant.

Si vous souhaitez associer le paiement à un client, vous pouvez utiliser le champ customer et transmettez l’ID cust_xxx correspondant.

Dans le cas des paiements basiques (objet payment et méthode POST /v2/payments/), vous devez forcer la validation 3DS avec le paramètre auth=true. Sinon, votre client ne pourra pas procéder au paiement, car sa banque refusera sûrement un paiement non authentifié. Dans le cas des intentions de paiement (objet payment_intent et méthode POST /v2/payment_intents/), la validation 3DS est directement active par défaut sans avoir besoin de paramètre supplémentaire.

Vous pouvez maintenant envoyer votre client sur la page de paiement située sur https://payment.stancer.com/payment_intents/<pi_xxx>. Remplacez l’ID de paiement en conséquence.

Une fois le paiement créé, vous pouvez le modifier jusqu’à sa capture par exemple pour refléter un changement d’achat, en utilisant la méthode PATCH /v2/payment_intents/{id}.

shell
$ http https://api.stancer.com/v2/payments/paym_xxx amount=200

Avec l’API

Vous pouvez spécifier une URL de retour avec le paramètre return_url. À la fin du processus 3DS, nous redirigerons votre client vers cette URL.

shell
$ http https://api.stancer.com/v2/cards/ \
    name="Foo Bar" number=4242424242424242 exp_month=10 exp_year=2024 cvc=424
{
    "id": "card_xxx",

}

$ http https://api.stancer.com/v2/payments/ \
currency=eur amount=100 customer=cust_xxx description="Test payment" \
card=card_xxx auth[return_url]=http://example.org
{
    "id": "paym_xxx",
    "auth": {
        "redirect_url": "https://3ds.iliad78.net/v2/redirect/xxx",

    }

}

Envoyez ensuite votre client sur le auth[redirect_url] donné pour permettre la récupération des données de carte bancaire et la validation éventuelle de 3DS si nécessaire. Après la redirection vers votre return_url, vous devez vérifier la validité de l’authentification, puis capturer le paiement si tout va bien.

shell
$ http https://api.stancer.com/v2/payments/paym_xxx
{

    "auth": {
        "status": "success"
    }
}

$ http PATCH https://api.stancer.com/v2/payments/paym_xxx \
status=capture

Si vous préférez collecter vous-même le numéro de carte, vous pouvez traiter un paiement directement et obtenir un identifiant de carte (tokenisation) avec POST /v2/cards/ avant de l’utiliser ailleurs dans les appels d’API. Dans ce mode, étant donné que vous manipulez directement les numéros de carte en clair, vous avez cependant des obligations réglementaires de conformité PCI/DSS à mettre en œuvre, dépendant de votre volumétrie.

shell
$ http https://api.stancer.com/v2/cards/ number=4242424242424242 month=8 year=2026 name="Jean Dupont"
{
  "id": "card_xxx",

}

$ http https://api.stancer.com/v2/payments/ \
currency=eur amount=100 customer=cust_xxx card=card_xxx description="Test payment" \
card=card_xxx auth[return_url]=http://example.org

Gérer des abonnements

Créer des produits et des prix

Notre solution s’appuie sur la notion de produits (product) et de tarifs (price) pour définir vos différents abonnements disponibles à la souscription. Chaque tarif est associé à un produit et permet de moduler les tarifs par exemple pour distinguer les abonnements mensuels de ceux annuels.

Vous devez tout d’abord créer un produit avec POST /v2/products/:

shell
$ http https://api.stancer.com/v2/products/ name=’Offre xxx’
{
  "id": "prod_xxx",

}

Vous devez ensuite définir les différentes gammes de prix proposées avec POST /v2/prices/, par exemple ici pour un abonnement mensuel à 10€/mois et un annuel à 100€/an:

shell
$ http https://api.stancer.com/v2/prices/ product=prod_xxx name=Mensuel nickname=offre_mensuelle amount:=10 interval=month
{
  "id": "price_xxx",

}

$ http https://api.stancer.com/v2/prices/ product=prod_xxx name=Annuel nickname=offre_annuelle amount:=100 interval=year
{
  "id": "price_yyy",

}

Souscrire un client

Lorsque vous souhaitez abonner un de vos clients à une de vos offres, vous pouvez utiliser POST /v2/subscriptions/

shell
$ http https://api.stancer.com/v2/subscriptions/ customer=cust_xxx items[][price]=price_xxx
{
  "id": "sub_xxx",
  "payment_intent": "pi_xxx",

}

Votre client suit ensuite le flot normal de paiement, via notre page de paiement disponible sur https://payment.stancer.com/payment_intents/<pi_xxx>. Après saisie de ses données bancaires et authentification 3DS auprès de sa banque, l’abonnement démarre aussitôt.

Webhooks

Afin d’être notifié en temps réel des modifications (paiement échoué, abonnement impayé…) et réagir rapidement en conséquence, vous pouvez définir des webhooks.

L’utilisation des webhooks nécessite un serveur web de votre côté en capacité de recevoir les différents évènements de modification, répondant par exemple sur l’adresse https://example.org/stancer/webhooks/.

Vous devez ensuite créer un endpoint sur notre API via POST /v2/webhooks/:

shell
$ http https://api.stancer.com/v2/webhooks/ url=https://example.org/stancer/webhooks/
{
  "id": "we_xxx",
  "secret": "xxx",

}

L’envoi d’un évènement se fait via un POST <url> sur votre infrastructure, qui doit présenter un certificat HTTPS valide et doit répondre avec une réponse HTTP 200 OK ou 204 NO CONTENT. Tout autre code de retour sera considéré comme une erreur et l’évènement sera retenté ultérieurement, via un mécanisme de backoff exponentiel. Notre infrastructure tente de délivrer le message 15×, soit environ 1 jour et demi, avant d’abandonner:

TentativeDélaiTotal
10:00:150:00:15
20:00:160:00:31
30:00:310:01:02
40:01:360:02:38
50:04:310:07:09
60:10:400:17:49
70:21:510:39:40
80:40:161:19:56
91:08:312:28:27
101:49:364:18:03
112:46:557:04:58
124:04:1611:09:14
135:45:5116:55:05
147:56:161 jour, 0:51:21
1510:40:311 jour, 11:31:52

Lors de l’envoi d’un évènement, le contenu de la requête est signé avec la clef secrète fournit lors de la création du endpoint, ceci afin de vous permettre d’identifier l’émetteur et de ne conserver que le contenu légitime.

La signature est un HMAC SHA256 d’un champ constitué du timestamp correspondant à la date d’envoi de la requête et du corps de la requête, séparés par un .. Cette signature est transmise dans l’entête Stancer-Signature de la requête HTTP.

python
def compute(secret: str, payload: str, date: datetime) -> [int, str]:
    secret = bytes.fromhex(secret)
    timestamp = int(date.timestamp())
    signed_payload = (str(timestamp) + ".").encode() + payload
    return hmac.digest(secret, signed_payload, hashlib.sha256).hex()


def parse(signature: str, version="v1") -> [int, str]:
    tmp = signature.split(",")
    parts = {}
    for t in tmp:
        t = t.split("=")
        parts[t[0]] = t[1]
    timestamp = int(parts["t"])
    return timestamp, parts[version]


def verify(
    secret: str,
    payload: str,
    signature: str,
    now=datetime.now(tz=timezone.utc),
    delta=timedelta(minutes=1),
) -> None:
    timestamp, expected = parse(signature)
    date = datetime.fromtimestamp(timestamp, tz=timezone.utc)
    if now - date > delta:
        raise Exception("Expired signature")
    _, computed = compute(secret, payload, date)
    ok = hmac.compare_digest(expected, computed)
    if not ok:
        raise Exception("Invalid signature")

Lors de la vérification de la signature, afin de vous prémunir d’une attaque par rejeu, vous devez vérifier que le timestamp transmis n’est pas trop éloigné de celui courant, par exemple moins de 1 minute.

Actuellement seules des signatures v1 sont émises, mais ce paramètre est nécessaire pour permettre un changement futur d’algorithme et votre code doit donc être robuste à la présence :

  • de plusieurs versions dans une même signature
  • de versions non supportées de votre côté

Vous ne devez valider la signature que de la version la plus élevée supportée par votre environnement.

L’évènement reçu dans le corps de la requête est sous la forme

json
{
  "id": "<evt_xxx>",
  "type": "<object_type>.<event>",
  "data": {  }
}

Les données présentes dans data sont celles présentes dans l’API sur la récupération des informations d’un objet (GET /v2/<object_type>/<object_id>)

Les types d’évènements disponibles sont:

  • customer
    • created
    • deleted
  • card
    • created
  • mandate
    • created
    • signed
  • payment_intent
    • created
    • updated
    • authorized
    • captured
  • payment
    • created
    • updated
    • authorized
    • succeeded
    • captured
    • refused
    • disputed
  • product
    • created
    • updated
    • deleted
  • price
    • created
    • updated
    • deleted
  • subscription
    • created
    • updated
    • deleted
    • past_due
    • unpaid
    • canceled
    • resumed

Attention

Les webhooks sont dépendants d’éléments extérieurs à Stancer (connexion réseau à vos points de terminaison, disponibilité et réponse de vos systèmes, validité de vos certificats HTTPS, rate limiting interne pour éviter les DDoS sortants, mise en file en cas de surcharge…) et sont fournis à ce titre en mode « best effort ».

Il n’y a aucune garantie :

  • de délivrabilité : un webhook peut ne jamais vous être délivré
  • de latence : un webhook peut arriver dans la seconde ou dans plusieurs heures
  • d’unicité : un webhook peut être délivré plusieurs fois

Vos services doivent en conséquence être conçus en tenant compte de ces limitations.

API status code

Payment status

Le champ status de l’objet de paiement indique dans quel état se trouve le paiement.

StatusMeaning
📋 authorizedThe bank authorized the payment but the transaction will only be processed after asking for capture
📋 to_captureThe bank authorized the payment, money will be processed within the day
📋 capture_sentThe capture operation is being processed, the payment can not be cancelled anymore, refunds must wait the end of the capture process
📋 capturedThe amount of the payment have been credited to your account
📋 disputedThe customer declined the payment after it have been captured on your account
📋 expiredThe authorisation was not captured and expired after 7 days
📋 failedThe payment has failed, refer to the response field for more details
📋 refusedThe payment has been refused

Authenticated payment status codes

The status field in the auth object returns the authorisation state for an authenticated payment.

StatusMeaning
📋 availableCustomer strong authentication is possible
📋 requestedA strong authentication is awaiting for more information
📋 attemptedCustomer was redirected to his bank for authentication
📋 successAuthentication succeeded, processing can continue
📋 declinedAuthentication declined
📋 expiredAuthentication sessions expired after 6 hours
📋 failedAuthentication failed
📋 unavailableThe strong authentication is not available for this payment method

Payout status codes

The status field in the payout explains in which state is the credit transfer which wires funds from Stancer to your bank.

StatusMeaning
📋 pendingThe payout has been created and is awaiting for clearing
📋 to_payThe payout is ready to be transfered
📋 sentThe payout has been sent out for processing
📋 paidThe payout credit transfer has been processed: funds have been received by your bank
📋 failedThe credit transfer has failed, please refer to you dashboard for more informations

Card response codes

⚠️ = real code & reason must not be sent to the customer, replace them with a generic 05 do-not-honor.

CodeMeaning
📋 00Successful approval/completion or that VIP PIN verification is valid
📋 01Refer to card issuer
📋 02Refer to card issuer, special condition
📋 03Invalid merchant or service provider
📋 04Pickup
📋 05Do not honor
📋 06General error
📋 07Pickup card, special condition (other than lost/stolen card)
📋 08Honor with identification
📋 09Request in progress
📋 10Partial approval
📋 11VIP approval
📋 12Invalid transaction
📋 13Invalid amount (currency conversion field overflow) or amount exceeds maximum for card program
📋 14Invalid account number (no such number)
📋 15No such issuer
📋 16Insufficient funds
📋 17Customer cancellation
📋 19Re-enter transaction
📋 20Invalid response
📋 21No action taken (unable to back out prior transaction)
📋 22Suspected Malfunction
📋 25Unable to locate record in file, or account number is missing from the inquiry
📋 28File is temporarily unavailable
📋 30Format error
📋 41⚠️Merchant should retain card (card reported lost)
📋 43⚠️Merchant should retain card (card reported stolen)
📋 51Insufficient funds
📋 52No checking account
📋 53No savings account
📋 54Expired card
📋 55Incorrect PIN
📋 56Card missing from file
📋 57Transaction not permitted to cardholder
📋 58Transaction not allowed at terminal
📋 59⚠️Suspected fraud
📋 61Activity amount limit exceeded
📋 62⚠️Restricted card (for example, in country exclusion table)
📋 63⚠️Security violation
📋 65⚠️Activity count limit exceeded
📋 68Response received too late
📋 75Allowable number of PIN-entry tries exceeded
📋 76Unable to locate previous message (no match on retrieval reference number)
📋 77Previous message located for a repeat or reversal, but repeat or reversal data are inconsistent with original message
📋 78’Blocked, first used’—The transaction is from a new cardholder, and the card has not been properly unblocked.
📋 80Visa transactions: credit issuer unavailable. Private label and check acceptance: Invalid date
📋 81PIN cryptographic error found (error found by VIC security module during PIN decryption)
📋 82Negative CAM, dCVV, iCVV, or CVV results
📋 83Unable to verify PIN
📋 85No reason to decline a request for account number verification, address verification, CVV2 verification; or a credit voucher or merchandise return
📋 91Issuer unavailable or switch inoperative (STIP not applicable or available for this transaction)
📋 92Destination cannot be found for routing
📋 93⚠️Transaction cannot be completed, violation of law
📋 94Duplicate transmission
📋 95Reconcile error
📋 96System malfunction, System malfunction or certain field error conditions
📋 98Server inaccessible
📋 A0Authentication Required, you must do a card inserted payment with PIN code
📋 A1Authentication Required, you must do a 3-D Secure authentication
📋 B1Surcharge amount not permitted on Visa cards (U.S. acquirers only)
📋 N0Force STIP
📋 N3Cash service not available
📋 N4Cashback request exceeds issuer limit
📋 N7Decline for CVV2 failure
📋 P2Invalid biller information
📋 P5PIN change/unblock request declined
📋 P6Unsafe PIN
📋 Q1Card authentication failed
📋 R0Stop payment order
📋 R1Revocation of authorization order
📋 R3Revocation of all authorizations order
📋 XAForward to issuer
📋 XDForward to issuer
📋 Z1Offline-declined
📋 Z3Unable to go online
📋 7810⚠️Refusal count exceeded for this card / sepa
📋 7811⚠️Exceeded payment volume for this card / sepa
📋 7840⚠️Stolen or lost card
📋 7898Bank server unavailable

Dispute response codes

⚠️ = real code & reason must not be sent to the customer, replace them with a generic 45 transaction-disputed.

ResponseNetworkMeaning
📋 14NationalTransaction not authorized
📋 42NationalDuplicate processing
📋 45NationalTransaction disputed
📋 1040⚠️VisaFraud; card Absent Environment
📋 1261VisaDuplicate processing
📋 4808MastercardRequested/required authorization not obtained. Transaction not authorized
📋 4834MastercardDuplicate processing
📋 4837⚠️MastercardFraudulent transaction; no cardholder authorization
📋 4853MastercardCardholder Dispute Defective/Not as Described
📋 4863⚠️MastercardCardholder does not recognize. Potential fraud

Test data

Test cards

NumberBrandCountry3DSReturn
📋 4000000400000008VisaAT 🇦🇹Optional00 OK
📋 4000000560000004VisaBE 🇧🇪Optional00 OK
📋 4000002080000001VisaDK 🇩🇰Optional00 OK
📋 4000002460000001VisaFI 🇫🇮Optional00 OK
📋 4000002500000003CBFR 🇫🇷Optional00 OK
📋 4000002760000016VisaDE 🇩🇪Optional00 OK
📋 4000003720000005VisaIE 🇮🇪Optional00 OK
📋 4000003800000008VisaIT 🇮🇹Optional00 OK
📋 4000004420000006VisaLU 🇱🇺Optional00 OK
📋 4000005280000002VisaNL 🇳🇱Optional00 OK
📋 4000005780000007VisaNO 🇳🇴Optional00 OK
📋 4000006200000007VisaPT 🇵🇹Optional00 OK
📋 4000006430000009VisaRU 🇷🇺Optional00 OK
📋 4000007240000007VisaES 🇪🇸Optional00 OK
📋 4000007520000008VisaSE 🇸🇪Optional00 OK
📋 4000007560000009VisaCH 🇨🇭Optional00 OK
📋 4000008260000000VisaGB 🇬🇧Optional00 OK
📋 4242424242424242VisaUS 🇺🇸Not required00 OK
📋 4444333322221111VisaUS 🇺🇸Not required00 OK
📋 4111111111111111VisaUS 🇺🇸Not required00 OK
📋 5555555555554444MastercardUS 🇺🇸Optional00 OK
📋 5200828282828210MastercardUS 🇺🇸Optional00 OK
📋 5105105105105100MastercardUS 🇺🇸Optional00 OK
📋 4000000000003055VisaUS 🇺🇸Not enrolled00 OK
📋 4000000760000002VisaBR 🇧🇷Optional00 OK
📋 4000001240000000VisaCA 🇨🇦Optional00 OK
📋 4000004840000008VisaMX 🇲🇽Optional00 OK
📋 4000000000000077VisaFR 🇫🇷Optional00 OK | status = 50 captured
📋 4000000000003220VisaUS 🇺🇸RequiredA1 must perform 3DS | status = 0 refused
📋 4000000000000002VisaFR 🇫🇷Optional05 do not honor | status = 0 refused
📋 4000000000009995VisaFR 🇫🇷Optional51 insufficient fund | status = 0 refused
📋 4000000000009987VisaFR 🇫🇷Optional41 lost card | status = 0 refused
📋 4000000000000259VisaFR 🇫🇷Optional00 OK | status = 60 disputed
📋 4000000000001976VisaFR 🇫🇷Optional00 OK | status = 60 disputed
📋 4000000000005423VisaFR 🇫🇷Optional00 OK | status = 60 disputed

Test SEPA

IBANCountryNameBirthdateReturn
📋 AT611904300234573201AT 🇦🇹Otto Normalverbraucher1971-02-0200 OK
📋 BE62510007547061BE 🇧🇪Jef Van Pijperzele1972-03-0300 OK
📋 CH2089144321842946678CH 🇨🇭Leonhard Euler1973-04-0400 OK
📋 DE89370400440532013000DE 🇩🇪Max Mustermann1974-05-0500 OK
📋 EE382200221020145685EE 🇪🇪Friedrich Robert Faehlmann1975-06-0600 OK
📋 ES0700120345030000067890ES 🇪🇸Juan Pérez1976-07-0700 OK
📋 FI2112345600000785FI 🇫🇮Maija Meikäläinen1977-08-0800 OK
📋 FR1420041010050500013M02606FR 🇫🇷Pierre Martin1978-09-0900 OK
📋 GB33BUKB20201555555555GB 🇬🇧John Doe1970-01-0100 OK
📋 IE29AIBK93115212345678IE 🇮🇪John Kilkenny1979-10-1000 OK
📋 LT121000011101001000LT 🇱🇹Jonas Petraitis1980-11-1100 OK
📋 LU280019400644750000LU 🇱🇺Adalbert Boros1981-12-1200 OK
📋 IT02A0301926102000000490887IT 🇮🇹Piero Pers1982-01-1300 OK
📋 NL39RABO0300065264NL 🇳🇱Jan Modaal1983-02-1400 OK
📋 NO9386011117947NO 🇳🇴Peder Aas1984-03-1500 OK
📋 PT50000201231234567890154PT 🇵🇹Jan Kowalski1985-04-1600 OK
📋 SE3550000000054910000003SE 🇸🇪Lisa Svensson1986-05-1700 OK
📋 FR9430003000409249176322Z50FR 🇫🇷Gilles Dupont1987-06-1800 OK
📋 FR2990665286926539507769811FR 🇫🇷Jean Banbois1992-11-23AC01 Incorrect Account Number | status = 0 refused
📋 FR8191676442817818484578833FR 🇫🇷Marie-Jeanne Sansbanque1993-12-24AC04 Closed Account Number | status = 0 refused
📋 FR3083648641551044006702169FR 🇫🇷Marc Barrer1994-01-25AC06 Blocked Account | status = 0 refused
📋 FR4200838098473368525032012FR 🇫🇷Sophie Fontek1995-02-26AG01 Transaction Forbidden | status = 0 refused
📋 FR7099253427049384102178149FR 🇫🇷Hector Fauché1996-03-27AM04 Insufficient Funds | status = 0 refused
📋 FR7240745948453163519978561FR 🇫🇷Lillianne Sansmandat1997-04-28MD01 No Mandate | status = 0 refused
📋 FR5533686478441573584650545FR 🇫🇷Vincent Refusé1998-05-29MD06 Refund Request By End Customer | status = 0 refused
📋 FR2488294045573706143240475FR 🇫🇷Eric Indécis1999-06-30MS02 Not Specified Reason Customer Generated | status = 0 refused
📋 BE08510007547063BE 🇧🇪Camille Honnête1988-07-19AM04 Insufficient Funds | status = 60 disputed
📋 ES5000120345030000067892ES 🇪🇸Pepito Pérez1990-09-21AC04 Closed Account Number | status = 60 disputed