Docs › Quickstarts › Ruby on Rails
BackendIntégrer NaabigaPay en Ruby on Rails
Une action de contrôleur crée le lien de paiement avec Net::HTTP, une action webhook vérifie la signature avec OpenSSL::HMAC, puis marque la commande payée.
Deux actions 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 vos variables d'environnement et n'atteint jamais le navigateur.
1.Créer le lien
Votre front appelle cette action ; elle appelle POST/v1/payment-links avec la clé secrète, puis renvoie l'url de paiement. Ici avec Net::HTTP (aucune gem à installer).
require 'net/http'
require 'json'
require 'uri'
class PaiementsController < ApplicationController
NPAY = 'https://api.pay.naabiga.com/api/v1'.freeze
# Votre front appelle CETTE action ; elle seule connaît la clé secrète.
def creer
commande = params.fetch(:commande)
uri = URI("#{NPAY}/payment-links")
req = Net::HTTP::Post.new(uri)
req['Authorization'] = "Bearer #{ENV.fetch('NPAY_SECRET')}" # sk_live_... : reste côté serveur
req['Content-Type'] = 'application/json'
req['Idempotency-Key'] = "cmd-#{commande}" # évite un double lien si retry
req.body = {
amount: params.fetch(:amount).to_i, # entier, en FCFA
reference: commande, # votre numéro de commande
description: "Commande #{commande}",
return_url: 'https://monsite.com/merci',
cancel_url: 'https://monsite.com/panier',
single_use: true
}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
lien = JSON.parse(res.body)
render json: { url: lien['url'] } # le front ouvre cette URL
end
endLa 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. Vous pouvez aussi utiliser la gem faraday à la place de Net::HTTP, le corps et les en-têtes restent identiques.
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 request.raw_post (le corps intact), recalculez le HMAC, comparez avec ActiveSupport::SecurityUtils.secure_compare, et ne créditez qu'ensuite.
require 'openssl'
require 'json'
class WebhooksController < ApplicationController
# Requête serveur à serveur, sans jeton CSRF.
skip_before_action :verify_authenticity_token
def naabiga
corps = request.raw_post # corps BRUT : la signature porte sur ces octets
signature = request.headers['X-NPay-Signature'].to_s
secret = ENV.fetch('NPAY_WEBHOOK_SECRET')
attendu = OpenSSL::HMAC.hexdigest('SHA256', secret, corps)
# Comparaison à temps constant.
unless ActiveSupport::SecurityUtils.secure_compare(signature, attendu)
return head :bad_request
end
charge = JSON.parse(corps)
if request.headers['X-NPay-Event'] == 'payment.succeeded'
# data.merchant_reference = VOTRE numéro de commande (la reference du lien).
marquer_commande_payee(charge['data']['merchant_reference'])
end
head :ok
end
endAstuce. Lisez toujours request.raw_post avant tout parseur : si le corps est déjà transformé, le HMAC ne correspond plus.
Router les deux actions
Rails.application.routes.draw do
post 'api/pay', to: 'paiements#creer'
post 'webhooks/naabiga', to: 'webhooks#naabiga'
endRé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.