Fotoüberweisung in Banking Apps integrieren
21. Oktober 2025 by Marvin

Fotoüberweisung einfach, schnell & sicher in Banking-Apps integriert

Sie wollen direkt zum Code springen?

Hier geht's zum Code

Docutain Photo Payment SDK

Mit dem Docutain Photo Payment SDK kann die Funktion der Fotoüberweisung in nur wenigen Minuten in Banking-Apps integriert werden. Die Datenerkennung und Datenextraktion findet lokal auf dem Gerät des Nutzers statt. Keine Datenübertragung auf externe Server, wie es bei anderen Anbietern der Fall ist. Dadurch erhalten Sie:

  • 100% Datenschutz und keine Gefahren eines Serverhackings
  • 100% Geschwindigkeit - Datenerkennung in Echtzeit
  • 100% Verfügbarkeit - Die Fotoüberweisung steht 24/7 an 365 Tagen zur Verfügung
  • 100% weniger sonstige Aufwände - Z.B. zu prüfende und überwachende Anforderungen bezüglich IKT-Dienstleitungen im Rahmen von DORA

In einigen Banking-Apps wie der Sparkasse, DKB oder Consorsbank ist die Funktion der Fotoüberweisung bereits integriert. Im Gegensatz zum Docutain Photo Payment SDK hat die dort integrierte Fotoüberweisungsfunktion einen entscheidenden Nachteil: Der Anbieter arbeitet serverbasiert, d.h. beim Scan der Rechnung mit der Banking-App wird der gesamte Scan an einen externen Server gesendet, um die Rechnungsdaten dort auszulesen. Rechnungen enthalten viele sensible Inhalte und lassen Rückschlüsse auf Vorlieben, Einstellungen, Krankheiten und mehr zu. Dies sind Informationen, die nicht für die Fotoüberweisung benötigt werden. Lediglich die IBAN, der Betrag, der Empfänger und ein Verwendungszweck sind entscheidende Informationen. Dennoch werden bei dem serverbasierten Anbieter alle Inhalte der Rechnung ausgelesen und vorgehalten. Neben dem Datenschutzrisiko kann auch das Risiko eines Serverausfalls oder Serverhackings nie 100% ausgeschlossen werden.

Mit der Fotoüberweisung des Docutain SDK werden Scans von Rechnungen gemacht und die Informationen in Echtzeit auf dem Gerät des Nutzers ausgelesen – ohne externe Server, sondern 100% lokal auf dem Gerät, um maximalen Datenschutz zu garantieren.

Kunden wie die PKO Bank Polski, Polens größte Bank, die Star Finanz GmbH mit StarMoney und SFirm und die 1822direkt Bank vertrauen bereits auf das Docutain SDK.

Im folgenden Artikel erklären wir, wie Sie das Docutain Photo Payment SDK in nur wenigen Minuten in Ihre Banking-App integrieren können.

Docutain Fotoüberweisungs-SDK
Erfahren Sie mehr zum Docutain Photo Payment SDK, welches Sie in nur wenigen Minuten integrieren können.
Testen Sie das Docutain SDK, ohne auch nur eine Zeile Code zu schreiben und laden Sie sich die Showcase App herunter.

Fotoüberweisung integrieren

Die Fotoüberweisung von Docutain steht für alle relevanten Plattformen und Frameworks zur Verfügung: Android, iOS, Windows, React Native, .NET MAUI, Flutter, Capacitor (Ionic) und Cordova. In der Dokumentation wählen Sie einfach die gewünschte Plattform aus und finden dort alle was Sie wissen müssen. In nachfolgendem Beispiel gehen wir von einem Android Kotlin Projekt aus.

Hinzufügen des Docutain Photo Payment SDK

Bevor Sie Docutain's Fotoüberweisung in Ihrer App nutzen können, müssen Sie zunächst die notwendigen Pakete zu Ihrer App hinzufügen. Im Falle von Android geschieht dies klassisch über Maven Dependencies in der build.gradle:

def docutainSdkVersion = '1.8.2.0'
implementation("de.docutain:Docutain-SDK-UI:$docutainSdkVersion")
implementation("de.docutain:Docutain-SDK-DataExtraction:$docutainSdkVersion")

Initialisieren des Docutain Photo Payment SDK

Bevor Sie die Fotoüberweisung starten können, muss das Docutain SDK zunächst initialisiert werden. Zu Testzwecken können Sie den Lizenzschlüssel leer lassen. Beachten Sie jedoch, dass das SDK dann nur 60 Sekunden genutzt werden kann. Anschließend müssen Sie die App neu starten um es wieder nutzen zu können. Wenn Sie länger testen möchten, können Sie sich eine kostenlose Testlizenz erstellen.

import de.docutain.sdk.DocutainSDK;

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if(!DocutainSDK.initSDK(this.application, "")){
            //init of Docutain SDK failed, get the last error message
            val error = DocutainSDK.getLastError()
            //your logic to deactivate access to SDK functionality
        }
    }
}

Starten der Fotoüberweisung

Um die Fotoüberweisung zu starten, müssen Sie nur einen ActivityResultLauncher mit unserem vordefinierten PhotoPaymentResult starten. Übergeben Sie dem Launcher eine PhotoPaymentConfiguration. Darüber haben Sie die Möglichkeit, die Fotoüberweisung an Ihren Bedarf und das Corporate Branding anzupassen. Details dazu finden Sie in der Dokumentation. Zu Testzwecken nutzen wir die Standardeinstellungen.

import de.docutain.sdk.ui.PhotoPaymentResult

val photoPaymentResult = registerForActivityResult(PhotoPaymentResult()) { data ->
    if(data != null){
        if(data.isNotEmpty()){
            // data is a JSON string containing the information
            // extract the fields you need and pass it on to your payment sheet
        } else{
            // no data was extracted at all
            // Note: this case is only reachable, if you disabled the Empty Result Screen
        }
    } else{
        // user canceled scan process
    }
}

myButton.setOnClickListener {   
    val paymentConfig = PhotoPaymentConfiguration()
    photoPaymentResult.launch(paymentConfig)
}

Wenn Sie nun den ActivityResultLauncher starten, sollten Sie die Rechnung wie in nachfolgendem Video scannen können. Möglicherweise wird Ihnen vor dem ersten Scan ein Onboarding angezeigt. Die Anzeige der erkannten Daten wie am Ende des Video ist bis hierhin noch nicht Bestandteil des Codes. Dies finden Sie am Ende des Tutorials.

Zahlungsdaten extrahieren

Die erkannten Daten der Rechnung werden als JSON string zurückgegeben. Nachfolgend ein Beispiel der extrahierten Daten, die zu einer Bahncard Rechnung gehören:

{
    "Address":
    {
        "Name1": "DB Fernverkehr AG",
        "Name2": "",
        "Name3": "",
        "Zipcode": "60643",
        "City": "Frankfurt am Main",
        "Street": "BahnCard-Service",
        "Phone": "0302970",
        "CustomerId": "",
        "Bank": [{"BIC": "PBNKDEFFXXX",
        "IBAN": "DE02100100100152517108"}]
    },
    "Date": "2024-10-14",
    "Amount": "244.00",
    "InvoiceId": "2023174086",
    "Reference": "RNr:2023174086 vom 14.10.2024",
    "SEPACreditor": "DB Vertrieb GmbH"
}

Optional kann auch der Bezahlstatus ausgelesen werden, der angibt, ob die Rechnung bereits gezahlt wurde oder nicht. Details dazu finden Sie in der Dokumentation.

Leerer Ergebnisbildschirm

Sollten keinerlei Zahldaten extrahiert werden können, wird standardmäßig ein Bildschirm mit einigen Tipps und einer Option zum Abbrechen oder Wiederholen des Scans angezeigt. Sie können diesen Bildschirm nach Ihren Bedürfnissen anpassen oder vollständig deaktivieren. Details dazu finden Sie in der Dokumentation.

Android photo payment sdk empty result screen

Onboarding

Sie möchten Ihren App Nutzern vielleicht den Einstieg in die Fotoüberweisungsfunktion erleichtern und ihnen einige Erläuterungen & Tipps geben. Zu diesem Zweck bietet das Docutain SDK zwei Onboarding-UI-Komponenten. Sie können entweder die Standardeinstellungen verwenden oder die Inhalte nach Ihren Bedürfnissen anpassen, bzw. auch komplett deaktivieren. Details finden Sie im Dokumentatiosabschnitt: Onboarding.

Android photo payment sdk onboarding

Scan Tipps

Auch wenn das Docutain SDK in sämtlichen Szenarien die bestmöglichen Scanergebnisse liefert, möchten Sie Ihren App Nutzern möglicherweise dennoch einen Bildschirm mit Tipps zur Erzielung der besten Scanergebnisse anzeigen. Standardmäßig finden Sie dazu eine entsprechendes Toolbar Item oben rechts im Scan-Bildschirm. Die Scan-Tipps können nach Ihrem Bedarf angepasst oder deaktiviert werden. Weitere Informationen finden Sie hier: Scan Tipps.

Android photo payment sdk scan tips for best scan results

Bonus: Erkannte Zahlungsdaten anzeigen

Da jede Banking App Ihr eigenes UI für das Überweisungsformular hat, beinhaltet das Docutain SDK entsprechend keine UI Komponenten zur Anzeige der erkannten Zahldaten. Damit Sie im Sinne dieses Beispiels jedoch die erkannten Daten auch optisch anzeigen können, finden Sie nachfolgend ein beispielhaftes Layout.

Legen Sie eine neue Layout Datei an und nennen Sie sie activity_photo_payment_result. Fügen Sie den folgenden Code ein:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/materialToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ScrollView
        android:id="@+id/scroll_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/materialToolbar">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="16dp"
            android:orientation="vertical">

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/textField_payment_recipient"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:hint="Payment Recipient">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:editable="false" />

            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/textField_IBAN"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:hint="IBAN">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:editable="false" />

            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/textField_BIC"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:hint="BIC">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:editable="false" />

            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/textField_Amount"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:hint="Amount">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:editable="false" />

            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/textField_customerId"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:hint="CustomerId">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:editable="false" />

            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/textField_InvoiceId"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:hint="InvoiceId">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:editable="false" />

            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/textField_reference"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:hint="Reference">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:editable="false" />

            </com.google.android.material.textfield.TextInputLayout>

        </LinearLayout>
    </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

Erstellen Sie nun eine neue AppCompatActivity und nennen Sie sie PhotoPaymentResultActivity. Entfernen Sie den kompletten Code bis auf die oberste Zeile in der das package definiert ist und fügen Sie den nachfolgenden Code ein.

import android.os.Bundle
import com.google.android.material.textfield.TextInputLayout
import org.json.JSONObject

internal class PhotoPaymentResultActivity : AppCompatActivity() {

    private lateinit var textViewRecipient: TextInputLayout
    private lateinit var textViewCustomerID: TextInputLayout
    private lateinit var textViewIBAN: TextInputLayout
    private lateinit var textViewBIC: TextInputLayout
    private lateinit var textViewAmount: TextInputLayout
    private lateinit var textViewInvoiceId: TextInputLayout
    private lateinit var textViewReference: TextInputLayout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_photo_payment_result)
        supportToolbar()
        initTextfields()
        val data = intent.getStringExtra("data")
        loadData(data!!)
    }

    private fun initTextfields() {
        textViewRecipient = findViewById(R.id.textField_payment_recipient)
        textViewCustomerID = findViewById(R.id.textField_customerId)
        textViewIBAN = findViewById(R.id.textField_IBAN)
        textViewBIC = findViewById(R.id.textField_BIC)
        textViewAmount = findViewById(R.id.textField_Amount)
        textViewInvoiceId = findViewById(R.id.textField_InvoiceId)
        textViewReference = findViewById(R.id.textField_reference)
    }

    private fun loadData(data: String) {
        try {
            val jsonArray = JSONObject(data)
            val address = JSONObject(jsonArray.getString("Address"))
            val customerId = address.getString("CustomerId")
            val bank = address.optJSONArray("Bank")
            var sIBAN = ""
            var sBIC = ""
            if (bank != null) {
                (0 until bank.length()).forEach {
                    if (sIBAN.isNotEmpty())
                        sIBAN += "\n"
                    if (sBIC.isNotEmpty())
                        sBIC += "\n"
                    val item = bank.getJSONObject(it)
                    sIBAN += item.getString("IBAN")
                    sBIC += item.getString("BIC")
                }
            }
            val amount = jsonArray.getString("Amount")
            val invoiceId = jsonArray.getString("InvoiceId")
            val reference = jsonArray.getString("Reference")
            val recipient = jsonArray.optString("SEPACreditor")
            if (recipient.isNotEmpty()) {
                textViewRecipient.editText!!.setText(recipient)
            }
            if (customerId.isNotEmpty()) {
                textViewCustomerID.editText!!.setText(customerId)
            }
            if (sIBAN.isNotEmpty()) {
                val regex = ".{4}".toRegex()
                textViewIBAN.editText!!.setText(sIBAN.replace(regex, "\$0 "))
            }
            if (sBIC.isNotEmpty()) {
                textViewBIC.editText!!.setText(sBIC)
            }
            if (amount.isNotEmpty() && (amount != "0.00")) {
                textViewAmount.editText!!.setText(amount)
            }
            if (invoiceId.isNotEmpty()) {
                textViewInvoiceId.editText!!.setText(invoiceId)
            }
            if (reference.isNotEmpty()) {
                textViewReference.editText!!.setText(reference)
            }
        } catch (ex: Exception) {
            // TODO: error handling
        }
    }
}

Starten Sie nun die PhotoPaymentResultActivity, nachdem die Fotoüberweisung erfolgreich war. Passen Sie dazu den Code zum Starten der Fotoüberweisung wie folgt an:

private val photoPaymentResult = registerForActivityResult(PhotoPaymentResult()) { data ->
    if (data != null) {
        val intent = Intent(this@MainActivity, PhotoPaymentResultActivity::class.java).apply {
            putExtras(bundleOf("data" to data))
        }
        startActivity(intent)
    }
}

myButton.setOnClickListener {   
    val paymentConfig = PhotoPaymentConfiguration()
    photoPaymentResult.launch(paymentConfig)
}
             

Docutain Scanner & Datenextraktions-SDK
Erfahren Sie mehr zum Docutain SDK. Neben dem Photo Payment SDK gibt es auch ein Barcode Scanner SDK und ein Document Scanner SDK. Alle SDKs sind für alle gängigen Plattformen & Frameworks verfügbar.

Testen Sie das Docutain SDK, ohne auch nur eine Zeile Code zu schreiben und laden Sie sich die Showcase App herunter.

Contact us and receive your quote

Our pricing is tailored to your use case. Let our colleague Harry Beck know how we can help and receive your quote.