Skip to content

SDK Java ​

La bibliothèque officielle SOAMTAR pour Java (com.soamtar:soamtar-java 1.1.0) : encaissez et envoyez des paiements avec Orange Money Sierra Leone, en leones (SLE).

  • Java 11+, une seule dépendance (Jackson)
  • Valeurs par défaut pour la Sierra Leone : devise SLE, fournisseur orange_money_sl
  • Montants en String ("150.00") ou BigDecimal, 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 ​

Maven :

xml
<dependency>
    <groupId>com.soamtar</groupId>
    <artifactId>soamtar-java</artifactId>
    <version>1.1.0</version>
</dependency>

Gradle :

groovy
implementation 'com.soamtar:soamtar-java:1.1.0'

Tant que l'artefact n'est pas publié, compilez-le depuis le dépôt (mvn install).

Configuration ​

java
import com.soamtar.sdk.SoamtarClient;

SoamtarClient soamtar = new SoamtarClient();   // lit SOAMTAR_SECRET_KEY et SOAMTAR_API_URL
SoamtarClient explicit = new SoamtarClient(System.getenv("SOAMTAR_SECRET_KEY"));

// Autre URL de base, autre délai d'attente (30 s par défaut) et signature facultative des requêtes
SoamtarClient custom = new SoamtarClient(secretKey, SoamtarClient.DEFAULT_BASE_URL, Duration.ofSeconds(60), true);
Variable d'environnementRôle
SOAMTAR_SECRET_KEYClé secrète, si aucune n'est passée au constructeur.
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é).

Les valeurs passées au constructeur l'emportent toujours sur l'environnement.

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 ​

Avec le dernier argument du constructeur signRequests = true (new SoamtarClient(null, null, Duration.ofSeconds(30), true)), chaque POST porte aussi Soamtar-Timestamp: <unix> et Soamtar-Signature: v1=hex(HMAC_SHA256(secretKey, "<timestamp>.<METHODE>.<chemin>")), par exemple sur 1700000000.POST.v1/payments (chemin sans la barre oblique initiale ; horodatage à ±300 s). C'est un durcissement facultatif : la clé Bearer sur HTTPS vous authentifie déjà. Voir Authentification.

Encaisser un paiement ​

java
import com.soamtar.sdk.SoamtarException;
import java.util.Map;

try {
    Map<String, Object> response = soamtar.payments.create(Map.of(
        "merchant_ref", "ORDER-1042",
        "amount", "150.00",              // 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

    @SuppressWarnings("unchecked")
    Map<String, Object> payment = (Map<String, Object>) response.get("data");
    payment.get("status");               // "pending"
    payment.get("payment_url");          // page de paiement hébergée
} catch (SoamtarException e) {
    System.err.println(e.getHttpStatus() + " " + e.getCode() + " " + e.getMessage());
}

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

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.

Fournissez votre propre Idempotency-Key pour pouvoir relancer une requête sans risque :

java
soamtar.payments.create(params, "order-1042-attempt-1");

Autres appels :

java
soamtar.payments.retrieve(id);                    // par identifiant ou référence SOAMTAR
soamtar.payments.retrieve(id, true);              // attend un statut final (long polling)
soamtar.payments.verify(id);                      // revérifie le statut auprès d'Orange Money
soamtar.payments.list(Map.of("status", "success", "per_page", 50));
soamtar.payments.status("TXN-1A2B3C4D-...");      // par identifiant ou référence SOAMTAR
soamtar.balance.retrieve();                       // 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.

java
soamtar.payouts.create(Map.of(
    "merchant_ref", "PAYOUT-77",
    "amount", "50.00",
    "recipient", Map.of("phone", "+23276000001", "name", "Aminata Kamara")
), "payout-PAYOUT-77");                    // Idempotency-Key
soamtar.payouts.retrieve(id);
soamtar.payouts.list(Map.of("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.

java
Map<String, Object> response = soamtar.refunds.create(Map.of(
    "transaction_id", paymentId,          // identifiant (ou référence) SOAMTAR du paiement
    "amount", "50.00",                    // facultatif : omis = remboursement total du restant
    "reason", "customer_request",         // facultatif
    "description", "Article en rupture"   // facultatif
), "refund-order-1042-1");                // Idempotency-Key

Map<String, Object> refund = (Map<String, Object>) response.get("data");
refund.get("reference");                  // "REF-…"
refund.get("status");                     // "success" (clé de test), "processing" (Orange Money) ou "failed"

soamtar.refunds.retrieve("REF-…");        // identifiant ou référence
soamtar.refunds.list(Map.of("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.create), 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.

Spring Boot ​

java
import com.soamtar.sdk.Webhook;

@RestController
public class SoamtarWebhookController {

    @Value("${soamtar.webhook-secret}")
    private String webhookSecret;

    @PostMapping("/webhooks/soamtar")
    @SuppressWarnings("unchecked")
    public ResponseEntity<String> handle(@RequestBody String payload,
                                         @RequestHeader(Webhook.SIGNATURE_HEADER) String signature) {
        try {
            Map<String, Object> event = Webhook.constructEvent(payload, signature, webhookSecret);
            Map<String, Object> data = (Map<String, Object>) event.get("data");

            switch ((String) event.get("event")) {
                case "payment.success" -> markPaid((String) data.get("merchant_reference"));
                case "payment.failed", "payment.expired", "payment.cancelled" -> markFailed((String) data.get("merchant_reference"));
                case "refund.success" -> markRefunded((String) data.get("merchant_reference"), (String) data.get("amount"));
                default -> { }   // webhook.test et le reste : simple 200
            }
            return ResponseEntity.ok("OK");
        } catch (Webhook.WebhookSignatureException e) {
            return ResponseEntity.badRequest().body("Invalid signature");
        }
    }
}

Recevez le corps en String (@RequestBody String) pour que la signature soit vérifiée sur les octets bruts. 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.sendTest() et par le bouton de test du tableau de bord, quels que soient vos abonnements. Voir Webhooks.

Gérer les endpoints ​

java
soamtar.webhooks.create("https://shop.example.sl/webhooks/soamtar");   // événements par défaut
soamtar.webhooks.create("https://shop.example.sl/webhooks/soamtar",
    List.of("payment.success", "payment.failed", "refund.success", "refund.failed"));
soamtar.webhooks.list();
soamtar.webhooks.secret(endpointId);   // secret de signature
soamtar.webhooks.sendTest();           // envoie un événement webhook.test

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

Erreurs ​

Les erreurs de l'API lèvent une SoamtarException : getHttpStatus(), getCode() (par exemple validation_error) et getBody() (JSON décodé). Les pannes réseau ont le statut 0 et le code network_error. Une clé invalide ou un montant en double lèvent une IllegalArgumentException.

Sécurité ​

  • java.net.http.HttpClient natif, sans bibliothèque HTTP tierce.
  • Comparaison des signatures en temps constant.
  • La clé secrète est masquée dans toString().

SOAMTAR Payment API Documentation.