TIB Financial Solutions
Version 1.0.0 | 3 octobre 2025
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.).
200 OKCréez une URL publique qui recevra les webhooks :
Exemple : https://votre-domaine.com/api/webhooks/tib/transaction
Recommandations :
Contactez votre représentant TIB et fournissez l'URL complète de votre endpoint.
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.
Sans ce filtrage, n'importe qui sur Internet pourrait envoyer de fausses requêtes à votre endpoint et potentiellement manipuler vos données financières.
| 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é |
POST /api/webhooks/tib/transaction HTTP/1.1
Host: votre-domaine.com
Content-Type: application/json
{
"EventName": "TransactionEvent",
"Target": "Client",
"Direction": "Collect",
...
}
| 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 |
| 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) |
| 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 |
| 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 |
| Valeur | Nom | Description |
|---|---|---|
| -1 | Unknown | Type inconnu |
| 0 | NotSet | Non défini |
| 1 | Payment | Paiement standard |
| 2 | FreeDeposit | Dépôt gratuit |
| 3 | FreeCollection | Collection gratuite |
| 4 | FeePayment | Frais de paiement |
| 5 | FeeFreeDeposit | Frais de dépôt gratuit |
| 6 | FeeFreeCollection | Frais de collection gratuite |
| 10 | ReturnCharge | Frais de retour |
| 11 | FileFees | Frais de dossier |
| 12 | Refund | Remboursement |
| 13 | TibCollect | Collection TIB |
| 14 | AccountCheckNsfFees | Frais NSF vérification de compte |
| 15 | AccountCheckOppositionFees | Frais d'opposition vérification |
| 16 | AccountCheckNsfFeesHit | Impact frais NSF |
| 17 | AccountCheckOppositionFeesHit | Impact frais d'opposition |
| 18 | FeeReverseCharge | Frais de contre-passation |
| 19 | RevertedOperation | Opération annulée |
| 20 | ConvenientFee | Frais de commodité |
| 21 | RevertConvenientFee | Annulation frais de commodité |
| 22 | PlatformFee | Frais de plateforme |
| 23 | GatewayUsageFee | Frais d'utilisation passerelle |
| 24 | SupportFee | Frais de support |
| 100 | GroupedFees | Frais groupés |
| 101 | FeesRetransfer | Retransfert de frais |
1. Validation → Step: Validation, Status: Success_Skip
2. Transmission → Step: Transmission, Status: Success_Success
3. StatusCheck → Step: StatusCheck, Status: Success_NoResultReturned
1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. StatusCheck → Status: Success_NoResultReturned
4. Payback → Status: Success_Error, DescriptionCode: "3" (NoFund)
1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. StatusCheck → Status: Success_Error, DescriptionCode: "5" (Opposition)
1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. Payback → Status: Success_Error, DescriptionCode: "4" (AccountError)
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.
Internet
↓
Firewall (Autoriser 204.225.113.0/24)
↓
Endpoint /webhooks
↓
Valider Payload + Logger
↓
Retourner 200 OK (IMPORTANT)
↓
Traitement asynchrone
Votre endpoint DOIT retourner un code HTTP 200 OK en moins de 30 secondes.
[HttpPost("webhooks/tib/transaction")]
public IActionResult ReceiveWebhook([FromBody] TransactionEventPayload payload)
{
_ = TraiterEnArrierePlan(payload);
return Ok();
}
[HttpPost("webhooks/tib/transaction")]
public IActionResult ReceiveWebhook([FromBody] TransactionEventPayload payload)
{
TraiterTransaction(payload); // Peut prendre trop de temps
return Ok();
}
_logger.LogInformation(
"Webhook reçu - TransferId: {TransferId}, Status: {Status}, Amount: {Amount}",
transferId,
payload.Status,
payload.Amount
);
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);
}
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
| 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 |
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
);
}
}
}
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; }
}
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}`);
});
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)
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.
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 :
Requête :
POST /api/webhooks/tib/transaction
{
"EventName": "TransactionEvent"
}
Résultat attendu :
Simulation : Éteindre votre base de données
Résultat attendu :
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.
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.
R: Contactez votre représentant TIB pour discuter des options de récupération des événements manqués.
R: Oui
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
R: Oui, vous pouvez recevoir plusieurs webhooks pour différentes étapes (Validation, Transmission, StatusCheck, etc.).
R: Utilisez le champ BillCurrency dans TransferPayload pour identifier la devise.
R: Les comptes bancaires sont masqués. Aucun numéro de carte complet n'est transmis.
Solutions :
Solutions :
Solutions :
Support technique TIB :
Pour un support efficace, incluez :
{
"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"
}