SDK mobiles (clés publiques uniquement)
Les SDK mobiles affichent le checkout hébergé SOAMTAR dans votre application et vous indiquent comment il s'est terminé. Ils ne créent jamais de paiement et ne détiennent jamais de clé secrète : ils n'acceptent que des clés publiques (pk_test_… / pk_live_…) et refusent les clés sk_….
| Plateforme | Paquet | Points d'entrée |
|---|---|---|
| iOS (Swift) | SoamtarSDK (Swift Package) 1.0.1 | SoamtarCheckoutView, SoamtarCheckoutViewController |
| Android (Kotlin) | com.soamtar:soamtar-android:1.1.0 | Soamtar.checkoutIntent(), SoamtarCheckoutActivity |
| Flutter | soamtar_sdk 1.0.1 | widget SoamtarCheckout |
| React Native | @soamtar/react-native 1.1.0 | composant <Checkout /> |
Installation
Aucun de ces paquets n'est encore publié sur un registre public : installez-les depuis le dépôt SOAMTAR en attendant.
Fonctionnement
- L'application envoie la commande à votre backend.
- Votre backend crée le paiement avec sa clé secrète (
POST/v1/payments), en indiquant votresuccess_urlet votrefailed_url, puis renvoiedata.payment_url(https://app.so-amtar.com/pay/checkout/{id}) à l'application. - L'application ouvre
payment_urlavec le SDK. Le payeur saisit son numéro Orange Money (+232suivi de 8 chiffres) et valide la demande sur son téléphone avec son code PIN Orange Money. Le PIN ne transite jamais par votre application. - La page de résultat hébergée redirige vers votre
success_urlou votrefailed_url. Le SDK s'arrête sur cette navigation et vous rappelle. - Votre backend confirme le statut final (webhook
payment.success, ouGET/v1/payments/{id}) avant de livrer la commande.
Le callback du SDK n'est pas une preuve de paiement
onSuccess / STATUS_SUCCESS signifie seulement que la WebView a atteint votre success_url. Seul un webhook vérifié ou une lecture de l'API avec votre clé secrète permet de valider une commande.
Requête côté backend (exemple)
curl -X POST https://app.so-amtar.com/v1/payments \
-H "Authorization: Bearer sk_test_xxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0c6f3a52-6a0e-4f43-9a0e-2a7f4b1d9e11" \
-d '{
"merchant_ref": "ORDER-42",
"amount": "150.00",
"currency": "SLE",
"provider": "orange_money_sl",
"success_url": "https://shop.example/orders/42/paid",
"failed_url": "https://shop.example/orders/42/failed"
}'Transmettez payment_url à l'application avec les mêmes success_url / failed_url : les SDK en ont besoin pour détecter la fin du checkout. Avec une clé sk_test_, le paiement passe par la simulation, sur le même hôte.
Utilisez deux chemins différents pour success_url et failed_url
Les quatre SDK comparent la navigation à vos URL de retour exactement sur le schéma, l'hôte et le chemin. La query string et le fragment sont ignorés, et ce n'est jamais une comparaison par préfixe. failed_url est testée en premier : si les deux URL ont le même chemin (par exemple https://shop.example/return?status=ok et https://shop.example/return?status=ko), tous les paiements sont signalés en échec. Utilisez des chemins distincts, comme /orders/42/paid et /orders/42/failed.
iOS (Swift)
Prérequis : iOS 14+, Swift 5.7+, Xcode 14+.
Installation (Swift Package Manager)
Dans Xcode : File → Add Package Dependencies…, URL https://github.com/soamtar/soamtar-ios-sdk, produit SoamtarSDK. Ou dans Package.swift :
.package(url: "https://github.com/soamtar/soamtar-ios-sdk", from: "1.0.1")Configuration (facultative)
import SoamtarSDK
Soamtar.shared.configure(publicKey: "pk_test_your_public_key")
// Anything other than pk_test_ / pk_live_ stops the app (fatalError).SwiftUI
import SwiftUI
import SoamtarSDK
struct PayButton: View {
let paymentUrl: URL // from your backend
@State private var showCheckout = false
private let returnURLs = SoamtarReturnURLs(
success: URL(string: "https://shop.example/orders/42/paid")!,
failure: URL(string: "https://shop.example/orders/42/failed")!
)
var body: some View {
Button("Pay \(SoamtarMoney.format("150.00") ?? "")") { showCheckout = true } // "Pay Le 150.00"
.sheet(isPresented: $showCheckout) {
SoamtarCheckoutView(
paymentUrl: paymentUrl,
returnURLs: returnURLs,
onSuccess: { _ in showCheckout = false /* then confirm with your backend */ },
onFailure: { _ in showCheckout = false }
)
}
}
}UIKit
let checkout = SoamtarCheckoutViewController(
paymentUrl: paymentUrl,
returnURLs: returnURLs,
onSuccess: { [weak self] _ in self?.dismiss(animated: true) },
onFailure: { [weak self] _ in self?.dismiss(animated: true) }
)
present(checkout, animated: true)API
| Symbole | Description |
|---|---|
Soamtar.shared.configure(publicKey:baseURL:) | Enregistre la clé publique. baseURL vaut par défaut https://app.so-amtar.com. |
Soamtar.validatePublicKey(_:) throws | Lève SoamtarError.invalidPublicKey si la clé ne commence pas par pk_test_ / pk_live_. |
Soamtar.shared.isCheckoutURL(_:) -> Bool | true pour une page HTTPS …/pay/checkout/{id} de l'hôte configuré. |
SoamtarReturnURLs(success:failure:) | Vos success_url / failed_url ; outcome(for:) compare exactement schéma, hôte et chemin (query ignorée, failed_url testée en premier). |
SoamtarCheckoutView(paymentUrl:returnURLs:onSuccess:onFailure:) | Checkout SwiftUI (WKWebView). Chaque callback reçoit l'URL atteinte. |
SoamtarCheckoutViewController(…) | Checkout UIKit, mêmes paramètres. |
SoamtarMoney.format(_:currency:) | Decimal ou chaîne décimale → "Le 1,250.00". |
Android (Kotlin)
Prérequis : Android API 21+, Kotlin 1.8+. Le manifeste de la bibliothèque déclare la permission INTERNET et l'activité de checkout.
Installation
dependencies {
implementation 'com.soamtar:soamtar-android:1.1.0'
}Initialisation
import com.soamtar.sdk.Soamtar
Soamtar.init("pk_test_your_public_key")
// Toute clé autre que pk_test_… / pk_live_… lève IllegalArgumentException.Ouvrir le checkout (Activity Result API)
import com.soamtar.sdk.ui.SoamtarCheckoutActivity
private val checkoutLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
when (result.data?.getStringExtra(SoamtarCheckoutActivity.RESULT_STATUS)) {
SoamtarCheckoutActivity.STATUS_SUCCESS -> showPending("Confirming your payment…")
SoamtarCheckoutActivity.STATUS_FAILED -> showError("Payment not completed.")
else -> showPending("Checking your payment…") // closed by the payer
}
refreshOrderFromBackend() // the backend status is the source of truth
}
// paymentUrl, successUrl and failedUrl come from your backend
checkoutLauncher.launch(
Soamtar.checkoutIntent(this, paymentUrl, successUrl = successUrl, failedUrl = failedUrl)
)Avec l'ancienne API onActivityResult :
Soamtar.checkout(this, paymentUrl, REQUEST_PAY, successUrl, failedUrl)Statuts renvoyés
| Constante | Signification |
|---|---|
SoamtarCheckoutActivity.STATUS_SUCCESS | La page hébergée a redirigé vers success_url |
SoamtarCheckoutActivity.STATUS_FAILED | La page hébergée a redirigé vers failed_url (échec, expiration ou annulation) |
SoamtarCheckoutActivity.STATUS_CANCELLED | Le payeur a fermé le checkout avant toute redirection |
payment_url doit être une URL HTTPS de l'hôte SOAMTAR (app.so-amtar.com). Sans successUrl / failedUrl, le payeur reste sur la page de résultat SOAMTAR et l'activité renvoie STATUS_CANCELLED quand il la ferme.
La navigation s'arrête quand une page a le même schéma, le même hôte et le même chemin que failedUrl ou successUrl (query et fragment ignorés, jamais par préfixe) ; failedUrl est testée en premier.
Afficher un montant
SoamtarMoney.formatLe(BigDecimal("1250")) // "Le 1,250.00"
SoamtarMoney.formatLe("150.5") // "Le 150.50" (null)
Soamtar.formatLe("2000000.00") // "Le 2,000,000.00"Flutter
Prérequis : Dart 3.0+, Flutter 3.10+, webview_flutter 4.0+.
Installation
dependencies:
soamtar_sdk:
git:
url: https://github.com/soamtar/soamtar_flutter_sdk
ref: v1.0.1flutter pub getAndroid : ajoutez <uses-permission android:name="android.permission.INTERNET"/> dans android/app/src/main/AndroidManifest.xml. iOS : rien à configurer.
Utilisation
import 'package:flutter/material.dart';
import 'package:soamtar_sdk/soamtar_sdk.dart';
class PaymentPage extends StatelessWidget {
const PaymentPage({super.key, required this.paymentUrl});
/// `payment_url` received from your backend.
final String paymentUrl;
@override
Widget build(BuildContext context) {
final client = SoamtarClient('pk_test_your_public_key'); // ArgumentError on sk_
if (!client.isCheckoutUrl(paymentUrl)) {
return const Center(child: Text('Invalid payment link.'));
}
return Scaffold(
appBar: AppBar(title: const Text('Pay with Orange Money')),
body: SoamtarCheckout(
paymentUrl: Uri.parse(paymentUrl),
returnUrls: SoamtarReturnUrls(
success: Uri.parse('https://shop.example/orders/42/paid'),
failure: Uri.parse('https://shop.example/orders/42/failed'),
),
onSuccess: (_) => Navigator.pop(context, true), // then confirm with your backend
onFailure: (_) => Navigator.pop(context, false),
),
);
}
}Afficher un montant :
SoamtarMoney.format('1250.00'); // "Le 1,250.00"
SoamtarMoney.formatMinorUnits(BigInt.from(15000)); // "Le 150.00"| Symbole | Remarques |
|---|---|
SoamtarClient(String publicKey, {Uri? baseUrl}) | pk_test_ / pk_live_ uniquement. Hôte par défaut https://app.so-amtar.com. isCheckoutUrl(url) vérifie HTTPS, l'hôte et /pay/checkout/…. |
SoamtarCheckout({paymentUrl, returnUrls, onSuccess, onFailure}) | Widget WebView ; déclenche un seul callback, une seule fois. |
SoamtarReturnUrls({success, failure}) | resultFor(uri) → SoamtarCheckoutResult.success / .failure / null (schéma, hôte et chemin exacts ; failure testé en premier). |
SoamtarMoney.format / formatMinorUnits | Chaîne décimale ou centimes entiers → "Le 1,250.00" (jamais de double). |
React Native
Installation
npm install @soamtar/react-native react-native-webviewUtilisation
import { Client, Checkout } from '@soamtar/react-native';
const soamtar = new Client('pk_test_...'); // erreur si ce n'est pas pk_test_… / pk_live_…
export function PayScreen({ navigation, route }) {
// payment_url returned by your backend (must be https)
const paymentUrl = soamtar.getCheckoutUrl(route.params.paymentUrl);
return (
<Checkout
paymentUrl={paymentUrl}
successUrl="https://shop.example/orders/42/paid"
failureUrl="https://shop.example/orders/42/failed"
onSuccess={() => navigation.replace('OrderStatus', { orderId: route.params.orderId })}
onFailure={() => navigation.replace('PaymentFailed')}
onError={(message) => console.warn(message)}
/>
);
}| Prop | Description |
|---|---|
paymentUrl (obligatoire) | payment_url renvoyée par POST/v1/payments. Doit être en https. |
successUrl | La success_url envoyée. L'atteindre (même schéma, hôte avec port et chemin ; query ignorée) appelle onSuccess(url) une fois. |
failureUrl | La failed_url envoyée. Testée avant successUrl. L'atteindre appelle onFailure(url) une fois. |
onResult(url) | La page de résultat SOAMTAR (/pay/result/{id}) est atteinte : le paiement a été soumis et est en cours de confirmation. |
onError(message) | URL invalide, navigation bloquée ou erreur de chargement de la WebView. |
loaderColor | Couleur du spinner (par défaut #FF7900). |
Client expose aussi isTestMode (true pour une clé pk_test_). Dans le checkout, les pages https se chargent, les URL http:, javascript: et data: sont bloquées, et les autres schémas (comme tel:) sont confiés au système d'exploitation.
Sécurité mobile
- Clés publiques uniquement : tous les SDK n'acceptent que les clés
pk_test_…/pk_live_…et refusent les clés secrètes (sk_…), qui pourraient être extraites de l'APK/IPA. - Vue web isolée : la page de paiement s'exécute dans une
WKWebView/WebView, en HTTPS uniquement. Le payeur confirme avec son code PIN Orange Money sur son propre téléphone ; le PIN ne passe jamais par votre application. - Aucun stockage : les SDK ne conservent aucune donnée sur l'appareil.
- Confirmation côté serveur : l'application n'est qu'une couche d'affichage pour une transaction créée et confirmée sur votre serveur.