Documentation Webhook API

TIB Financial Solutions

Version 1.0.0 | 3 octobre 2025

Table des matières

1. Vue d'ensemble

Qu'est-ce qu'un webhook ?

Les webhooks TIB permettent à votre système de recevoir des notifications en temps réel sur les événements de transaction (paiements, dépôts, changements de statut, etc.).

Fonctionnement :
Transaction TIB → Événement déclenché → POST Webhook → Votre endpoint → Traitement

Prérequis

2. Configuration initiale

Étape 1 : Créer votre endpoint

Créez une URL publique qui recevra les webhooks :

Exemple : https://votre-domaine.com/api/webhooks/tib/transaction

Recommandations :

Étape 2 : Fournir l'URL à TIB

Contactez votre représentant TIB et fournissez l'URL complète de votre endpoint.

Étape 3 : Configuration de la sécurité réseau (OBLIGATOIRE)

IMPORTANT : Sécurité obligatoire
Pour garantir que seuls les serveurs TIB peuvent envoyer des webhooks à votre endpoint, vous devez configurer votre infrastructure réseau pour n'autoriser que notre plage d'adresses IP.
Plage IP à autoriser :
204.225.113.0/24

Cette configuration doit être effectuée sur votre infrastructure réseau (firewall, security groups, WAF, reverse proxy, etc.) selon vos outils en place.

Pourquoi c'est essentiel

Sans ce filtrage, n'importe qui sur Internet pourrait envoyer de fausses requêtes à votre endpoint et potentiellement manipuler vos données financières.

3. Sécurité et authentification

Protection en couches recommandée

Couche Description Priorité
Filtrage IP Autoriser uniquement 204.225.113.0/24 OBLIGATOIRE
HTTPS Chiffrement des données en transit Fortement recommandé
Validation du payload Vérifier la structure JSON Recommandé
Logging Enregistrer tous les webhooks reçus Recommandé

Format de la requête

4. Structure du payload

Format général

POST /api/webhooks/tib/transaction HTTP/1.1
Host: votre-domaine.com
Content-Type: application/json

{
  "EventName": "TransactionEvent",
  "Target": "Client",
  "Direction": "Collect",
  ...
}

Propriétés principales

TransactionEventPayload

Propriété Type Description Valeurs possibles
EventName string Nom de l'événement "TransactionEvent"
Target string Cible de la transaction "Client" / "Merchant"
Direction string Direction du flux financier "Collect" / "Deposit"
Step string Étape du processus "Validation" / "Transmission" / "StatusCheck" / "Payback"
Status string Statut de l'événement Voir section Statuts
DescriptionCode string Code de description "0" à "7"
Description string Description technique interne Pour débogage
Amount string Montant formaté Ex: "123.45 $"
MerchantId string (GUID) Identifiant unique du marchand
MerchantName string Nom du marchand
MerchantAccountPreview string Aperçu du compte marchand Ex: "***-*****-**1111"
MerchantExternalId string ID externe du marchand
MerchantDescription string Description du marchand
TransferPayload array Liste des transferts Voir TransferPayload

TransferPayload

Propriété Type Description
TransferId string (GUID) Identifiant unique du transfert
Amount decimal Montant du transfert
OperationKind int Type d'opération (voir OperationKind enum)
OriginalAmount decimal Montant original
ExternalReferenceId string Référence externe définie par vous
DueDate string Date d'échéance - Format: "MM/DD/YYYY HH:MM:SS AM/PM"
CreatedDate string Date de création
PaymentMethodId string (GUID) ID de la méthode de paiement
PaymentMethodDescription string Description de la méthode
PaymentMethodPreview string Aperçu masqué
CustomerId string (GUID) ID du client
CustomerName string Nom du client
CustomerExternalId string Votre ID externe du client
BillAmount string Montant de la facture
BillCurrency string "1" (CAD) / "2" (USD) / "3" (EUR) / "4" (GBP)
BillDescription string Description de la facture
BillId string (GUID) ID de la facture
BillLanguage string "1" (Français) / "2" (Anglais)
BillTitle string Titre de la facture
BillCreatedDate string Date de création de la facture
ExternalSystemBillNumber1 string Numéro de facture système externe 1
ExternalSystemBillNumber2 string Numéro de facture système externe 2
ExternalSystemBillNumber3 string Numéro de facture système externe 3
BillMerchantId string (GUID) ID marchand sur la facture
BillCustomerId string (GUID) ID client sur la facture
Payout object Informations de paiement (US seulement)

5. Gestion des événements

Statuts possibles

Status Signification Action recommandée
Success_Success Transaction complétée avec succès Marquer comme payé
Success_NoResultReturned Succès mais résultat en attente Surveiller les prochains webhooks
Success_Skip Étape sautée Informationnel
Success_WaitManual En attente d'intervention manuelle Attendre validation TIB
Success_Error Succès partiel avec erreur Vérifier les détails
Error_Temporary Erreur temporaire Sera réessayé automatiquement
Error_Fatal Erreur fatale Transaction échouée définitivement
Abort Transaction annulée Transaction abandonnée

Codes de description

Code Nom Description
"0" NotSet Non défini
"1" Confirmed Transaction confirmée
"2" ErrorOther Erreur autre/générique
"3" NoFund Fonds insuffisants
"4" AccountError Erreur de compte bancaire
"5" Opposition Opposition bancaire
"6" InteracRefused Transaction Interac refusée
"7" InteracFailed Transaction Interac échouée

OperationKind Enum

Valeur Nom Description
-1UnknownType inconnu
0NotSetNon défini
1PaymentPaiement standard
2FreeDepositDépôt gratuit
3FreeCollectionCollection gratuite
4FeePaymentFrais de paiement
5FeeFreeDepositFrais de dépôt gratuit
6FeeFreeCollectionFrais de collection gratuite
10ReturnChargeFrais de retour
11FileFeesFrais de dossier
12RefundRemboursement
13TibCollectCollection TIB
14AccountCheckNsfFeesFrais NSF vérification de compte
15AccountCheckOppositionFeesFrais d'opposition vérification
16AccountCheckNsfFeesHitImpact frais NSF
17AccountCheckOppositionFeesHitImpact frais d'opposition
18FeeReverseChargeFrais de contre-passation
19RevertedOperationOpération annulée
20ConvenientFeeFrais de commodité
21RevertConvenientFeeAnnulation frais de commodité
22PlatformFeeFrais de plateforme
23GatewayUsageFeeFrais d'utilisation passerelle
24SupportFeeFrais de support
100GroupedFeesFrais groupés
101FeesRetransferRetransfert de frais

Flux d'événements typique

Scénario 1 : Transaction normale (Réussie)

1. Validation → Step: Validation, Status: Success_Skip
2. Transmission → Step: Transmission, Status: Success_Success
3. StatusCheck → Step: StatusCheck, Status: Success_NoResultReturned

Scénario 2 : NSF (Fonds insuffisants)

1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. StatusCheck → Status: Success_NoResultReturned
4. Payback → Status: Success_Error, DescriptionCode: "3" (NoFund)

Scénario 3 : Opposition (Opposition bancaire)

1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. StatusCheck → Status: Success_Error, DescriptionCode: "5" (Opposition)

Scénario 4 : Compte fermé

1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. Payback → Status: Success_Error, DescriptionCode: "4" (AccountError)
Important - Période de validation du Payback Check :

Après une transaction réussie, TIB valide le paiement durant ces périodes. Si un problème survient (NSF, opposition, compte fermé), vous recevrez un événement webhook de type Payback.

6. Implémentation recommandée

Architecture recommandée

Internet
   ↓
Firewall (Autoriser 204.225.113.0/24)
   ↓
Endpoint /webhooks
   ↓
Valider Payload + Logger
   ↓
Retourner 200 OK (IMPORTANT)
   ↓
Traitement asynchrone

Règles d'or

1. Répondre rapidement avec 200 OK

Votre endpoint DOIT retourner un code HTTP 200 OK en moins de 30 secondes.

BON :
[HttpPost("webhooks/tib/transaction")]
public IActionResult ReceiveWebhook([FromBody] TransactionEventPayload payload)
{
    _ = TraiterEnArrierePlan(payload);
    return Ok();
}
MAUVAIS :
[HttpPost("webhooks/tib/transaction")]
public IActionResult ReceiveWebhook([FromBody] TransactionEventPayload payload)
{
    TraiterTransaction(payload); // Peut prendre trop de temps
    return Ok();
}

2. Logger tous les événements

_logger.LogInformation(
    "Webhook reçu - TransferId: {TransferId}, Status: {Status}, Amount: {Amount}",
    transferId, 
    payload.Status, 
    payload.Amount
);

3. Ne retourner 200 OK que si le webhook est bien reçu

try
{
    await SauvegarderWebhook(payload);
    return Ok();
}
catch (Exception ex)
{
    _logger.LogError(ex, "Impossible de sauvegarder le webhook");
    
    // Ne pas retourner 200 OK
    // TIB saura que votre serveur est down
    return StatusCode(500);
}

7. Gestion des erreurs

Comportement en cas d'erreur

Si votre endpoint ne répond pas 200 OK, TIB considère que votre serveur est down et arrête temporairement l'envoi de webhooks.

TIB envoie webhook
   ↓
Votre endpoint répond 200 OK → Succès, fin
   ↓
Pas de 200 OK (500, timeout, etc.) → Serveur considéré down

Codes de réponse HTTP

Code Signification Action TIB
200 OK Webhook reçu et accepté Succès
201, 202, 204 Accepté (variantes) Succès
400 Requête invalide Serveur down
401, 403 Non autorisé Serveur down
404 Endpoint introuvable Serveur down
500, 502, 503 Erreur serveur Serveur down
Timeout Pas de réponse Serveur down
IMPORTANT : Aucun webhook n'est renvoyé. Si votre serveur est down, les événements ne seront pas délivrés pendant cette période.

8. Exemples de code

Exemple complet C# (.NET Core)

using Microsoft.AspNetCore.Mvc;
using System.Text.Json;

namespace VotreApp.Controllers
{
    [ApiController]
    [Route("api/webhooks/tib")]
    public class TibWebhookController : ControllerBase
    {
        private readonly ILogger<TibWebhookController> _logger;
        private readonly ApplicationDbContext _db;
        private readonly IBackgroundJobQueue _queue;

        public TibWebhookController(
            ILogger<TibWebhookController> logger,
            ApplicationDbContext db,
            IBackgroundJobQueue queue)
        {
            _logger = logger;
            _db = db;
            _queue = queue;
        }

        [HttpPost("transaction")]
        public async Task<IActionResult> ReceiveTransactionEvent(
            [FromBody] TransactionEventPayload payload)
        {
            try
            {
                // 1. Valider le payload
                if (payload?.TransferPayload == null || !payload.TransferPayload.Any())
                {
                    _logger.LogWarning("Webhook reçu avec payload invalide");
                    return BadRequest("Payload invalide");
                }

                var transferId = payload.TransferPayload.First().TransferId;

                // 2. Logger la réception
                _logger.LogInformation(
                    "Webhook reçu - Event: {EventName}, TransferId: {TransferId}, " +
                    "Status: {Status}, Amount: {Amount}",
                    payload.EventName,
                    transferId,
                    payload.Status,
                    payload.Amount
                );

                // 3. Sauvegarder immédiatement
                var webhookEvent = new WebhookEvent
                {
                    Id = Guid.NewGuid(),
                    TransferId = transferId,
                    EventName = payload.EventName,
                    Status = payload.Status,
                    RawPayload = JsonSerializer.Serialize(payload),
                    ReceivedAt = DateTime.UtcNow
                };

                await _db.WebhookEvents.AddAsync(webhookEvent);
                await _db.SaveChangesAsync();

                // 4. Mettre en file d'attente pour traitement asynchrone
                _queue.QueueBackgroundWorkItem(async token =>
                {
                    await ProcessWebhookAsync(payload, webhookEvent.Id, token);
                });

                // 5. IMPORTANT: Retourner 200 OK immédiatement
                return Ok(new 
                { 
                    message = "Webhook reçu avec succès",
                    transferId
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erreur lors de la réception du webhook");
                return StatusCode(500, "Erreur interne");
            }
        }

        private async Task ProcessWebhookAsync(
            TransactionEventPayload payload, 
            Guid eventId,
            CancellationToken cancellationToken)
        {
            try
            {
                _logger.LogInformation("Début du traitement - EventId: {EventId}", eventId);

                switch (payload.Status)
                {
                    case "Success_Success":
                        await TraiterPaiementReussi(payload);
                        break;

                    case "Error_Fatal":
                        await TraiterPaiementEchoue(payload);
                        break;

                    default:
                        _logger.LogInformation("Status: {Status}", payload.Status);
                        break;
                }

                _logger.LogInformation("Traitement terminé - EventId: {EventId}", eventId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erreur lors du traitement - EventId: {EventId}", eventId);
            }
        }

        private async Task TraiterPaiementReussi(TransactionEventPayload payload)
        {
            var transfer = payload.TransferPayload.First();
            
            _logger.LogInformation(
                "Paiement réussi - Client: {CustomerName}, Montant: {Amount}",
                transfer.CustomerName,
                transfer.Amount
            );

            // Mettre à jour votre système
            var billId = transfer.BillId;
            var invoice = await _db.Invoices
                .FirstOrDefaultAsync(i => i.ExternalBillId == billId);
            
            if (invoice != null)
            {
                invoice.Status = "Paid";
                invoice.PaidAt = DateTime.UtcNow;
                invoice.PaidAmount = transfer.Amount ?? 0;
                await _db.SaveChangesAsync();
            }
        }

        private async Task TraiterPaiementEchoue(TransactionEventPayload payload)
        {
            var transfer = payload.TransferPayload.First();
            
            _logger.LogWarning(
                "Paiement échoué - Client: {CustomerName}, Code: {DescriptionCode}",
                transfer.CustomerName,
                payload.DescriptionCode
            );
        }
    }
}

Modèles de données

public class WebhookEvent
{
    public Guid Id { get; set; }
    public string TransferId { get; set; }
    public string EventName { get; set; }
    public string Status { get; set; }
    public string RawPayload { get; set; }
    public DateTime ReceivedAt { get; set; }
}

public class TransactionEventPayload
{
    public string EventName { get; set; }
    public string Target { get; set; }
    public string Direction { get; set; }
    public string Step { get; set; }
    public string Status { get; set; }
    public string DescriptionCode { get; set; }
    public string Description { get; set; }
    public string Amount { get; set; }
    public string MerchantId { get; set; }
    public string MerchantName { get; set; }
    public string MerchantAccountPreview { get; set; }
    public string MerchantExternalId { get; set; }
    public string MerchantDescription { get; set; }
    public List<TransferPayload> TransferPayload { get; set; }
}

public class TransferPayload
{
    public string TransferId { get; set; }
    public decimal? Amount { get; set; }
    public int? OperationKind { get; set; }
    public decimal OriginalAmount { get; set; }
    public string ExternalReferenceId { get; set; }
    public string DueDate { get; set; }
    public string CreatedDate { get; set; }
    public string PaymentMethodId { get; set; }
    public string PaymentMethodDescription { get; set; }
    public string PaymentMethodPreview { get; set; }
    public string CustomerId { get; set; }
    public string CustomerName { get; set; }
    public string CustomerExternalId { get; set; }
    public string BillAmount { get; set; }
    public string BillCurrency { get; set; }
    public string BillDescription { get; set; }
    public string BillId { get; set; }
    public string BillLanguage { get; set; }
    public string BillTitle { get; set; }
    public string BillCreatedDate { get; set; }
    public string ExternalSystemBillNumber1 { get; set; }
    public string ExternalSystemBillNumber2 { get; set; }
    public string ExternalSystemBillNumber3 { get; set; }
    public string BillMerchantId { get; set; }
    public string BillCustomerId { get; set; }
    public PayoutPayload Payout { get; set; }
}

Exemple Node.js / Express

const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(bodyParser.json());

app.post('/api/webhooks/tib/transaction', async (req, res) => {
    try {
        const payload = req.body;
        
        // 1. Valider le payload
        if (!payload.TransferPayload || payload.TransferPayload.length === 0) {
            console.warn('Payload invalide reçu');
            return res.status(400).json({ error: 'Payload invalide' });
        }
        
        const transferId = payload.TransferPayload[0].TransferId;
        
        // 2. Logger
        console.log(`Webhook reçu - TransferId: ${transferId}, Status: ${payload.Status}`);
        
        // 3. Sauvegarder
        await saveWebhook(payload);
        
        // 4. Traiter en asynchrone
        processWebhookAsync(payload).catch(err => {
            console.error('Erreur traitement asynchrone:', err);
        });
        
        // 5. IMPORTANT: Répondre 200 OK immédiatement
        return res.status(200).json({ 
            message: 'Webhook reçu avec succès', 
            transferId 
        });
        
    } catch (error) {
        console.error('Erreur critique:', error);
        return res.status(500).json({ error: 'Erreur interne' });
    }
});

async function processWebhookAsync(payload) {
    console.log('Début du traitement asynchrone');
    
    switch(payload.Status) {
        case 'Success_Success':
            await handleSuccessfulPayment(payload);
            break;
        case 'Error_Fatal':
            await handleFailedPayment(payload);
            break;
    }
    
    console.log('Traitement terminé');
}

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Serveur webhook démarré sur le port ${PORT}`);
});

Exemple Python / Flask

from flask import Flask, request, jsonify
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

@app.route('/api/webhooks/tib/transaction', methods=['POST'])
def receive_webhook():
    try:
        payload = request.get_json()
        
        # 1. Valider
        if not payload.get('TransferPayload'):
            logging.warning('Payload invalide')
            return jsonify({'error': 'Payload invalide'}), 400
        
        transfer_id = payload['TransferPayload'][0]['TransferId']
        
        # 2. Logger
        logging.info(f"Webhook reçu - TransferId: {transfer_id}, "
                    f"Status: {payload['Status']}")
        
        # 3. Sauvegarder
        save_webhook(payload)
        
        # 4. Traiter en asynchrone
        process_webhook(payload)
        
        # 5. Retourner 200 OK immédiatement
        return jsonify({
            'message': 'Webhook reçu avec succès',
            'transferId': transfer_id
        }), 200
        
    except Exception as e:
        logging.error(f"Erreur critique: {str(e)}")
        return jsonify({'error': 'Erreur interne'}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

9. Tests et validation

Checklist avant la mise en production

Exécutez chaque scénario ci-dessous dans l'environnement sandbox TIB avant la mise en production. Les webhooks sont émis pour chaque rail présenté sur notre page modes de paiement, et notre équipe peut les brancher à vos systèmes grâce à une intégration d'affaires.

Scénarios de test

Test 1 : Webhook valide

Requête :

POST /api/webhooks/tib/transaction
{
  "EventName": "TransactionEvent",
  "Target": "Client",
  "Direction": "Collect",
  "Step": "StatusCheck",
  "Status": "Success_Success",
  "Amount": "100.00 $",
  "TransferPayload": [{
    "TransferId": "test-guid-123",
    "Amount": 100.00,
    "CustomerName": "Test User"
  }]
}

Résultat attendu :

Test 2 : Webhook invalide

Requête :

POST /api/webhooks/tib/transaction
{
  "EventName": "TransactionEvent"
}

Résultat attendu :

Test 3 : Erreur serveur

Simulation : Éteindre votre base de données

Résultat attendu :

10. FAQ et dépannage

Questions fréquentes

Q: Que se passe-t-il si mon serveur est down ?

R: TIB détectera que votre serveur ne répond pas 200 OK et arrêtera temporairement l'envoi de webhooks. Les événements pendant cette période ne seront pas délivrés.

Q: Les webhooks sont-ils renvoyés en cas d'erreur ?

R: Oui. Si votre endpoint ne répond pas 200 OK, TIB considère que votre serveur est down et arrête temporairement l'envoi de webhooks. Lorsque votre serveur répond de nouveau avec un 200 OK, les webhooks seront à nouveau délivrés.

Q: Comment puis-je récupérer les webhooks manqués ?

R: Contactez votre représentant TIB pour discuter des options de récupération des événements manqués.

Q: Puis-je avoir plusieurs endpoints ?

R: Oui

Q: Les webhooks sont-ils envoyés dans l'ordre ?

R: Les webhooks sont envoyés dans l'ordre chronologique des événements. Par contre, en raison du traitement asynchrone et des conditions réseau, il n'est pas garanti que vous les recevrez dans le même ordre

Q: Puis-je recevoir plusieurs webhooks pour la même transaction ?

R: Oui, vous pouvez recevoir plusieurs webhooks pour différentes étapes (Validation, Transmission, StatusCheck, etc.).

Q: Comment gérer les devises multiples ?

R: Utilisez le champ BillCurrency dans TransferPayload pour identifier la devise.

Q: Les webhooks contiennent-ils des données sensibles ?

R: Les comptes bancaires sont masqués. Aucun numéro de carte complet n'est transmis.

Problèmes courants

Problème: Je ne reçois pas de webhooks

Solutions :

Problème: Mon endpoint timeout

Solutions :

Problème: Erreur 401/403 sur mon endpoint

Solutions :

11. Support et contact

Obtenir de l'aide

Support technique TIB :

Informations à fournir

Pour un support efficace, incluez :

  1. Informations système
  2. Description du problème
  3. Exemples

Exemple de payload complet

{
  "Target": "Client",
  "Direction": "Collect",
  "Step": "StatusCheck",
  "Status": "Success_Success",
  "DescriptionCode": "1",
  "Description": "Transaction confirmée",
  "Amount": "123.45 $",
  "MerchantId": "00000000-0000-0000-0000-000000000000",
  "MerchantName": "Votre Entreprise Inc.",
  "MerchantAccountPreview": "***-*****-**1111",
  "MerchantExternalId": "MERCHANT-001",
  "MerchantDescription": "Entreprise de services",
  "TransferPayload": [
    {
      "TransferId": "11111111-1111-1111-1111-111111111111",
      "Amount": 123.45,
      "OperationKind": 1,
      "OriginalAmount": 123.45,
      "ExternalReferenceId": "FACTURE-2025-001",
      "DueDate": "10/3/2025 12:00:00 PM",
      "CreatedDate": "10/1/2025 10:00:00 AM",
      "PaymentMethodId": "22222222-2222-2222-2222-222222222222",
      "PaymentMethodDescription": "Compte bancaire",
      "PaymentMethodPreview": "***-*****-***1111",
      "CustomerId": "33333333-3333-3333-3333-333333333333",
      "CustomerName": "Jean Tremblay",
      "CustomerExternalId": "CLIENT-12345",
      "BillAmount": "123.45 $",
      "BillCurrency": "1",
      "BillDescription": "Facture mensuelle - Octobre 2025",
      "BillId": "44444444-4444-4444-4444-444444444444",
      "BillLanguage": "1",
      "BillTitle": "Facture #2025-001",
      "BillCreatedDate": "10/1/2025 9:00:00 AM",
      "ExternalSystemBillNumber1": "INV-2025-001",
      "ExternalSystemBillNumber2": "DEPT-A-001",
      "ExternalSystemBillNumber3": "",
      "BillMerchantId": "00000000-0000-0000-0000-000000000000",
      "BillCustomerId": "33333333-3333-3333-3333-333333333333",
      "Payout": null
    }
  ],
  "EventName": "TransactionEvent"
}

Résumé - Points clés

À FAIRE

  1. Configurer votre infrastructure réseau pour autoriser uniquement 204.225.113.0/24
  2. Répondre 200 OK immédiatement (moins de 30 secondes)
  3. Traiter de manière asynchrone
  4. Logger tous les événements
  5. Utiliser HTTPS
  6. Tester avant production

À ÉVITER

  1. Traiter de manière synchrone
  2. Exposer l'endpoint sans filtrage IP
  3. Retourner 200 OK si webhook non sauvegardé
  4. Ignorer les erreurs et les logs