Przejdź do treści
Ostatnia aktualizacja

SimPay Checkout Android SDK wyświetla arkusz płatności w aplikacji. Pobiera kanały, tworzy transakcję, obsługuje BLIK Level 0 i kanały z przekierowaniem w Custom Tab, a następnie sprawdza status przez API. To biblioteka interfejsu Android, nie Java SDK do integracji backendowej.

Kod i aplikacja demonstracyjna są w repozytorium simpay-android. Ten SDK nie udostępnia obsługi OneClick, płatności cyklicznych BLIK, zwrotów, SMS ani DirectBilling.

Przygotowanie

  1. W panelu akceptanta otwórz ustawienia usługi i włącz obsługę w zakładce "Mobilne SDK".
  2. Zapisz krótki, 8-znakowy identyfikator usługi, np. a1b2c3d4. To pierwszy segment UUID usługi, nie cały UUID. Backend i aplikacja muszą używać tej samej wartości.
  3. Zarejestruj schemat powrotu, np. pl.example.shop. Musi być identyczny w panelu, manifeście Androida i TransactionReturns.
  4. Przygotuj backend do wydawania sesji i weryfikacji IPN. Klucz API akceptanta pozostaje wyłącznie na backendzie. Nie umieszczaj go w APK, zasobach ani BuildConfig.

SDK wymaga minSdk 21, compileSdk co najmniej 34 i zgodności kodu Java 11. Wersja JDK uruchamiająca Gradle zależy od używanego Android Gradle Plugin, nie od ustawienia zgodności Java 11. Hostem arkusza musi być FragmentActivity lub jej podklasa, np. AppCompatActivity.

Zależność Gradle

W settings.gradle.kts aplikacji:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

W build.gradle.kts modułu aplikacji:

android {
    compileSdk = 34
    defaultConfig { minSdk = 21 }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
}

dependencies {
    implementation("pl.simpay:checkout-android:1.0.0")
}

Wersja 1.0.0 jest dostępna w Maven Central. Pakiet zawiera AAR, źródła i dokumentację Javadoc. Nie wymaga dodatkowego repozytorium Maven poza mavenCentral().

Sesja z backendu

Aplikacja prosi Twój uwierzytelniony backend o sesję. Backend wywołuje POST https://api.simpay.pl/payment/{serviceId}/mobile-sdk/session z kluczem akceptanta w Authorization: Bearer <klucz API>. Zwraca aplikacji krótko ważny token, zwykle około 15 minut, jego termin ważności, jeśli dostępny, oraz publiczny IP płatnika zaobserwowany przy żądaniu. Za proxy korzystaj wyłącznie z zaufanej konfiguracji przekazywania IP, nie z dowolnego nagłówka od klienta.

MyBackendClient i BackendSessionResponse poniżej to abstrakcje dostarczane przez integratora, nie klasy SDK. Zaimplementuj klienta HTTP swojego backendu. Nie wywołuj endpointu sesji z kluczem akceptanta z aplikacji.

import pl.simpay.android.checkout.mobile.MobileSdkSession
import pl.simpay.android.checkout.mobile.MobileSessionProvider

data class BackendSessionResponse(
    val token: String,
    val expiresAt: String?,
    val payerIp: String
)

interface MyBackendClient {
    suspend fun createPaymentSession(): BackendSessionResponse
}

class BackendSessionProvider(private val client: MyBackendClient) : MobileSessionProvider {
    override suspend fun provideSession(): MobileSdkSession {
        val response = client.createPaymentSession()
        return MobileSdkSession(
            token = response.token,
            expiresAt = response.expiresAt,
            payerIp = response.payerIp
        )
    }
}

Przekaż cały token z prefiksem mobsdk_. SDK dodaje Bearer i używa stałego hosta https://api.simpay.pl. Provider powinien zwracać aktualną sesję, także przy ponownym wywołaniu. Nie loguj tokenów. payerIp to publiczny adres płatnika, nie adres serwera backendu ani lokalnego interfejsu telefonu. payerIpOverride w zamówieniu jest tylko wartością zapasową, gdy sesja nie zawiera IP, i podlega tym samym zasadom.

Zamówienie i otwarcie arkusza

Poniższa funkcja przyjmuje klienta Twojego backendu. Wywołaj ją z aktywnej FragmentActivity, np. po naciśnięciu przycisku płatności. onResult aktualizuje interfejs aplikacji, nie realizuje zamówienia.

import androidx.fragment.app.FragmentActivity
import java.math.BigDecimal
import pl.simpay.android.checkout.mobile.CheckoutResult
import pl.simpay.android.checkout.mobile.SimPayMobileCheckout
import pl.simpay.android.checkout.mobile.SimPayMobileConfiguration
import pl.simpay.android.checkout.mobile.TransactionOrder
import pl.simpay.android.checkout.mobile.TransactionReturns

fun pay(
    activity: FragmentActivity,
    client: MyBackendClient,
    onResult: (CheckoutResult) -> Unit
) {
    val checkout = SimPayMobileCheckout(
        configuration = SimPayMobileConfiguration(
            serviceId = "a1b2c3d4",
            merchantDisplayName = "Example Shop"
        ),
        sessionProvider = BackendSessionProvider(client)
    )
    val order = TransactionOrder(
        amount = BigDecimal("49.99"),
        currency = "PLN",
        description = "Order 2451",
        control = "order-2451",
        customerEmail = "buyer@example.com",
        customerCountryCode = "PL",
        returns = TransactionReturns(
            success = "pl.example.shop://payment-return?result=success",
            failure = "pl.example.shop://payment-return?result=failure"
        )
    )
    checkout.present(activity, order, onResult)
}

Kwota musi wynosić co najmniej 1. Używaj BigDecimal z tekstu, nie z Double. Opis ma limit 64 znaków, control 255. control powinno wskazywać zamówienie zapisane w Twoim backendzie. E-mail jest wymagany. Kraj to dwie wielkie litery ISO 3166-1 alpha-2 zgodne z walutą. SDK może ustalić kraj z waluty, ale dla niejednoznacznej waluty, np. EUR, podaj go jawnie.

Powrót z przeglądarki

Dodaj do <application> w AndroidManifest.xml. Zastąp nazwy klas i schemat wartościami swojej aplikacji. CheckoutActivity musi dziedziczyć po FragmentActivity.

<activity
    android:name=".CheckoutActivity"
    android:exported="false"
    android:launchMode="singleTop" />
<activity
    android:name=".PaymentReturnActivity"
    android:exported="true"
    android:theme="@android:style/Theme.Translucent.NoTitleBar">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="pl.example.shop" android:host="payment-return" />
    </intent-filter>
</activity>
import android.app.Activity
import android.content.Intent
import android.os.Bundle

class PaymentReturnActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        startActivity(
            Intent(this, CheckoutActivity::class.java)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
        )
        finish()
    }
}

Nie traktuj parametru result=success jako potwierdzenia płatności. Schematy niestandardowe nie gwarantują wyłączności aplikacji. Powrót ma jedynie przywrócić ekran, a SDK ponownie sprawdza status transakcji. Nie zakładaj odtworzenia callbacku i całego checkoutu po zabiciu procesu. Po ponownym uruchomieniu pobierz stan zamówienia z backendu zamiast automatycznie tworzyć nową płatność.

Wynik i realizacja zamówienia

CheckoutResultDziałanie aplikacji
SucceededSDK odczytał transaction_paid. Pokaż wynik i pobierz stan zamówienia z backendu.
PendingWynik pozostaje otwarty, np. trwa weryfikacja lub skończył się czas odpytywania. Nie oznaczaj jako opłacone ani definitywnie odrzucone. transactionId może być null.
FailedObsłuż error; message zawiera szczegóły techniczne. transactionId i rawStatus mogą być null. Sprawdź stan zamówienia przed ponowieniem.
CancelledUżytkownik zamknął arkusz. Nie jest to dowód anulowania zamówienia na backendzie.

Żaden wynik SDK nie uprawnia do wydania towaru. Zmodyfikowana aplikacja może zmienić kwotę, walutę i control, a token sesji nie wiąże tych wartości z zamówieniem.

Backend musi zweryfikować sygnaturę IPN i obsłużyć zmianę statusu transakcji. Przed realizacją porównaj status opłacenia, identyfikator usługi, control i powiązanie transakcji z zamówieniem, kwotę oraz walutę z własnym rekordem zamówienia. Obsłuż powtórzone IPN idempotentnie. Przeglądarka i aplikacja nie są źródłem potwierdzenia. Zobacz wprowadzenie do IPN.

Rozwiązywanie problemów

ObjawCo sprawdzić
Gradle nie znajduje 1.0.0Sprawdź współrzędne pl.simpay:checkout-android:1.0.0 i obecność mavenCentral() w repozytoriach.
Błąd metadanych AARcompileSdk musi wynosić co najmniej 34.
present nie przyjmuje ActivityWymagana jest FragmentActivity, nie sama ComponentActivity.
SessionFailed lub UnauthorizedWłączenie Mobilnego SDK, krótki service ID, zgodność usługi i klucza na backendzie, ważność tokenu i pełny prefiks mobsdk_.
MissingPayerIpBackend musi zwrócić publiczny IP płatnika w sesji.
MissingCountryCodePodaj customerCountryCode, zwłaszcza dla EUR.
Płatnik zostaje w przeglądarcePorównaj schemat w panelu, manifeście i obu adresach TransactionReturns, a także host filtra.
RateLimitedOdczekaj przed ponowieniem, nie pobieraj sesji w pętli.
BrowserUnavailableNa urządzeniu musi być przeglądarka obsługująca przekierowanie.
Indeterminate, timeout lub PendingSprawdź stan na backendzie. Nie ponawiaj automatycznie operacji, która mogła już utworzyć transakcję.

Do diagnostyki używaj CheckoutResult.Failed.error i message, bez tokenów i danych płatnika w logach. Demo z fałszywym API sprawdza interfejs, nie rzeczywistą płatność. Przed wdrożeniem sprawdź na przygotowanej usłudze BLIK, przekierowanie i powrót, przerwanie płatności, opóźnione IPN i zgodność danych zamówienia na backendzie.