SDK Ruby
La gem officielle SOAMTAR (soamtar 1.1.0) : encaissez et envoyez des paiements avec Orange Money Sierra Leone, en leones (SLE).
- Ruby 2.6+, aucune dépendance à l'exécution (
net/http,openssl,json) - Valeurs par défaut pour la Sierra Leone : devise
SLE, fournisseurorange_money_sl - Montants en chaînes à 2 décimales (
"150.00"),IntegerouBigDecimal, jamaisFloat - En-tête
Idempotency-Keyajouté 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
# Gemfile
gem 'soamtar'Tant que la gem n'est pas publiée, installez-la depuis le dépôt (gem 'soamtar', git: '<URL du dépôt>').
Configuration
require 'soamtar'
client = Soamtar::Client.new # lit SOAMTAR_SECRET_KEY
client = Soamtar::Client.new('sk_test_...') # ou passez la clé secrète
# Client partagé, par exemple dans config/initializers/soamtar.rb
Soamtar.configure(ENV['SOAMTAR_SECRET_KEY'])
Soamtar.payments.create(...)| Option | Défaut | Rôle |
|---|---|---|
base_url: | SOAMTAR_API_URL, puis SOAMTAR_BASE_URL (ancien nom, déprécié), sinon https://app.so-amtar.com/v1 | Racine de l'API |
timeout: | 15 | Délai d'attente en secondes |
sign_requests: | false | Signe chaque POST (voir Signature des requêtes) |
client = Soamtar::Client.new(ENV['SOAMTAR_SECRET_KEY'], timeout: 30)
client.test_mode? # true avec une clé sk_test_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.
Signature des requêtes
Soamtar::Client.new(key, sign_requests: true) ajoute Soamtar-Timestamp: <unix> et Soamtar-Signature: v1=hex(HMAC_SHA256(secret_key, "<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
payment = client.payments.create(
{ merchant_ref: 'ORDER-1042',
amount: '150.00', # Le 150.00
phone: '+23276000001', # +232 suivi de 8 chiffres
description: 'Commande n° 1042',
success_url: 'https://shop.example.sl/merci',
failed_url: 'https://shop.example.sl/panier',
callback_url: 'https://shop.example.sl/webhooks/soamtar' }, # facultatif
idempotency_key: 'order-1042'
) # devise 'SLE' et fournisseur 'orange_money_sl' par défaut
payment['data']['status'] # "pending"
payment.payment_url # page de paiement hébergée (data.payment_url)callback_url (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).
Avec Rails, redirigez le client vers la page hébergée :
redirect_to payment.payment_url, allow_other_host: trueFournissez votre propre Idempotency-Key pour pouvoir relancer une requête sans risque :
client.payments.create(params, idempotency_key: 'order-1042-attempt-1')Autres appels :
client.payments.retrieve(id) # par identifiant ou référence SOAMTAR
client.payments.retrieve(id, wait: true) # attend un statut final (long polling)
client.payments.verify(id) # revérifie le statut auprès d'Orange Money
client.payments.list(status: 'success', per_page: 50)
client.payments.status('TXN-1A2B3C4D-...') # par identifiant ou référence SOAMTAR
client.balance.retrieveEnvoyer un paiement (payout)
client.payouts.create(
{ merchant_ref: 'PAYOUT-77',
amount: '50.00',
recipient: { phone: '+23276000001', name: 'Aminata Kamara' },
description: 'Paiement fournisseur' },
idempotency_key: 'payout-PAYOUT-77'
)
client.payouts.retrieve(id)
client.payouts.list(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.
refund = client.refunds.create(
{ transaction_id: payment['data']['id'], # identifiant (ou référence) SOAMTAR du paiement
amount: '50.00', # omis : remboursement total du restant
reason: 'customer_request',
description: 'Article en rupture de stock' },
idempotency_key: 'refund-order-1042-1'
)
refund['data']['reference'] # "REF-…"
refund['data']['status'] # "success" (clé de test), "processing" (Orange Money) ou "failed"
client.refunds.retrieve(refund['data']['reference']) # identifiant ou référence
client.refunds.list(transaction_id: payment['data']['id'], status: 'success')Le remboursement est soumis dans la même requête ; avec Orange Money il reste processing jusqu'à ce qu'Orange confirme le crédit. 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.
Rails
class SoamtarWebhooksController < ActionController::API
def create
event = Soamtar::Webhook.construct_event(
request.raw_post, # corps BRUT
request.headers['X-Soamtar-Signature'],
ENV.fetch('SOAMTAR_WEBHOOK_SECRET')
)
case event['event']
when 'payment.success'
Order.mark_paid!(event['data']['merchant_reference'])
when 'payment.failed', 'payment.expired', 'payment.cancelled'
Order.mark_failed!(event['data']['merchant_reference'])
when 'refund.success'
Order.mark_refunded!(event['data']['merchant_reference'], event['data']['amount'])
end # webhook.test et le reste : simple 200
head :ok
rescue Soamtar::SignatureError
head :bad_request
end
endSinatra
require 'sinatra'
require 'soamtar'
post '/webhooks/soamtar' do
event = Soamtar::Webhook.construct_event(
request.body.read,
request.env['HTTP_X_SOAMTAR_SIGNATURE'],
ENV.fetch('SOAMTAR_WEBHOOK_SECRET')
)
# traitez event['event'] / event['data']
status 200
rescue Soamtar::SignatureError
status 400
endUn 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.send_test et par le bouton de test du tableau de bord, quels que soient vos abonnements. Voir Webhooks.
Gérer les endpoints
client.webhooks.create('https://shop.example.sl/webhooks/soamtar') # par défaut : payment.success/failed, payout.success/failed
client.webhooks.create('https://shop.example.sl/webhooks/soamtar', %w[payment.success refund.success refund.failed])
client.webhooks.list
client.webhooks.lookup('https://shop.example.sl/webhooks/soamtar')
client.webhooks.secret(endpoint_id) # secret de signature
client.webhooks.send_test # envoie un événement webhook.testAlias dépréciés, toujours fonctionnels : client.webhooks.test (utilisez send_test), client.status.retrieve (utilisez client.payments.status).
Erreurs
begin
client.payments.create(...)
rescue Soamtar::ApiError => e
e.status # statut HTTP
e.code # code d'erreur de l'API, par exemple "validation_error"
e.errors # messages par champ en cas de 422
rescue Soamtar::Error => e
e.message # configuration, réseau ou saisie invalide
endSécurité
- Comparaison des signatures en temps constant.
- La clé secrète est masquée dans
inspect,to_setmarshal_dump. - Les webhooks de plus de 1 Mo sont rejetés.