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_….
| Plataforma | Pacote | Pontos de entrada |
|---|---|---|
| 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 | componente <Checkout /> |
Instalação
Nenhum destes pacotes está ainda publicado num registo público: instale-os a partir do repositório SOAMTAR entretanto.
Funcionamento
- A aplicação envia a encomenda para o seu backend.
- O seu backend cria o pagamento com a sua chave secreta (
POST/v1/payments), com o seusuccess_urlefailed_url, e devolvedata.payment_url(https://app.so-amtar.com/pay/checkout/{id}) à aplicação. - A aplicação abre
payment_urlcom o SDK. O pagador introduz o seu número Orange Money (+232seguido 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. - A página de resultado alojada redireciona para o seu
success_urloufailed_url. O SDK para nessa navegação e avisa-o. - O seu backend confirma o estado final (webhook
payment.success, ouGET/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)
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:
.package(url: "https://github.com/soamtar/soamtar-ios-sdk", from: "1.0.1")Configuração (opcional)
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
| Símbolo | Descrição |
|---|---|
Soamtar.shared.configure(publicKey:baseURL:) | Guarda a chave pública. baseURL é https://app.so-amtar.com por omissão. |
Soamtar.validatePublicKey(_:) throws | Lança SoamtarError.invalidPublicKey se a chave não começar por pk_test_ / pk_live_. |
Soamtar.shared.isCheckoutURL(_:) -> Bool | true 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
dependencies {
implementation 'com.soamtar:soamtar-android:1.1.0'
}Inicialização
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)
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:
Soamtar.checkout(this, paymentUrl, REQUEST_PAY, successUrl, failedUrl)Estados devolvidos
| Constante | Significado |
|---|---|
SoamtarCheckoutActivity.STATUS_SUCCESS | A página alojada redirecionou para success_url |
SoamtarCheckoutActivity.STATUS_FAILED | A página alojada redirecionou para failed_url (falhado, expirado ou cancelado) |
SoamtarCheckoutActivity.STATUS_CANCELLED | O 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
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
dependencies:
soamtar_sdk:
git:
url: https://github.com/soamtar/soamtar_flutter_sdk
ref: v1.0.1flutter pub getAndroid: adicione <uses-permission android:name="android.permission.INTERNET"/> em android/app/src/main/AndroidManifest.xml. iOS: nada a configurar.
Utilização
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:
SoamtarMoney.format('1250.00'); // "Le 1,250.00"
SoamtarMoney.formatMinorUnits(BigInt.from(15000)); // "Le 150.00"| Símbolo | Notas |
|---|---|
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 / formatMinorUnits | String decimal ou cêntimos inteiros → "Le 1,250.00" (nunca double). |
React Native
Instalação
npm install @soamtar/react-native react-native-webviewUtilização
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)}
/>
);
}| Prop | Descrição |
|---|---|
paymentUrl (obrigatória) | payment_url devolvido por POST/v1/payments. Tem de ser https. |
successUrl | O success_url enviado. Ao alcançá-lo chama onSuccess(url) uma vez. |
failureUrl | O 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. |
loaderColor | Cor 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.