Skip to content

SDK móviles (solo claves públicas) ​

Los SDK móviles muestran el checkout alojado de SOAMTAR dentro de su aplicación y le indican cómo terminó. Nunca crean pagos ni guardan una clave secreta: solo aceptan claves públicas (pk_test_… / pk_live_…) y rechazan las claves sk_….

PlataformaPaquetePuntos de entrada
iOS (Swift)SoamtarSDK (Swift Package) 1.0.1SoamtarCheckoutView, SoamtarCheckoutViewController
Android (Kotlin)com.soamtar:soamtar-android:1.1.0Soamtar.checkoutIntent(), SoamtarCheckoutActivity
Fluttersoamtar_sdk 1.0.1widget SoamtarCheckout
React Native@soamtar/react-native 1.1.0componente <Checkout />

Instalación

Ninguno de estos paquetes está publicado todavía en un registro público: instálelos desde el repositorio de SOAMTAR mientras tanto.

Funcionamiento ​

  1. La aplicación envía el pedido a su backend.
  2. Su backend crea el pago con su clave secreta (POST/v1/payments), con su success_url y su failed_url, y devuelve data.payment_url (https://app.so-amtar.com/pay/checkout/{id}) a la aplicación.
  3. La aplicación abre payment_url con el SDK. El pagador introduce su número Orange Money (+232 seguido de 8 dígitos) y aprueba la solicitud en su teléfono con su PIN de Orange Money. El PIN nunca pasa por su aplicación.
  4. La página de resultado alojada redirige a su success_url o failed_url. El SDK se detiene en esa navegación y le avisa.
  5. Su backend confirma el estado final (webhook payment.success, o GET/v1/payments/{id}) antes de entregar el pedido.

El callback del SDK no es una prueba de pago

onSuccess / STATUS_SUCCESS solo significa que la WebView llegó a su success_url. Solo un webhook verificado o una lectura de la API con su clave secreta confirma un pedido.

Solicitud del backend (ejemplo) ​

bash
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"
  }'

Envíe payment_url a la aplicación junto con los mismos success_url / failed_url: los SDK los necesitan para detectar el final del checkout. Con claves sk_test_ el pago pasa por la simulación, en el mismo host.

Use dos rutas distintas para success_url y failed_url

Los cuatro SDK comparan la navegación con sus URL de retorno exactamente por esquema, host y ruta. La query string y el fragmento se ignoran, y nunca es una comparación por prefijo. failed_url se comprueba primero: si ambas URL comparten la misma ruta (por ejemplo https://shop.example/return?status=ok y https://shop.example/return?status=ko), todos los pagos se notifican como fallidos. Use rutas distintas, como /orders/42/paid y /orders/42/failed.

iOS (Swift) ​

Requisitos: iOS 14+, Swift 5.7+, Xcode 14+.

Instalación (Swift Package Manager) ​

En Xcode: File → Add Package Dependencies…, URL https://github.com/soamtar/soamtar-ios-sdk, producto SoamtarSDK. O en Package.swift:

swift
.package(url: "https://github.com/soamtar/soamtar-ios-sdk", from: "1.0.1")

Configuración (opcional) ​

swift
import SoamtarSDK

Soamtar.shared.configure(publicKey: "pk_test_your_public_key")
// Anything other than pk_test_ / pk_live_ stops the app (fatalError).

SwiftUI ​

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

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

SímboloDescripción
Soamtar.shared.configure(publicKey:baseURL:)Guarda la clave pública. baseURL es https://app.so-amtar.com por defecto.
Soamtar.validatePublicKey(_:) throwsLanza SoamtarError.invalidPublicKey si la clave no empieza por pk_test_ / pk_live_.
Soamtar.shared.isCheckoutURL(_:) -> Booltrue para una página HTTPS …/pay/checkout/{id} del host configurado.
SoamtarReturnURLs(success:failure:)Sus success_url / failed_url; outcome(for:) compara esquema, host y ruta.
SoamtarCheckoutView(paymentUrl:returnURLs:onSuccess:onFailure:)Checkout SwiftUI (WKWebView). Cada callback recibe la URL alcanzada.
SoamtarCheckoutViewController(…)Checkout UIKit, mismos parámetros.
SoamtarMoney.format(_:currency:)Decimal o cadena decimal → "Le 1,250.00".

Android (Kotlin) ​

Requisitos: Android API 21+, Kotlin 1.8+. El manifiesto de la biblioteca declara el permiso INTERNET y la actividad de checkout.

Instalación ​

groovy
dependencies {
    implementation 'com.soamtar:soamtar-android:1.1.0'
}

Inicialización ​

kotlin
import com.soamtar.sdk.Soamtar

Soamtar.init("pk_test_your_public_key")
// Cualquier clave distinta de pk_test_… / pk_live_… lanza IllegalArgumentException.

Abrir el checkout (Activity Result API) ​

kotlin
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)
)

Con la API antigua onActivityResult:

kotlin
Soamtar.checkout(this, paymentUrl, REQUEST_PAY, successUrl, failedUrl)

Estados devueltos ​

ConstanteSignificado
SoamtarCheckoutActivity.STATUS_SUCCESSLa página alojada redirigió a success_url
SoamtarCheckoutActivity.STATUS_FAILEDLa página alojada redirigió a failed_url (fallido, expirado o cancelado)
SoamtarCheckoutActivity.STATUS_CANCELLEDEl pagador cerró el checkout antes de cualquier redirección

payment_url debe ser HTTPS en el host de SOAMTAR (app.so-amtar.com). Sin successUrl / failedUrl, el pagador se queda en la página de resultado de SOAMTAR y la actividad devuelve STATUS_CANCELLED al cerrarla.

El checkout se detiene cuando una navegación tiene el mismo esquema, host y ruta que failedUrl o successUrl (query y fragmento ignorados, nunca por prefijo); failedUrl se comprueba primero.

Mostrar un importe ​

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

Requisitos: Dart 3.0+, Flutter 3.10+, webview_flutter 4.0+.

Instalación ​

yaml
dependencies:
  soamtar_sdk:
    git:
      url: https://github.com/soamtar/soamtar_flutter_sdk
      ref: v1.0.1
bash
flutter pub get

Android: añada <uses-permission android:name="android.permission.INTERNET"/> en android/app/src/main/AndroidManifest.xml. iOS: no hay nada que configurar.

Uso ​

dart
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),
      ),
    );
  }
}

Mostrar un importe:

dart
SoamtarMoney.format('1250.00');                    // "Le 1,250.00"
SoamtarMoney.formatMinorUnits(BigInt.from(15000)); // "Le 150.00"
SímboloNotas
SoamtarClient(String publicKey, {Uri? baseUrl})Solo pk_test_ / pk_live_. Host por defecto https://app.so-amtar.com. isCheckoutUrl(url) comprueba HTTPS, host y /pay/checkout/….
SoamtarCheckout({paymentUrl, returnUrls, onSuccess, onFailure})Widget WebView; dispara un único callback, una sola vez.
SoamtarReturnUrls({success, failure})resultFor(uri) → SoamtarCheckoutResult.success / .failure / null.
SoamtarMoney.format / formatMinorUnitsCadena decimal o céntimos enteros → "Le 1,250.00" (nunca double).

React Native ​

Instalación ​

bash
npm install @soamtar/react-native react-native-webview

Uso ​

jsx
import { Client, Checkout } from '@soamtar/react-native';

const soamtar = new Client('pk_test_...'); // throws on a sk_ key

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)}
    />
  );
}
PropDescripción
paymentUrl (obligatoria)payment_url devuelta por POST/v1/payments. Debe ser https.
successUrlLa success_url enviada. Al alcanzarla se llama a onSuccess(url) una vez.
failureUrlLa failed_url enviada. Al alcanzarla se llama a onFailure(url) una vez.
onResult(url)Se alcanzó la página de resultado de SOAMTAR (/pay/result/{id}): el pago se envió y se está confirmando.
onError(message)URL no válida, navegación bloqueada o error de carga de la WebView.
loaderColorColor del indicador de carga (por defecto #FF7900).

Client también expone isTestMode (true con claves pk_test_). Dentro del checkout se cargan las páginas https, se bloquean las URL http:, javascript: y data:, y los demás esquemas (como tel:) se entregan al sistema operativo.

Seguridad móvil ​

  • Solo claves públicas: todos los SDK rechazan las claves secretas (sk_…), que podrían extraerse del APK/IPA.
  • Vista web aislada: la página de pago se ejecuta en una WKWebView / WebView, solo por HTTPS. El pagador confirma con su PIN de Orange Money en su propio teléfono; el PIN nunca pasa por su aplicación.
  • Nada almacenado: los SDK no guardan datos en el dispositivo.
  • Confirmación en el servidor: la aplicación es solo una capa de visualización de una transacción creada y confirmada en su servidor.

SOAMTAR Payment API Documentation.