Skip to content

SDK móveis (apenas chaves públicas) ​

Os SDK móveis mostram o checkout alojado da SOAMTAR dentro da sua aplicação e indicam-lhe como terminou. Nunca criam pagamentos nem guardam uma chave secreta: só aceitam chaves públicas (pk_test_… / pk_live_…) e recusam chaves sk_….

PlataformaPacotePontos 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 />

Instalação

Nenhum destes pacotes está ainda publicado num registo público: instale-os a partir do repositório SOAMTAR entretanto.

Funcionamento ​

  1. A aplicação envia a encomenda para o seu backend.
  2. O seu backend cria o pagamento com a sua chave secreta (POST/v1/payments), com o seu success_url e failed_url, e devolve data.payment_url (https://app.so-amtar.com/pay/checkout/{id}) à aplicação.
  3. A aplicação abre payment_url com o SDK. O pagador introduz o seu número Orange Money (+232 seguido de 8 dígitos) e aprova o pedido no telemóvel com o seu PIN Orange Money. O PIN nunca passa pela sua aplicação.
  4. A página de resultado alojada redireciona para o seu success_url ou failed_url. O SDK para nessa navegação e avisa-o.
  5. O seu backend confirma o estado final (webhook payment.success, ou GET/v1/payments/{id}) antes de entregar a encomenda.

O callback do SDK não é prova de pagamento

onSuccess / STATUS_SUCCESS significa apenas que a WebView chegou ao seu success_url. Só um webhook verificado ou uma leitura da API com a sua chave secreta confirma uma encomenda.

Pedido do backend (exemplo) ​

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

Envie payment_url para a aplicação juntamente com os mesmos success_url / failed_url: os SDK precisam deles para detetar o fim do checkout. Com chaves sk_test_ o pagamento passa pela simulação, no mesmo host.

Use dois caminhos diferentes para success_url e failed_url

Os quatro SDK comparam a navegação com os seus URL de retorno exatamente por esquema, host e caminho. A query string e o fragmento são ignorados, e nunca é uma comparação por prefixo. failed_url é verificado primeiro: se os dois URL tiverem o mesmo caminho (por exemplo https://shop.example/return?status=ok e https://shop.example/return?status=ko), todos os pagamentos são reportados como falhados. Use caminhos distintos, como /orders/42/paid e /orders/42/failed.

iOS (Swift) ​

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

Instalação (Swift Package Manager) ​

No Xcode: File → Add Package Dependencies…, URL https://github.com/soamtar/soamtar-ios-sdk, produto SoamtarSDK. Ou em Package.swift:

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

Configuração (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ímboloDescrição
Soamtar.shared.configure(publicKey:baseURL:)Guarda a chave pública. baseURL é https://app.so-amtar.com por omissão.
Soamtar.validatePublicKey(_:) throwsLança SoamtarError.invalidPublicKey se a chave não começar por pk_test_ / pk_live_.
Soamtar.shared.isCheckoutURL(_:) -> Booltrue para uma página HTTPS …/pay/checkout/{id} do host configurado.
SoamtarReturnURLs(success:failure:)Os seus success_url / failed_url; outcome(for:) compara esquema, host e caminho.
SoamtarCheckoutView(paymentUrl:returnURLs:onSuccess:onFailure:)Checkout SwiftUI (WKWebView). Cada callback recebe o URL alcançado.
SoamtarCheckoutViewController(…)Checkout UIKit, mesmos parâmetros.
SoamtarMoney.format(_:currency:)Decimal ou string decimal → "Le 1,250.00".

Android (Kotlin) ​

Requisitos: Android API 21+, Kotlin 1.8+. O manifesto da biblioteca declara a permissão INTERNET e a atividade de checkout.

Instalação ​

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

Inicialização ​

kotlin
import com.soamtar.sdk.Soamtar

Soamtar.init("pk_test_your_public_key")
// Qualquer chave que não seja pk_test_… / pk_live_… lança IllegalArgumentException.

Abrir o 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)
)

Com a API antiga onActivityResult:

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

Estados devolvidos ​

ConstanteSignificado
SoamtarCheckoutActivity.STATUS_SUCCESSA página alojada redirecionou para success_url
SoamtarCheckoutActivity.STATUS_FAILEDA página alojada redirecionou para failed_url (falhado, expirado ou cancelado)
SoamtarCheckoutActivity.STATUS_CANCELLEDO pagador fechou o checkout antes de qualquer redirecionamento

payment_url tem de ser HTTPS no host SOAMTAR (app.so-amtar.com). Sem successUrl / failedUrl, o pagador fica na página de resultado SOAMTAR e a atividade devolve STATUS_CANCELLED quando a fecha.

O checkout termina quando uma navegação tem o mesmo esquema, host e caminho que failedUrl ou successUrl (query e fragmento ignorados, nunca por prefixo); failedUrl é verificado primeiro.

Mostrar um montante ​

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

Instalação ​

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

Android: adicione <uses-permission android:name="android.permission.INTERNET"/> em android/app/src/main/AndroidManifest.xml. iOS: nada a configurar.

Utilização ​

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 um montante:

dart
SoamtarMoney.format('1250.00');                    // "Le 1,250.00"
SoamtarMoney.formatMinorUnits(BigInt.from(15000)); // "Le 150.00"
SímboloNotas
SoamtarClient(String publicKey, {Uri? baseUrl})Apenas pk_test_ / pk_live_. Host por omissão https://app.so-amtar.com. isCheckoutUrl(url) verifica HTTPS, host e /pay/checkout/….
SoamtarCheckout({paymentUrl, returnUrls, onSuccess, onFailure})Widget WebView; dispara um único callback, uma só vez.
SoamtarReturnUrls({success, failure})resultFor(uri) → SoamtarCheckoutResult.success / .failure / null.
SoamtarMoney.format / formatMinorUnitsString decimal ou cêntimos inteiros → "Le 1,250.00" (nunca double).

React Native ​

Instalação ​

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

Utilização ​

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)}
    />
  );
}
PropDescrição
paymentUrl (obrigatória)payment_url devolvido por POST/v1/payments. Tem de ser https.
successUrlO success_url enviado. Ao alcançá-lo chama onSuccess(url) uma vez.
failureUrlO failed_url enviado. Ao alcançá-lo chama onFailure(url) uma vez.
onResult(url)A página de resultado SOAMTAR (/pay/result/{id}) foi alcançada: o pagamento foi submetido e está a ser confirmado.
onError(message)URL inválido, navegação bloqueada ou erro de carregamento da WebView.
loaderColorCor do indicador de carregamento (por omissão #FF7900).

Client expõe também isTestMode (true com chaves pk_test_). Dentro do checkout, as páginas https carregam, os URL http:, javascript: e data: são bloqueados, e os outros esquemas (como tel:) são entregues ao sistema operativo.

Segurança móvel ​

  • Apenas chaves públicas: todos os SDK recusam chaves secretas (sk_…), que poderiam ser extraídas do APK/IPA.
  • Vista web isolada: a página de pagamento corre numa WKWebView / WebView, apenas em HTTPS. O pagador confirma com o seu PIN Orange Money no próprio telemóvel; o PIN nunca passa pela sua aplicação.
  • Nada guardado: os SDK não guardam dados no dispositivo.
  • Confirmação no servidor: a aplicação é apenas uma camada de apresentação de uma transação criada e confirmada no seu servidor.

SOAMTAR Payment API Documentation.