Skip to main content

Platobná brána v aplikácii

Integrácia platobnej brány do mobilnej, či desktopovej aplikácie (ďalej už len ako aplikácia) je veľmi podobná ako integrácia do webového rozhrania. Aj v tomto prípade je využívaný štandardný API protokol. Jediným rozdielom je, že na webe je možné zvoliť si buď redirect na platobnú bránu, alebo zobrazenie brány v iframe, zatiaľ čo v aplikácii je potrebné platbu vždy otvoriť v komponente určenom pre zobrazenie webovej stránky, typicky WebView.

XML kód

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity">

// <TextView...>

<WebView
android:id="@+id/comgatePaymentFrame"
android:layout_width="406dp" android:layout_height="760dp"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteX="2dp"
tools:layout_editor_absoluteY="59dp" />

// <Button...>

</androidx.constraintlayout.widget.ConstraintLayout>

Pre Android, .Net a Xamarin je dostupný komponent WebView, zatiaľ čo Apple používa vo svojom Swifte WKWebView. Rozhranie WebView by malo byť zobrazené na čo najväčšej časti obrazovky tak, aby mala platobná brána dostatok priestoru pre zobrazenie a maximalizovala tak používateľský komfort.

Priebeh spracovania platby:

  1. Aplikácia kontaktuje svoj backend (server), ktorý požiada o založenie platby

  2. Server vykoná HTTP request na Comgate API (/v.1.0/create) a založí platbu. Comgate na túto žiadosť vráti odpoveď obsahujúcu informácie o platbe vo formáte query:

    • code - návratový kód odpovede
    • message - stav vytvorenia platby
    • transId - Comgate ID transakcie
    • redirect - url pre zobrazenie vo WebView
  3. Server aplikácia na požiadavku v bode 1 odpovie a vráti parametre platby získané od Comgate.

  4. Aplikácia parametre platby rozparsuje (ako parsovať query string v Kotlinu) a Javě), vytvorí cez čo najväčšiu časť displeja WebView komponent a v ňom načíta URL platobnej brány (parameter redirect).

  5. Platcovi sa zobrazí možnosť platby.

  6. Počas toho, čo sa platca pokúša zaplatiť, je nutné spustiť status watcher. Ten sa snaží čo najrýchlejšie synchronizovať stav platby zo svojho serverového backendu do aplikácie. Dostupné metódy:

    • Polling - krátke opakované requesty na server (cca každé 3 sekundy)
    • Long Polling - server drží requesty otvorené pokiaľ nie sú dáta dostupné alebo nedôjde k timeoutu. Pokiaľ ešte nie je známy status, request sa opakuje.
    • WebSockets - poskytujú full-duplex komunikačný kanál, kedy je server schopný aplikácii v reálnom čase poslať informáciu o stave platby.
    • ďalšie…
  7. Server Comgate zasiela okamžite po zmene stavu platby push notifikace na definovanú URL backendu aplikácie.

  8. Platca zaplatí objednávku.

  9. Brána zobrazená vo WebView zistí, že došlo k zaplateniu a presmeruje platcu na URL definovanú v prepojení obchodu.

  10. Skrytie a odstránenie WebView.

  11. Aplikácia kontaktuje backend pre overenie stavu platby.

  12. Aplikácia platcovi zobrazí obrazovku o stave platby.

Nastavenie návratových URL

V klientskom portáli je pre každé prepojenie obchodu potrebné definovať 4 špeciálne URL adresy, jedná sa o:

  1. url zaplatený - zaplatené platby
  2. url zrušený - zrušené a expirované platby
  3. url nevyriešený - stále prebiehajúce platby
  4. url pre odovzdanie výsledku platby - URL pre odovzdanie stavu platby (push notifikácia)

Na 1. - 3. adresu je platca presmerovaný priamo z platobnej brány. Adresy môžu byť rovnaké, môžu sa líšiť v parametroch pre stav platby. Platcu je tiež možné presmerovať na nejakú prázdnu URL serverového API. V tomto prípade je nutné zabezpečiť automatické zistenie tohto presmerovania a skrytie WebView.

  1. adresa je určená pre server–server komunikáciu. Vo chvíli, kedy je stav platby známy (zaplatená alebo zrušená/expirovaná), comgate okamžite na túto adresu pošle o tejto skutočnosti informáciu. Tú je nutné overiť sprievodným requestom späť na Comgate API pre stav platby.

Ako správne poznať moment, kedy je potrebné skryť WebView

Pri WebView je možné detekovať konkrétnu URL a na tej automaticky vykonať ďalšie akcie súvisiace so stavom platby.

Java kód

WebViewClient webViewClient = new WebViewClient() {

@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if(url.equals("your link...")){
finish();
}
}
};

Zaujímavé odkazy:

Zjednodušená Implementácia:

Ako zobraziť platbu vo WebView pre Android štúdio v Kotline

package com.comgate.webviewtest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.webkit.WebView
import java.net.URLDecoder
import java.nio.charset.StandardCharsets

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// TODO create payment
// ask mobile app cloud backend to create payment at Comgate API

// cloud return structured payment data
val cloudResponseFromComgate = "code=0&message=OK&transId=XXXX-XXXX-XXXX&redirect=https%3A%2F%2Fpay1.comgate.cz%2Finit%3Fid%3DXXXX-XXXX-XXXX";

// parse response
val paymentData = this.parseQuery(cloudResponseFromComgate);
val comgateMessage = paymentData["message"];
val paymentUrl = paymentData["redirect"];
val transId = paymentData["transId"];

if(comgateMessage == "OK" && paymentUrl?.isEmpty() == false && transId?.isEmpty() == false) {
val paymentView: WebView = findViewById(R.id.comgatePaymentFrame);
paymentView.visibility = View.VISIBLE;

// URL can change over time, so always use the URL that the Comgate API returns
paymentView.loadUrl(paymentUrl);

// Run status watcher
runStatusWatcher(transId);
} else {
// TODO: payment error
}
}

private fun runStatusWatcher(transId: String) {
// TODO: create payment status watcher
// ask mobile app cloud backend to payment status, you can use:
// - Polling: repeated request every 3 seconds
// - Long polling: server holds the request open until new data is available
// - WebSockets: provides a full-duplex communication channel
// - others
}

/**
* Parse query string to Map (Key->Value)
*/
private fun parseQuery(input: String): Map<String, String> {
val result = mutableMapOf<String, String>()
input.split("&").forEach { pair ->
val (key, value) = pair.split("=").map {
URLDecoder.decode(it.trim(), StandardCharsets.UTF_8.toString())
}
result[key] = value
}
return result
}
}