NaabigaPay · Java / Spring
QuickstartsDocs

DocsQuickstarts › Java / Spring

Backend

Intégrer NaabigaPay en Java / Spring

Un contrôleur crée le lien de paiement avec HttpClient, un endpoint webhook vérifie la signature avec Mac HmacSHA256, puis marque la commande payée.

Deux méthodes côté serveur : l'une 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 votre configuration serveur et n'atteint jamais le navigateur.

1.Créer le lien

Votre front appelle cette route ; elle appelle POST/v1/payment-links avec la clé secrète, puis renvoie l'url de paiement.

PaiementController.java
package com.monsite.pay;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PaiementController {

  private static final String NPAY = "https://api.pay.naabiga.com/api/v1";
  private static final String SECRET = System.getenv("NPAY_SECRET"); // sk_live_...

  private final HttpClient http = HttpClient.newHttpClient();
  private final ObjectMapper json = new ObjectMapper();

  // Votre front appelle CETTE route, jamais NaabigaPay directement.
  @PostMapping("/api/pay")
  public Map<String, String> creerLien(@RequestBody Map<String, Object> corps) throws Exception {
    String commande = corps.get("commande").toString();
    String body = json.writeValueAsString(Map.of(
        "amount", corps.get("amount"),        // entier, en FCFA
        "reference", commande,                // votre numéro de commande
        "description", "Commande " + commande,
        "return_url", "https://monsite.com/merci",
        "single_use", true));

    HttpRequest req = HttpRequest.newBuilder(URI.create(NPAY + "/payment-links"))
        .header("Authorization", "Bearer " + SECRET)
        .header("Content-Type", "application/json")
        .header("Idempotency-Key", "cmd-" + commande) // évite un double lien si retry
        .POST(HttpRequest.BodyPublishers.ofString(body))
        .build();

    HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
    String url = json.readTree(res.body()).get("url").asText();
    return Map.of("url", url);              // le front ouvre cette URL
  }
}

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). Recevez le corps en byte[] (octets intacts), recalculez le HMAC en hexadécimal et comparez en temps constant avec MessageDigest.isEqual.

WebhookController.java
package com.monsite.pay;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebhookController {

  private static final byte[] WEBHOOK_SECRET =
      System.getenv("NPAY_WEBHOOK_SECRET").getBytes(StandardCharsets.UTF_8);
  private final ObjectMapper json = new ObjectMapper();

  @PostMapping("/webhook")
  public ResponseEntity<String> recevoir(
      @RequestBody byte[] raw,                            // corps BRUT (octets exacts)
      @RequestHeader("X-NPay-Signature") String signature,
      @RequestHeader("X-NPay-Event") String event) throws Exception {

    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(WEBHOOK_SECRET, "HmacSHA256"));
    byte[] brut = mac.doFinal(raw);

    StringBuilder hex = new StringBuilder(brut.length * 2);
    for (byte x : brut) hex.append(String.format("%02x", x));
    String attendu = hex.toString();

    // MessageDigest.isEqual : comparaison à temps constant.
    boolean ok = MessageDigest.isEqual(
        attendu.getBytes(StandardCharsets.UTF_8),
        signature.getBytes(StandardCharsets.UTF_8));
    if (!ok) return ResponseEntity.status(400).body("signature invalide");

    if ("payment.succeeded".equals(event)) {
      // data.merchant_reference = VOTRE numéro de commande (la reference du lien).
      String ref = json.readTree(raw).path("data").path("merchant_reference").asText();
      marquerCommandePayee(ref);
    }
    return ResponseEntity.ok("ok");
  }
}

Astuce. Recevez le corps en byte[] et non en String : signez sur les octets exacts reçus, sinon un simple ré-encodage casse la vérification.

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.