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, fournisseurorange_money_sl - Montants en
String("150.00") ouBigDecimal, jamais endouble/float - En-tête
Idempotency-Keyajouté 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 :
<dependency>
<groupId>com.soamtar</groupId>
<artifactId>soamtar-java</artifactId>
<version>1.1.0</version>
</dependency>Gradle :
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
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'environnement | Rôle |
|---|---|
SOAMTAR_SECRET_KEY | Clé secrète, si aucune n'est passée au constructeur. |
SOAMTAR_API_URL | URL 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
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 :
soamtar.payments.create(params, "order-1042-attempt-1");Autres appels :
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.
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.
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
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
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.testSans 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.HttpClientnatif, sans bibliothèque HTTP tierce.- Comparaison des signatures en temps constant.
- La clé secrète est masquée dans
toString().