NaabigaPay · Go
QuickstartsDocs

DocsQuickstarts › Go

Backend

Inté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.

paiement.go
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.

webhook.go
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

main.go
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éthodeEndpointRôle
POST/v1/payment-linksCré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.