Docs › Quickstarts › Go
BackendIntégrer NaabigaPay en Go
Un handler crée le lien de paiement avec net/http, un handler webhook vérifie la signature avec hmac, puis marque la commande payée.
Deux handlers côté serveur : l'un crée un lien de paiement quand un client veut payer, l'autre vérifie la signature du webhook une fois le paiement réussi. La clé secrète (sk_live_…) reste dans vos variables d'environnement et n'atteint jamais le navigateur.
1.Créer le lien
Votre front appelle ce handler ; il appelle POST/v1/payment-links avec la clé secrète, puis renvoie l'url de paiement. On sérialise le corps avec json.Marshal et on pose les en-têtes Authorization et Idempotency-Key.
package main
import (
"bytes"
"encoding/json"
"net/http"
"os"
)
const npay = "https://api.pay.naabiga.com/api/v1"
// Votre front appelle CE handler ; lui seul connaît la clé secrète.
func creerLien(w http.ResponseWriter, r *http.Request) {
var in struct {
Commande string `json:"commande"`
Amount int `json:"amount"`
}
json.NewDecoder(r.Body).Decode(&in)
corps, _ := json.Marshal(map[string]any{
"amount": in.Amount, // entier, en FCFA
"reference": in.Commande, // votre numéro de commande
"description": "Commande " + in.Commande,
"return_url": "https://monsite.com/merci",
"cancel_url": "https://monsite.com/panier",
"single_use": true,
})
req, _ := http.NewRequest("POST", npay+"/payment-links", bytes.NewReader(corps))
req.Header.Set("Authorization", "Bearer "+os.Getenv("NPAY_SECRET")) // sk_live_... : reste côté serveur
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "cmd-"+in.Commande) // évite un double lien si retry
res, err := http.DefaultClient.Do(req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
defer res.Body.Close()
var lien struct {
URL string `json:"url"`
}
json.NewDecoder(res.Body).Decode(&lien)
json.NewEncoder(w).Encode(map[string]string{"url": lien.URL}) // le front ouvre cette URL
}La réponse contient url (ex. https://pay.naabiga.com/link/9f3c…) : la page où le client paie. Pour un montant libre, remplacez amount par amount_min, amount_max et une description.
2.Vérifier le webhook
NaabigaPay envoie POST à l'URL enregistrée dans le portail marchand, avec les en-têtes X-NPay-Event et X-NPay-Signature (HMAC-SHA256 du corps brut). Lisez les octets bruts avec io.ReadAll(r.Body), recalculez le HMAC, comparez avec hmac.Equal, et ne créditez qu'ensuite.
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"os"
)
// Le secret du webhook reste côté serveur.
var webhookSecret = []byte(os.Getenv("NPAY_WEBHOOK_SECRET"))
func webhook(w http.ResponseWriter, r *http.Request) {
corps, err := io.ReadAll(r.Body) // corps BRUT : la signature porte dessus
if err != nil {
http.Error(w, "lecture du corps", http.StatusBadRequest)
return
}
mac := hmac.New(sha256.New, webhookSecret)
mac.Write(corps)
attendu := hex.EncodeToString(mac.Sum(nil))
signature := r.Header.Get("X-NPay-Signature")
// Comparaison à temps constant.
if !hmac.Equal([]byte(signature), []byte(attendu)) {
http.Error(w, "signature invalide", http.StatusBadRequest)
return
}
var charge struct {
Event string `json:"event"`
Data struct {
MerchantReference string `json:"merchant_reference"` // VOTRE numéro de commande
Reference string `json:"reference"` // ref de transaction NaabigaPay
} `json:"data"`
}
json.Unmarshal(corps, &charge)
if r.Header.Get("X-NPay-Event") == "payment.succeeded" {
// On crédite selon la reference du lien, pas selon un montant du corps.
marquerCommandePayee(charge.Data.MerchantReference)
}
w.WriteHeader(http.StatusOK)
}Astuce. Signez toujours sur les octets renvoyés par io.ReadAll. Si un middleware décode le JSON avant vous, le corps brut est perdu et le HMAC ne correspond plus.
Enregistrer les routes
package main
import "net/http"
func main() {
http.HandleFunc("/api/pay", creerLien)
http.HandleFunc("/webhooks/naabiga", webhook)
http.ListenAndServe(":8080", nil)
}Référence rapide
| Méthode | Endpoint | Rôle |
|---|---|---|
| POST | /v1/payment-links | Créer un lien de paiement |
| GET | /v1/payments/{reference} | Statut d'un paiement |
Base : https://api.pay.naabiga.com/api/v1 · Auth : Authorization: Bearer sk_…. Voir la documentation complète et le format des webhooks.