Skip to content

SDK .NET (C#) ​

La bibliothèque officielle SOAMTAR pour .NET (paquet Soamtar 1.1.0, espace de noms Soamtar) : encaissez et envoyez des paiements avec Orange Money Sierra Leone, en leones (SLE).

  • .NET 8, aucune dépendance hors bibliothèque de base (System.Text.Json)
  • Valeurs par défaut pour la Sierra Leone : devise SLE, fournisseur orange_money_sl
  • Montants en decimal (150.00m) ou "150.00", jamais en double/float
  • En-tête Idempotency-Key ajouté automatiquement à chaque POST
  • Vérification de la signature des webhooks (X-Soamtar-Signature)

Côté serveur uniquement

Le client utilise une clé secrète (sk_test_… ou sk_live_…). Ne l'embarquez jamais dans un navigateur ou une application mobile.

Installation ​

bash
dotnet add package Soamtar --version 1.1.0

Tant que le paquet n'est pas publié, référencez le projet depuis le dépôt (dotnet add reference chemin/vers/Soamtar.csproj).

Configuration ​

csharp
using Soamtar;

var soamtar = new SoamtarClient();   // lit SOAMTAR_SECRET_KEY et SOAMTAR_API_URL
var explicitClient = new SoamtarClient(Environment.GetEnvironmentVariable("SOAMTAR_SECRET_KEY")!);

// Votre propre HttpClient (par exemple via IHttpClientFactory), une autre URL de base, signature facultative
var custom = new SoamtarClient(secretKey, SoamtarClient.DefaultBaseUrl, httpClient, signRequests: true);
Variable d'environnementRôle
SOAMTAR_SECRET_KEYClé secrète, si aucune n'est passée.
SOAMTAR_API_URLURL de base (par défaut https://app.so-amtar.com/v1). SOAMTAR_BASE_URL reste lu en repli (ancien nom, déprécié).

Tous les arguments du constructeur sont facultatifs ; les valeurs explicites l'emportent toujours sur l'environnement.

Avec ASP.NET Core, enregistrez une instance unique :

csharp
builder.Services.AddSingleton(new SoamtarClient(builder.Configuration["Soamtar:SecretKey"]!));

Les clés sk_test_ passent par le même hôte que les clés live (https://app.so-amtar.com/v1) : les paiements sont simulés et aucun argent ne circule. soamtar.IsTestMode indique quelle clé est utilisée.

Signature des requêtes ​

new SoamtarClient(signRequests: true) ajoute Soamtar-Timestamp: <unix> et Soamtar-Signature: v1=hex(HMAC_SHA256(secretKey, "<timestamp>.<METHODE>.<chemin>")) à chaque POST, par exemple sur 1700000000.POST.v1/payments (chemin sans la barre oblique initiale ; horodatage à ±300 s). SoamtarClient.SignRequest(secretKey, timestamp, method, path) expose le calcul. C'est un durcissement facultatif : la clé Bearer sur HTTPS vous authentifie déjà. Voir Authentification.

Encaisser un paiement ​

csharp
try
{
    var response = await soamtar.Payments.CreateAsync(new Dictionary<string, object?>
    {
        ["merchant_ref"] = "ORDER-1042",
        ["amount"] = 150.00m,                 // Le 150.00
        ["phone"] = "+23276000001",           // +232 suivi de 8 chiffres
        ["description"] = "Commande n° 1042",
        ["success_url"] = "https://shop.example.sl/merci",
        ["failed_url"] = "https://shop.example.sl/panier",
        ["callback_url"] = "https://shop.example.sl/webhooks/soamtar", // facultatif
    });                                       // devise SLE et fournisseur orange_money_sl par défaut

    var payment = response.GetProperty("data");
    var status = payment.GetProperty("status").GetString();          // "pending"
    var paymentUrl = payment.GetProperty("payment_url").GetString(); // page de paiement hébergée
}
catch (SoamtarException e)
{
    Console.Error.WriteLine($"{e.HttpStatus} {e.Code} {e.Message}");
}

merchant_ref et amount sont vérifiés avant tout envoi (ArgumentException).

callback_url (facultatif, HTTPS, paiements et payouts) est une URL de webhook propre à la transaction : SOAMTAR y envoie tous ses événements avec le même payload et la même X-Soamtar-Signature que pour vos endpoints enregistrés (signée avec le secret de votre plus ancien endpoint actif : enregistrez-en au moins un). Vérifiez-le de la même façon.

Les appels renvoient un System.Text.Json.JsonElement (le corps {"data": …} décodé). Fournissez votre propre Idempotency-Key pour pouvoir relancer une requête sans risque :

csharp
await soamtar.Payments.CreateAsync(parameters, idempotencyKey: "order-1042-attempt-1");

Autres appels :

csharp
await soamtar.Payments.RetrieveAsync(id);               // par identifiant ou référence SOAMTAR
await soamtar.Payments.RetrieveAsync(id, wait: true);   // attend un statut final (long polling)
await soamtar.Payments.VerifyAsync(id);                 // revérifie le statut auprès d'Orange Money
await soamtar.Payments.ListAsync(new Dictionary<string, object?> { ["status"] = "success", ["per_page"] = 50 });
await soamtar.Payments.StatusAsync("TXN-1A2B3C4D-..."); // par identifiant ou référence SOAMTAR
await soamtar.Balance.RetrieveAsync();                  // disponible, en attente, réservé

Envoyer un paiement (payout) ​

Les payouts exigent une clé disposant de la permission de payout. merchant_ref, amount et recipient.phone (+232 suivi de 8 chiffres) sont vérifiés avant l'envoi.

csharp
await soamtar.Payouts.CreateAsync(new Dictionary<string, object?>
{
    ["merchant_ref"] = "PAYOUT-77",
    ["amount"] = 50.00m,
    ["recipient"] = new Dictionary<string, object?> { ["phone"] = "+23276000001", ["name"] = "Aminata Kamara" },
}, idempotencyKey: "payout-PAYOUT-77");
await soamtar.Payouts.RetrieveAsync(id);
await soamtar.Payouts.ListAsync(new Dictionary<string, object?> { ["status"] = "success" });

Remboursements ​

Remboursez un paiement réussi, en totalité ou en partie : SOAMTAR recrédite le montant sur le numéro Orange Money du payeur et débite votre solde. La clé doit disposer du scope can_refund.

csharp
var response = await soamtar.Refunds.CreateAsync(new Dictionary<string, object?>
{
    ["transaction_id"] = paymentId,        // identifiant (ou référence) SOAMTAR du paiement
    ["amount"] = 50.00m,                   // facultatif : omis = remboursement total du restant
    ["reason"] = "customer_request",       // facultatif
    ["description"] = "Article en rupture", // facultatif
}, idempotencyKey: "refund-order-1042-1");

var refund = response.GetProperty("data");
refund.GetProperty("reference").GetString(); // "REF-…"
refund.GetProperty("status").GetString();    // "success" (clé de test), "processing" (Orange Money) ou "failed"

await soamtar.Refunds.RetrieveAsync("REF-…");   // identifiant ou référence
await soamtar.Refunds.ListAsync(new Dictionary<string, object?> { ["transaction_id"] = paymentId, ["status"] = "success" });

Le remboursement est soumis dans la même requête ; avec Orange Money il reste processing jusqu'à ce qu'Orange confirme le crédit. Le résultat arrive par refund.success / refund.failed, plus payment.refunded / payment.partially_refunded en cas de succès. Voir Remboursements pour l'objet et les codes d'erreur.

Webhooks ​

SOAMTAR envoie les événements aux endpoints que vous enregistrez (tableau de bord ou Webhooks.CreateAsync), signés avec le secret de l'endpoint : X-Soamtar-Signature: t=<unix>,v1=<hmac> (HMAC-SHA256 de "<t>.<corps brut>"). Pendant une rotation du secret, une signature v2= est ajoutée ; l'une ou l'autre est acceptée. Les signatures de plus de 300 secondes sont rejetées.

ASP.NET Core (minimal API) ​

csharp
app.MapPost("/webhooks/soamtar", async (HttpRequest request) =>
{
    using var reader = new StreamReader(request.Body);
    var payload = await reader.ReadToEndAsync();   // corps BRUT

    try
    {
        var evt = Webhook.ConstructEvent(payload, request.Headers[Webhook.SignatureHeader],
            Environment.GetEnvironmentVariable("SOAMTAR_WEBHOOK_SECRET")!);
        var data = evt.GetProperty("data");

        switch (evt.GetProperty("event").GetString())
        {
            case "payment.success":
                MarkPaid(data.GetProperty("merchant_reference").GetString());
                break;
            case "payment.failed":
            case "payment.expired":
            case "payment.cancelled":
                MarkFailed(data.GetProperty("merchant_reference").GetString());
                break;
            case "refund.success":
                MarkRefunded(data.GetProperty("merchant_reference").GetString(), data.GetProperty("amount").GetString());
                break;
            // webhook.test et le reste : simple 200
        }
        return Results.Ok();
    }
    catch (WebhookSignatureException)
    {
        return Results.BadRequest("Invalid signature");
    }
});

Un même événement peut être livré plusieurs fois : dédoublonnez sur event + data.id.

Événements auxquels s'abonner : payment.success, payment.failed, payment.cancelled, payment.expired, payment.refunded, payment.partially_refunded, payout.success, payout.failed, payout.cancelled, refund.success, refund.failed, payout_request.submitted, payout_request.approved, payout_request.rejected, payout_request.completed, payout_request.failed. webhook.test est envoyé par soamtar.Webhooks.SendTestAsync() et par le bouton de test du tableau de bord, quels que soient vos abonnements. Voir Webhooks.

Gérer les endpoints ​

csharp
await soamtar.Webhooks.CreateAsync("https://shop.example.sl/webhooks/soamtar");   // événements par défaut
await soamtar.Webhooks.CreateAsync("https://shop.example.sl/webhooks/soamtar",
    new[] { "payment.success", "payment.failed", "refund.success", "refund.failed" });
await soamtar.Webhooks.ListAsync();
await soamtar.Webhooks.SecretAsync(endpointId);   // secret de signature
await soamtar.Webhooks.SendTestAsync();           // envoie un événement webhook.test

Sans liste d'événements (ou avec null / une liste vide), CreateAsync() abonne l'endpoint à payment.success, payment.failed, payout.success et payout.failed (Webhooks.DefaultEvents), comme les autres SDK.

Erreurs ​

Les erreurs de l'API lèvent une SoamtarException avec HttpStatus, Code (par exemple validation_error) et le corps brut Body. Les pannes réseau ont le statut 0 et le code network_error. Une clé invalide, un montant en double ou un décimal à plus de 2 décimales lèvent une ArgumentException.

Sécurité ​

  • Comparaison des signatures en temps constant.
  • La clé secrète est masquée dans ToString().
  • Les webhooks de plus de 1 Mo sont rejetés.

SOAMTAR Payment API Documentation.