Skip to content

SDK Go ​

Le module officiel SOAMTAR (github.com/soamtar/soamtar-go-sdk, version 1.1.0) : encaissez et envoyez des paiements avec Orange Money Sierra Leone, en leones (SLE).

  • Go 1.18+, bibliothèque standard uniquement
  • Valeurs par défaut pour la Sierra Leone : devise SLE, fournisseur orange_money_sl
  • Montants en chaînes décimales ("150.00"), jamais en flottants
  • 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
go get github.com/soamtar/soamtar-go-sdk

Configuration ​

go
import soamtar "github.com/soamtar/soamtar-go-sdk"

client, err := soamtar.NewClient("")   // lit SOAMTAR_SECRET_KEY
if err != nil {
	log.Fatal(err)
}

client, err = soamtar.NewClient("sk_test_...", soamtar.WithTimeout(30*time.Second))
OptionDéfautRôle
WithBaseURL(url)SOAMTAR_API_URL, puis SOAMTAR_BASE_URL (ancien nom, déprécié), sinon https://app.so-amtar.com/v1Racine de l'API
WithHTTPClient(c)&http.Client{Timeout: 15s}Votre propre *http.Client
WithTimeout(d)15 sDélai d'attente des requêtes
WithSignRequests(true)désactivéSigne chaque POST (voir Signature des requêtes)

Les clés sk_test_ passent par le même hôte que les clés live : les paiements sont simulés et aucun argent ne circule. client.IsTestMode() indique quelle clé est utilisée.

Signature des requêtes ​

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

Encaisser un paiement ​

go
payment, err := client.Payments.Create(ctx, soamtar.PaymentParams{
	MerchantRef: "ORDER-1042",
	Amount:      "150.00",                              // Le 150.00
	Payer:       &soamtar.Payer{Phone: "+23276000001"}, // +232 suivi de 8 chiffres
	Description: "Commande n° 1042",
	SuccessURL:  "https://shop.example.sl/merci",
	FailedURL:   "https://shop.example.sl/panier",
	CallbackURL: "https://shop.example.sl/webhooks/soamtar", // facultatif
}) // Currency "SLE" et Provider "orange_money_sl" par défaut
if err != nil {
	return err
}

fmt.Println(payment.Data()["status"]) // pending
http.Redirect(w, r, payment.PaymentURL(), http.StatusSeeOther) // page de paiement hébergée

PaymentParams accepte aussi Currency (SLE, USD, EUR), Provider, PaymentMethod, CallbackURL, Metadata et IdempotencyKey (envoyé dans l'en-tête Idempotency-Key ; UUID v4 aléatoire s'il est vide).

CallbackURL (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).

Autres appels :

go
client.Payments.Retrieve(ctx, id, false)   // par identifiant ou référence SOAMTAR
client.Payments.Retrieve(ctx, id, true)    // attend un statut final (long polling)
client.Payments.Verify(ctx, id)            // revérifie le statut auprès d'Orange Money
client.Payments.List(ctx, url.Values{"status": {"success"}, "per_page": {"50"}})
client.Payments.Status(ctx, "TXN-1A2B3C4D-...") // par identifiant ou référence SOAMTAR
client.Balance.Retrieve(ctx)

Chaque appel renvoie une soamtar.Response (map[string]interface{}) dotée des méthodes Data() et PaymentURL().

Envoyer un paiement (payout) ​

go
client.Payouts.Create(ctx, soamtar.PayoutParams{
	MerchantRef:    "PAYOUT-77",
	Amount:         "50.00",
	Recipient:      soamtar.Recipient{Phone: "+23276000001", Name: "Aminata Kamara"},
	Description:    "Paiement fournisseur",
	IdempotencyKey: "payout-PAYOUT-77",
})
client.Payouts.Retrieve(ctx, id)
client.Payouts.List(ctx, url.Values{"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.

go
refund, err := client.Refunds.Create(ctx, soamtar.RefundParams{
	TransactionID:  paymentID,           // identifiant (ou référence) SOAMTAR du paiement
	Amount:         "50.00",             // vide : remboursement total du restant
	Reason:         "customer_request",
	Description:    "Article en rupture de stock",
	IdempotencyKey: "refund-order-1042-1",
})
if err != nil {
	return err
}
fmt.Println(refund.Data()["reference"], refund.Data()["status"]) // REF-…, processing

client.Refunds.Retrieve(ctx, refundID) // identifiant ou référence
client.Refunds.List(ctx, url.Values{"transaction_id": {paymentID}, "status": {"success"}})

Le remboursement est soumis dans la même requête : success avec une clé de test, processing avec Orange Money jusqu'à la confirmation d'Orange, failed (avec failure_reason) si le crédit est refusé. 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.

net/http ​

go
http.HandleFunc("/webhooks/soamtar", func(w http.ResponseWriter, r *http.Request) {
	payload, err := io.ReadAll(http.MaxBytesReader(w, r.Body, soamtar.MaxPayloadSize))
	if err != nil {
		http.Error(w, "payload too large", http.StatusRequestEntityTooLarge)
		return
	}

	event, err := soamtar.ConstructEvent(payload, r.Header.Get(soamtar.SignatureHeader), os.Getenv("SOAMTAR_WEBHOOK_SECRET"))
	if err != nil { // enveloppe soamtar.ErrInvalidSignature
		http.Error(w, "invalid signature", http.StatusBadRequest)
		return
	}

	switch event.Type() {
	case "payment.success":
		markOrderPaid(event.Data()["merchant_reference"])
	case "payment.failed", "payment.expired", "payment.cancelled":
		markOrderFailed(event.Data()["merchant_reference"])
	case "refund.success":
		markOrderRefunded(event.Data()["merchant_reference"], event.Data()["amount"])
	} // webhook.test et le reste : simple 200
	w.WriteHeader(http.StatusOK)
})

Gin ​

go
r.POST("/webhooks/soamtar", func(c *gin.Context) {
	payload, _ := io.ReadAll(http.MaxBytesReader(c.Writer, c.Request.Body, soamtar.MaxPayloadSize))

	event, err := soamtar.ConstructEvent(payload, c.GetHeader(soamtar.SignatureHeader), os.Getenv("SOAMTAR_WEBHOOK_SECRET"))
	if err != nil {
		c.Status(http.StatusBadRequest)
		return
	}

	_ = event // event.Type(), event.Data()
	c.Status(http.StatusOK)
})

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 client.Webhooks.SendTest et par le bouton de test du tableau de bord, quels que soient vos abonnements. Voir Webhooks.

Gérer les endpoints ​

go
client.Webhooks.Create(ctx, soamtar.WebhookEndpointParams{
	URL: "https://shop.example.sl/webhooks/soamtar", // SubscribedEvents vide = DefaultWebhookEvents
})
client.Webhooks.Create(ctx, soamtar.WebhookEndpointParams{
	URL:              "https://shop.example.sl/webhooks/soamtar",
	SubscribedEvents: []string{"payment.success", "refund.success", "refund.failed"},
})
client.Webhooks.List(ctx)
client.Webhooks.Lookup(ctx, "https://shop.example.sl/webhooks/soamtar")
client.Webhooks.Secret(ctx, endpointID) // secret de signature
client.Webhooks.SendTest(ctx)            // envoie un événement webhook.test

DefaultWebhookEvents vaut payment.success, payment.failed, payout.success, payout.failed. Dépréciés mais toujours fonctionnels : Webhooks.Test (utilisez SendTest), Status.Retrieve (utilisez Payments.Status).

Erreurs ​

go
var apiErr *soamtar.APIError
if errors.As(err, &apiErr) {
	log.Println(apiErr.StatusCode, apiErr.Code, apiErr.Message)
	log.Println(apiErr.Errors) // messages par champ en cas de 422
}

Les autres erreurs (clé invalide, montant invalide, réseau) sont des error simples préfixées par soamtar:.

Sécurité ​

  • Comparaison des signatures en temps constant (hmac.Equal).
  • La clé secrète est masquée par String() et %#v.
  • Les webhooks de plus de 1 Mo sont rejetés (soamtar.MaxPayloadSize).

SOAMTAR Payment API Documentation.