Skip to content

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, fournisseur orange_money_sl
  • Montants en chaînes à 2 décimales ("150.00"), Integer ou BigDecimal, jamais Float
  • En-tête Idempotency-Key ajouté 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 ​

ruby
# 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 ​

ruby
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(...)
OptionDéfautRôle
base_url:SOAMTAR_API_URL, puis SOAMTAR_BASE_URL (ancien nom, déprécié), sinon https://app.so-amtar.com/v1Racine de l'API
timeout:15Délai d'attente en secondes
sign_requests:falseSigne chaque POST (voir Signature des requêtes)
ruby
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 ​

ruby
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 :

ruby
redirect_to payment.payment_url, allow_other_host: true

Fournissez votre propre Idempotency-Key pour pouvoir relancer une requête sans risque :

ruby
client.payments.create(params, idempotency_key: 'order-1042-attempt-1')

Autres appels :

ruby
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.retrieve

Envoyer un paiement (payout) ​

ruby
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.

ruby
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 ​

ruby
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
end

Sinatra ​

ruby
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
end

Un 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 ​

ruby
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.test

Alias dépréciés, toujours fonctionnels : client.webhooks.test (utilisez send_test), client.status.retrieve (utilisez client.payments.status).

Erreurs ​

ruby
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
end

Sécurité ​

  • Comparaison des signatures en temps constant.
  • La clé secrète est masquée dans inspect, to_s et marshal_dump.
  • Les webhooks de plus de 1 Mo sont rejetés.

SOAMTAR Payment API Documentation.