sprain / php-swiss-qr-bill

A PHP library to create Swiss QR bills | QR-Rechnung in PHP erstellen
MIT License
274 stars 79 forks source link

Error handling in case of incorrect entry of QR-IBAN or CLASSIC-IBAN #182

Closed merlincom closed 2 years ago

merlincom commented 2 years ago

First of all thank you for the great work!

When specifying a QR-IBAN in the CLASSIC-IBAN example, processing terminates without error message. [1] Is there any way to check which type of IBAN (QR or Classic) it is and then make an if-else line break? [2] How can I provoke an error message in $referenceNumber = QrBill\Reference\QrPaymentReferenceGenerator::generate(); I have not found a solution with try { } so far.

Bei der Angabe eines QR-IBAN im CLASSIC-IBAN Beispiel bricht die Verarbeitung ohne Fehlermeldung ab. [1] Kann mann irgendow überprüfen, ob um welchen Typ von IBAN (QR oder Classic) es sich handelt und dann eine if-else Verzweigung machen? [2] Wie kann ich eine Fehlermeldung im Bereich $referenceNumber = QrBill\Reference\QrPaymentReferenceGenerator::generate(); provozieren. Ich habe mit try{ } bisher keine Lösung gefunden.

sprain commented 2 years ago

~Could you provide a code example to reproduce this bug?~ ~If I understand your description correctly, this should not happen and there is even a test to cover this case.~

Sorry, I misunderstood. New information below.

sprain commented 2 years ago

Is there any way to check which type of IBAN (QR or Classic) it is and then make an if-else line break?

$creditorInformation = QrBill\DataGroup\Element\CreditorInformation::create($iban);
if ($creditorInformation->containsQrIban()) {
    // qr iban
} else {
    // classic iban
}
sprain commented 2 years ago

How can I provoke an error message in $referenceNumber = QrBill\Reference\QrPaymentReferenceGenerator::generate(); I have not found a solution with try { } so far.

There is an exception thrown if the provided data cannot generate a valid reference number. Otherwise it should work.

merlincom commented 2 years ago

Maybe there is some specific local error in you setup.

PHP 8.1

`Deprecated: Return type of Symfony\Component\Validator\ConstraintViolationList::getIterator() should either be compatible with IteratorAggregate::getIterator(): Traversable, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/merlinco/composer.sprain.swiss-qr-bill/vendor/symfony/validator/ConstraintViolationList.php on line 113

Deprecated: Return type of Symfony\Component\Validator\ConstraintViolationList::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/merlinco/composer.sprain.swiss-qr-bill/vendor/symfony/validator/ConstraintViolationList.php on line 121

Deprecated: Return type of Symfony\Component\Validator\ConstraintViolationList::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/merlinco/composer.sprain.swiss-qr-bill/vendor/symfony/validator/ConstraintViolationList.php on line 129

Deprecated: Return type of Symfony\Component\Validator\ConstraintViolationList::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/merlinco/composer.sprain.swiss-qr-bill/vendor/symfony/validator/ConstraintViolationList.php on line 137

Deprecated: Return type of Symfony\Component\Validator\ConstraintViolationList::offsetSet($offset, $violation) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/merlinco/composer.sprain.swiss-qr-bill/vendor/symfony/validator/ConstraintViolationList.php on line 145

Deprecated: Return type of Symfony\Component\Validator\ConstraintViolationList::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/merlinco/composer.sprain.swiss-qr-bill/vendor/symfony/validator/ConstraintViolationList.php on line 157

Fatal error: Uncaught Sprain\SwissQrBill\Exception\InvalidQrBillDataException: The provided data is not valid to generate a qr code. Use getViolations() to find details. in /home/merlinco/composer.sprain.swiss-qr-bill/src/QrBill.php:168 Stack trace: #0 /home/merlinco/composer.sprain.swiss-qr-bill/src/PaymentPart/Output/AbstractOutput.php(167): Sprain\SwissQrBill\QrBill->getQrCode() #1 /home/merlinco/composer.sprain.swiss-qr-bill/src/PaymentPart/Output/FpdfOutput/FpdfOutput.php(97): Sprain\SwissQrBill\PaymentPart\Output\AbstractOutput->getQrCode() #2 /home/merlinco/composer.sprain.swiss-qr-bill/src/PaymentPart/Output/FpdfOutput/FpdfOutput.php(77): Sprain\SwissQrBill\PaymentPart\Output\FpdfOutput\FpdfOutput->addSwissQrCodeImage() #3 /home/merlinco/www/qr-rechnung.vbbrb.ch/web/qr-code/FpdfOutput/fpdf-example-full-form-QR-IBAN.php(132): Sprain\SwissQrBill\PaymentPart\Output\FpdfOutput\FpdfOutput->getPaymentPart() #4 {main} thrown in /home/merlinco/composer.sprain.swiss-qr-bill/src/QrBill.php on line 168`

MyExample without PHP error reporting https://qr-rechnung.vbbrb.ch/qr-code/FpdfOutput/fpdf-example-full-form-QR-IBAN.php

MyExample with php-error-Reporting https://qr-rechnung.vbbrb.ch/qr-code/FpdfOutput/fpdf-example-full-form-QR-IBAN_error_report.php

merlincom commented 2 years ago

`<?php declare(strict_types=1); echo '<!DOCTYPE html>' ; $timestamp = time(); // https://github.com/sprain/php-swiss-qr-bill

use Sprain\SwissQrBill as QrBill;
$fakeQrIban = 'CH44 3199 9123 0008 8901 2' ;

$form = false ;

if ( isset($_POST) and !empty($_POST) ) {
    // print_r( $_POST ) ;
    // print_r( strlen(trim( $_POST['qrForm-kreditor-fullname'] ) ) ) ;
    // print_r( str_replace( " ", "", $_POST['qrForm-kreditor-iban'] ) ) ;

    //$_POST['qrForm-kreditor-fullname'] = substr( ( $_POST['qrForm-kreditor-fullname'] ), 0, 72 ) ;

    /*=======================================================================================*/

    // require __DIR__ . '/../../vendor/autoload.php';
    // Pfad angepasst auf Verzeichnis ausserhalb Web
    require __DIR__ . './../../../../../composer.sprain.swiss-qr-bill/vendor/autoload.php';

    // 1. Let's load the base example to define the qr bill contents
    // require __DIR__ . '/../example.php';
    // Create a new instance of QrBill, containing default headers with fixed values
    $qrBill = QrBill\QrBill::create();

    // Add creditor information
    // Who will receive the payment and to which bank account?
    $qrBill->setCreditor(
        QrBill\DataGroup\Element\CombinedAddress::create(
            substr( trim( $_POST['qrForm-kreditor-fullname'] ), 0, 72 ),
            trim( $_POST['qrForm-kreditor-strasse'].' '.$_POST['qrForm-kreditor-hausnummer'] ),
            trim( $_POST['qrForm-kreditor-plz'].' '.$_POST['qrForm-kreditor-ort'] ),
            trim( $_POST['qrForm-kreditor-land'] )
        )
    );

    $qrBill->setCreditorInformation(
            QrBill\DataGroup\Element\CreditorInformation::create(
            trim( str_replace( ' ', '', $_POST['qrForm-kreditor-iban']) ) // This is a special QR-IBAN. Classic IBANs will not be valid here. // CH9780808006069509421
        )
    );

    // Add debtor information
    // Who has to pay the invoice? This part is optional.
    //
    // Notice how you can use two different styles of addresses: CombinedAddress or StructuredAddress.
    // They are interchangeable for creditor as well as debtor.
    $qrBill->setUltimateDebtor(
        QrBill\DataGroup\Element\StructuredAddress::createWithStreet(
            trim( $_POST['qrForm-debitor-vorname'].' '.$_POST['qrForm-debitor-nachname'] ),
            trim( $_POST['qrForm-debitor-strasse'] ),
            trim( $_POST['qrForm-debitor-hausnummer'] ),
            trim( $_POST['qrForm-debitor-plz'] ),
            trim( $_POST['qrForm-debitor-ort'] ),
            trim( $_POST['qrForm-debitor-land'] )
        )
    );

    // Add payment amount information
    // What amount is to be paid?
    $qrBill->setPaymentAmountInformation(
        QrBill\DataGroup\Element\PaymentAmountInformation::create(
            trim( $_POST['qrForm-kreditor-currency'] ),
            floatval( $_POST['qrForm-kreditor-amount'] )
        )
    );

    // Add payment reference
    // This is what you will need to identify incoming payments.
    $referenceNumber = QrBill\Reference\QrPaymentReferenceGenerator::generate(
        preg_replace('/[^0-9]/', '', $_POST['qrForm-kreditor-besr-id'] ),  // You receive this number from your bank (BESR-ID). Unless your bank is PostFinance, in that case use NULL.
        preg_replace('/[^0-9]/', '', $_POST['qrForm-kreditor-referenz'] ) // A number to match the payment with your internal data, e.g. an invoice number
    );

    $qrBill->setPaymentReference(
        QrBill\DataGroup\Element\PaymentReference::create(
            QrBill\DataGroup\Element\PaymentReference::TYPE_QR,
            $referenceNumber
        )
    );

    // Optionally, add some human-readable information about what the bill is for.
    $qrBill->setAdditionalInformation(
        QrBill\DataGroup\Element\AdditionalInformation::create(
            trim( $_POST['qrForm-kreditor-zusatzinfo'] )
        )
    );

    // Now get the QR code image and save it as a file.

    $filename = basename( $_SERVER['PHP_SELF'] ) ;
    //echo '<div>$filename = '.$filename.'</div>' ;
    $fpdfOutputName = basename( basename( $_SERVER['PHP_SELF'], '.php' ) ) ;
    //echo '<div>$fpdfOutputName = '.$fpdfOutputName.'</div>' ;

    // 2. Create an FPDF instance (or use an existing one from your project)
    $fpdf = new \Fpdf\Fpdf('P', 'mm', 'A4');
    $fpdf->AddPage();

    $fpdf->SetFont('Arial','B',16);
    $fpdf->Cell(40,10,'Achtung! Fake QR-IBAN: CH4431999123000889012!',0,1);

    //$fpdf->Text(40,10,'Achtung! Classic QR-IBAN: CH4431999123000889012!');

    for( $i=1;$i<=8;$i++ ) {
        $fpdf->Cell(0,10,'Printing line number '.$i,0,1);
    }

    // 3. Create a full payment part for FPDF
    $output = new QrBill\PaymentPart\Output\FpdfOutput\FpdfOutput($qrBill, 'de', $fpdf);
    $output
            ->setPrintable(false)
            ->getPaymentPart();

    // 4. For demo purposes, let's save the generated example in a file

    $filename = basename( $_SERVER['PHP_SELF'] ) ;
    //echo '<div>$filename = '.$filename.'</div>' ;
    $fpdfOutputName = basename( basename( $_SERVER['PHP_SELF'], '.php' ) ) ;
    //echo '<div>$fpdfOutputName = '.$fpdfOutputName.'</div>' ;

    $examplePath = __DIR__ . '/'.$timestamp.'_'.$fpdfOutputName.'.pdf';
    $fpdf->Output($examplePath, 'F');

/*=======================================================================================*/

} else {
    $form = true ;
    // echo '<div>Warten auf $_POST-Daten</div>' ;
    // exit( 'Warten auf $_POST-Daten' );

}

echo '<title>' ;
    echo 'QR-IBAN-Generator' ;
echo '</title>' ;

echo '<body>' ;
    echo '<h1>QR-IBAN</h1>' ;
    if ( $form === true ) {
        //echo '<div>Warten auf $_POST-Daten</div>' ;
        echo '<div style="background: orange;">This is a special QR-IBAN. Classic IBANs will not be valid here.</div>';
        echo '<form id="qrForm" name="qrForm" action="'.basename( $_SERVER['PHP_SELF'] ).'" method="post">' ;
            echo '<table>' ;
                echo '<tr>' ;
                    echo '<td coolspan="2"><h2>Debitor</h2></td>' ;
                echo '<tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-debitor-nachname">Nachname</label></td>' ;
                    echo '<td><input type="text" name="qrForm-debitor-nachname" id="qrForm-debitor-nachname" value="Rutschmann-Schnyder" placeholder="Nachname" autocomplete="qrForm-debitor-nachname" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-debitor-vorname">Vorname</label></td>' ;
                    echo '<td><input type="text" name="qrForm-debitor-vorname" id="qrForm-debitor-vorname" value="Pia-Maria" placeholder="Vorname" autocomplete="qrForm-debitor-vorname" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-debitor-strasse">Strasse</label></td>' ;
                    echo '<td><input type="text" name="qrForm-debitor-strasse" id="qrForm-debitor-strasse" value="Grosse Marktgasse" placeholder="Strasse" autocomplete="qrForm-debitor-strasse" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-debitor-hausnummer">Hausnummer</label></td>' ;
                    echo '<td><input type="text" name="qrForm-debitor-hausnummer" id="qrForm-debitor-hausnummer" value="28" placeholder="Hausnummer" autocomplete="qrForm-debitor-hausnummer" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-debitor-plz">PLZ</label></td>' ;
                    echo '<td><input type="text" name="qrForm-debitor-plz" id="qrForm-debitor-plz" value="9400" placeholder="Ort" autocomplete="qrForm-debitor-plz" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-debitor-ort">Ort</label></td>' ;
                    echo '<td><input type="text" name="qrForm-debitor-ort" id="qrForm-debitor-ort" value="Rorschach" placeholder="Ort" autocomplete="qrForm-debitor-ort" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-debitor-land">Land</label></td>' ;
                    echo '<td><input type="text" name="qrForm-debitor-land" id="qrForm-debitor-land" value="CH" placeholder="Ort" autocomplete="qrForm-debitor-land" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;

                    echo '<td coolspan="2"><h2>Kreditor</h2></td>' ;
                echo '<tr>' ;
                echo '<tr>' ;

                    echo '<td><label for="qrForm-kreditor-iban">QR-IBAN</label></td>' ;
                    echo '<td><input title="This is a special QR-IBAN. Classic IBANs will not be valid here." style="background:orange;" type="text" name="qrForm-kreditor-iban" id="qrForm-kreditor-iban" value="'.$fakeQrIban.'" placeholder="Referenz" autocomplete="qrForm-kreditor-iban" required> CH93 0076 2011 6238 5295 7 is a classic iban. CLASSIC-IBANs will not be valid here</td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-fullname">Name</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-fullname" id="qrForm-kreditor-fullname" value="Robert Schneider AG" placeholder="fullname" autocomplete="qrForm-kreditor-nachname" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-strasse">Strasse</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-strasse" id="qrForm-kreditor-strasse" value="Rue du Lac" placeholder="Strasse" autocomplete="qrForm-kreditor-strasse" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-hausnummer">Hausnummer</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-hausnummer" id="qrForm-kreditor-hausnummer" value="1268" placeholder="Hausnummer" autocomplete="qrForm-kreditor-hausnummer" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-plz">PLZ</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-plz" id="qrForm-kreditor-plz" value="2501" placeholder="Ort" autocomplete="qrForm-kreditor-plz" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-ort">Ort</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-ort" id="qrForm-kreditor-ort" value="Biel" placeholder="Ort" autocomplete="qrForm-kreditor-ort" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-land">Land</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-land" id="qrForm-kreditor-land" value="CH" placeholder="Ort" autocomplete="qrForm-kreditor-land" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label title="Nur Zahlen erlaubt" for="qrForm-kreditor-referenz">Referenz</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-referenz" id="qrForm-kreditor-referenz" value="'.$timestamp.'" placeholder="Referenz" autocomplete="qrForm-kreditor-referenz" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-zusatzinfo">Zusätzliche Informationen</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-zusatzinfo" id="qrForm-kreditor-zusatzinfo" value="Invoice 123456, Gardening work" placeholder="Zusätzliche Information" autocomplete="qrForm-kreditor-zusatzinfo" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-besr-id" title="Unless your bank is PostFinance, in that case use NULL">BESR-ID</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-besr-id" id="qrForm-kreditor-besr-id" value="" placeholder="Unless your bank is PostFinance, in that case use NULL" autocomplete="qrForm-kreditor-besr-id"></td>' ;
                echo '</tr>' ;

                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-currency" title="Currency">Währung</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-currency" id="qrForm-kreditor-currency" value="CHF" placeholder="Referenz" autocomplete="qrForm-kreditor-currency" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td><label for="qrForm-kreditor-amount" title="Amount">Betrag</label></td>' ;
                    echo '<td><input type="text" name="qrForm-kreditor-amount" id="qrForm-kreditor-amount" value="2500.25" placeholder="Referenz" autocomplete="qrForm-kreditor-amount" required></td>' ;
                echo '</tr>' ;
                echo '<tr>' ;
                    echo '<td>&nbsp</td>' ;
                    echo '<td><input type="submit" name="qrForm-absenden" id="qrForm-absenden" value="qrForm-absenden"></td>' ;
                echo '</tr>' ;

            echo '</table>' ;

        echo '</form>' ;
    } elseif  ( $form === false ) {

        // echo '<div>$filename = '.$filename.'</div>' ;
        // echo '<div>$fpdfOutputName = '.$fpdfOutputName.'</div>' ;

        //print "PDF example created here : " . $examplePath;
        // echo '<div>PDF example created here : '.$examplePath.'</div>' ; 
        echo '<div style="margin-bottom: 32px;">PDF-Dokument: <a title="Download '.$timestamp.'_'.$fpdfOutputName.'.pdf"href="./'.$timestamp.'_'.$fpdfOutputName.'.pdf">'.$timestamp.'_'.$fpdfOutputName.'.pdf</a></div>';

        if ( trim( $_POST['qrForm-kreditor-iban'] ) == $fakeQrIban ) {

            echo '<div style="background:orange;">Achtung!<br />Fake IBAN<br />CH4431999123000889012</div>' ;
        }

        $outputweb = new QrBill\PaymentPart\Output\HtmlOutput\HtmlOutput($qrBill, 'de');

        // … oder gleich das ganze Rechnungsdokument als PDF
        print $outputweb
            ->setPrintable(false)
            ->setQrCodeImageFormat(QrBill\QrCode\QrCode::FILE_FORMAT_SVG)
            ->getPaymentPart();

    } else {
        echo '<div>Ups! Bei der Formularverarbeitung ist ein Fehler aufgetreten</div>' ;
    }

echo '</body>';

?>`

sprain commented 2 years ago

The relevant information is this:

Fatal error: Uncaught Sprain\SwissQrBill\Exception\InvalidQrBillDataException: The provided data is not valid to generate a qr code. Use getViolations() to find details.

This means that something in your qr bill setup does not match the qr bill requirements.

So in your code below // 3. Create a full payment part for FPDF make this change:

try {
    $output = new QrBill\PaymentPart\Output\FpdfOutput\FpdfOutput($qrBill, 'de', $fpdf);
    $output
        ->setPrintable(false)
        ->getPaymentPart();
} catch (Sprain\SwissQrBill\Exception\InvalidQrBillDataException $e) {
    foreach($qrBill->getViolations() as $violation) {
        print $violation->getMessage()."\n";
    }
    exit; // of course exiting here is only useful while debugging
}

You should then see where the problem ist. Feel free to post the ouput here if you need some more help.

(PS: Your linked examples work for me without any errors. Deprecation messages are not errors, they are just helpers to let developers know where to adjust code to be compatible with future changes).

sprain commented 2 years ago

I will close this now. Feel free to re-open if there is new information.

merlincom commented 2 years ago

Thanks for the hint with the try function. As expected, this results in the message:

The payment reference type "QRR" does not match with the iban type of "CH9300762011623852957".

Your answers is part of my question. Thank you very much for helping. Now the only open question is how I can make it that both QR-IBAN and CLASSIC-IBAN are processed in the same form. I will try it right away (and it works): You can test it here: https://qr-rechnung.vbbrb.ch/qr-code/FpdfOutput/fpdf-example-full-form-QR-IBAN+CLASSIC.php

$iban = str_replace( ' ', '', $_POST['qrForm-kreditor-iban'] );
$creditorInformation = QrBill\DataGroup\Element\CreditorInformation::create($iban);

if ($creditorInformation->containsQrIban()) {
    // Add payment reference for QR-IBAN
    // This is what you will need to identify incoming payments.
    $referenceNumber = QrBill\Reference\QrPaymentReferenceGenerator::generate(
            preg_replace('/[^0-9]/', '', $_POST['qrForm-kreditor-besr-id'] ),  // You receive this number from your bank (BESR-ID). Unless your bank is PostFinance, in that case use NULL.
            preg_replace('/[^0-9]/', '', $_POST['qrForm-kreditor-referenz'] ) // A number to match the payment with your internal data, e.g. an invoice number
            );

    $qrBill->setPaymentReference(
        QrBill\DataGroup\Element\PaymentReference::create(
            QrBill\DataGroup\Element\PaymentReference::TYPE_QR,
            $referenceNumber
        )
    );
} else {
    // Add payment reference CLASSIC-IBAN
    // This is what you will need to identify incoming payments.
    $qrBill->setPaymentReference(
            QrBill\DataGroup\Element\PaymentReference::create(
            QrBill\DataGroup\Element\PaymentReference::TYPE_SCOR,
            QrBill\Reference\RfCreditorReferenceGenerator::generate( preg_replace( '/[^a-zA-Z0-9]/', '', $_POST['qrForm-kreditor-referenz'] ) )
            )
    );
}
sprain commented 2 years ago

Yep, looks right to me 👍😊