Creating a QR-Bill in PHP

The QR-bill aims to modernize the swiss payment transactions. It got introduced on June, 30th 2020. A QR-bill provides, as the name suggest, a QR code with all necessary payment information encoded.

QR-Bill

In this example bellow we want to show you a basic template that implements the QR-code. You are free to use the template.

To get started, make sure you have typeset.sh required using composer. If you have a Symfony or Laravel application you can require the typeset.sh Symfony bundle or Laravel wrapper. Those will give you an easy way of rendering using the twig and blade templates.

Also make sure to require the QR-code extension. This will give you access to a QR-Code element that generates a QR code on the fly.

The QR-slip must be the last element on the bottom of the last page. Therefore we must always make sure that the last page has enough space for rendering the QR-slip. This is done by proving a placeholder box that will flow as any other content. If then the placeholder box does not fit, it makes sure a new page is insert where the QR-Codes has enough space.

The slip is not rendered inside that box, but positioned absolute to the page and placed at the bottom. The placeholder stays empty, it is just there to make sure we always have enough space.

The QR-slip can be easily layout using a grid container. Bellow is the CSS example for the receipt (right) and payment part (left). If you ever find your self not sure about the layout, it helps giving each grid item a different background for testing.

Here is the CSS used for the QR-slip part.

/* * The payment part is in DIN-A6 landscape format (148 x 105 mm). * The receipt to the left of the payment part measures 62 x 105 mm, * so that the two together measure 210 x 105 mm (DIN long). */ .qr-bill { /* the placeholder, that makes sure we have at least 105mm on the current page */ height: 105mm; } .qr-bill > .slip { position: absolute; bottom: -20mm; /* Match your page bottom margin */ left:-20mm; /* Match your page left margin */ height: 105mm; width: 210mm; border-top: 0.25mm dashed black; display: grid; grid-template-columns: 62mm 148mm; grid-auto-rows: 105mm; } .qr-bill > .slip .receipt { margin: 0 0 0 5mm; height: 100%; display: grid; border-right: 0.25mm dashed black; grid-template: 'margin' 5mm 'title ' 10mm 'info ' 1fr 'amount' 15mm 'acceptance' 20mm 'margin' 5mm / 1fr 5mm; } .qr-bill > .slip .payment { margin: 5mm; display: grid; height: 100mm; grid-template: 'title info' 10mm 'qrcode info' 55mm 'amount info' 1fr 'further further' 10mm / 50mm 1fr; grid-auto-flow: row; } .qr-bill section { margin: 0; } .qr-bill section.title { grid-area: title; font-size: 11pt; font-weight: bold; text-align: left; } .qr-bill > .slip .receipt section.title { margin-top: 0; } .qr-bill section.qrcode { grid-area: qrcode; margin: 5mm 5mm 5mm 0; position: relative; } /** * 5.4.2 Recognition symbol * To increase the recognizability and differentiation for users, the Swiss QR Code created * for printout is overlaid with a Swiss cross logo measuring 7 x 7 mm. */ .qr-bill section.qrcode:after { display: block; position: absolute; background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKYAAACmAQAAAAB488naAAAARklEQVRIx2P4jwX8YRiBoh8Y0IH9qOjwFOUHxviBUdFR0SEoyo9Uah0YFR0VpZHoYEjro6KjopSIIoFR0VFR6otiASNQFACdq/PI0UugMQAAAABJRU5ErkJggg==') no-repeat center / 7mm 7mm; width: 45mm; height: 45mm; top: 0; left: 0; content: ''; } .qr-bill section.amount { grid-area: amount; } .qr-bill table.amount th { text-align: left; font-weight: bold; font-size: 7pt; } .qr-bill table.amount td { text-align: left; font-weight: normal; font-size: 11pt; } .qr-bill table.amount .currency { padding-right: 4mm; } .qr-bill section.information { grid-area: info; } .qr-bill section.further-information { grid-area: further; } .qr-bill section.information dl { padding: 0; margin: 0; } .qr-bill section.information dt { font-size: 7pt; font-weight: bold; padding: 0; margin: 0; } .qr-bill section.information dd { font-size: 9pt; padding: 0; margin: 0 0 1em 0; white-space: pre-line; } .qr-bill .payment section.information dt { font-size: 8pt; } .qr-bill .payment section.information dd { font-size: 10pt; } .qr-bill section.acceptance-point { grid-area: acceptance; } .qr-bill section.acceptance-point .label { text-align: right; font-weight: bold; font-size: 7pt; } /* * Scissor icons */ .qr-bill > .slip::before, .qr-bill > .slip::after { display: block; position: absolute; width: 6mm; height: 4mm; background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJoLTYgdy02IiBmaWxsPSJub25lIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHN0cm9rZT0iY3VycmVudENvbG9yIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj4KCTxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiBkPSJNMTQuMTIxIDE0LjEyMUwxOSAxOW0tNy03bDctN20tNyA3bC0yLjg3OSAyLjg3OU0xMiAxMkw5LjEyMSA5LjEyMW0wIDUuNzU4YTMgMyAwIDEwLTQuMjQzIDQuMjQzIDMgMyAwIDAwNC4yNDMtNC4yNDN6bTAtNS43NThhMyAzIDAgMTAtNC4yNDMtNC4yNDMgMyAzIDAgMDA0LjI0MyA0LjI0M3oiIC8+Cjwvc3ZnPg==') no-repeat center / 6mm 4mm; content: ''; top: -2mm; left: 10mm; } .qr-bill > .slip::after { top: 10mm; left: 59mm; transform: rotate(90deg); }

The html then is quite self explaining. You have the receipt and payment part, both have different required sections that need to be filled in.

The QR-Code is generated on the fly using the QR-Code element. Each line holds the information for a given field, best is to consult the documentation to see the exact syntax as there are a few options available. However, the data can be simple written in the qr-code element.

<div class="qr-bill"> <div class="slip"> <div class="receipt"> <section class="title"> Receipt </section> <section class="information"> <dl> <dt>Account / Payable to</dt> <dd>CH44 0000 0000 0000 0</dd> <dt>Reference</dt> <dd>21 0000 00003 14756 74457 51474 Order from 15.10.2020</dd> <dt>Additional information</dt> <dd>Invoice 2344235 - Gardening work</dd> <dt>Payable by</dt> <dd>Max Muster & Söhne Musterstrasse 123 8000 Seldwyla</dd> </dl> </section> <section class="amount"> <table class="amount"> <tr> <th class="currency">Currency</th> <th class="amount">Amount</th> </tr> <tr> <td class="currency">CHF</td> <td class="amount">1 521.52</td> </tr> </table> </section> <section class="acceptance-point"> <div class="label">Acceptance point</div> </section> </div> <div class="payment"> <section class="title"> Payment part </section> <section class="qrcode"> <qr-code>SPC 0200 1 CH440000000000000 S Max Muster & Söhne Musterstrasse 123 8000 Seldwyla CH 1521.52 CHF S Simon Muster Musterstrasse 1 8000 Seldwyla CH QRR 21000000003147567445751474 Order from 15.10. 2020 EPD</qr-code> </section> <section class="amount"> <table class="amount"> <tr> <th class="currency">Currency</th> <th class="amount">Amount</th> </tr> <tr> <td class="currency">CHF</td> <td class="amount">1 521.52</td> </tr> </table> </section> <section class="information"> <dl> <dt>Account / Payable to</dt> <dd>CH44 0000 0000 0000 0</dd> <dt>Reference</dt> <dd>21 0000 00003 14756 74457 51474 Order from 15.10.2020</dd> <dt>Additional information</dt> <dd>Invoice 2344235 - Gardening work</dd> <dt>Payable by</dt> <dd>Max Muster & Söhne Musterstrasse 123 8000 Seldwyla</dd> </dl> </section> <section class="further-information"></section> </div> </div> </div>

There is also a validator for your data at validation.iso-payments.ch, it requires to create an account though with sixed.

Bellow is a full example.

<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <meta name="WCAG" content="2.1"> <title>invoice.html</title> <link rel="stylesheet" type="text/css" href="https://typeset.sh/samples/assets/invoice.css"> <link rel="stylesheet" type="text/css" href="https://typeset.sh/samples/assets/qr-bill.css"> </head> <body> <header id="logo"> <img alt="typeset.sh" class="logo" src="https://typeset.sh/samples/assets/typeset.sh-logo.svg" style="float:right" /> </header> <header id="letter"> <div class="left"> <address class="sender">typeset.sh • Hansaallee 78 • 60323 Frankfurt am Main • Germany</address> <address class="recipient"> <strong>Sample Business</strong><br /> John Doe<br /> 3 Edgar Buildings<br /> George Street<br /> England </address> </div> <div class="right"> <div class="info"> <div class="title">Invoice</div> <table class="information"> <tr> <th scope="row">Customer #</th> <td>55-4884844</td> </tr> <tr> <th scope="row">Date</th> <td>January 26 2021</td> </tr> <tr> <th scope="row">Due date</th> <td>February 27 2021</td> </tr> <tr> <th scope="row">USt.-IdNr.</th> <td>DE 317 949 066</td> </tr> </table> </div> </div> </header> <h1>Invoice #2344235</h1> <div class="order-details"> <section class="payment"> <strong>Payment method</strong><br /> Paypal </section> <section class="shipping"> <strong>Shipping method</strong><br /> DHL - Express </section> </div> <div class="order-details"> <table class="items"> <thead> <tr> <th scope="col" class="sku">SKU</th> <th scope="col">Name</th> <th scope="col" class="qty">Quantity</th> <th scope="col" class="price">Price</th> <th scope="col" class="price">Tax</th> <th scope="col" class="price">Total</th> </tr> </thead> <tbody> <tr> <td class="sku">45-572-547</td> <td>Gardening work</td> <td class="qty">1h</td> <td class="price">1.180,29 &euro;</td> <td class="price">341,23 &euro;</td> <td class="price">1.521,52 &euro;</td> </tr> </tbody> </table> <table class="totals"> <tr> <th scope="row">Subtotal</th> <td>1.180,29 &euro;</td> </tr> <tr> <th scope="row">Tax</th> <td>341,23 &euro;</td> </tr> <tr class="grand"> <th scope="row">Grand total</th> <td>1.521,52 &euro;</td> </tr> </table> </div> <div style="clear: both; margin-top: 10mm; text-align: justify; text-align-last: left"> <p>Dear Jon Doe, thank you for you order, if you have any questions, please don't hesitate to ask.</p> </div> <div class="qr-bill"> <div class="slip"> <div class="receipt"> <section class="title"> Receipt </section> <section class="information"> <dl> <dt>Account / Payable to</dt> <dd>CH44 0000 0000 0000 0</dd> <dt>Reference</dt> <dd>21 0000 00003 14756 74457 51474 Order from 26.01.2021</dd> <dt>Additional information</dt> <dd>Invoice 2344235 - Gardening work</dd> <dt>Payable by</dt> <dd>Max Muster & Söhne Musterstrasse 123 8000 Seldwyla</dd> </dl> </section> <section class="amount"> <table class="amount"> <tr> <th class="currency" scope="col">Currency</th> <th class="amount" scope="col">Amount</th> </tr> <tr> <td class="currency">CHF</td> <td class="amount">1 521.52</td> </tr> </table> </section> <section class="acceptance-point"> <div class="label">Acceptance point</div> </section> </div> <div class="payment"> <section class="title"> Payment part </section> <section class="qrcode"> <qr-code>SPC 0200 1 CH440000000000000 S Max Muster & Söhne Musterstrasse 123 8000 Seldwyla CH 1521.52 CHF S Simon Muster Musterstrasse 1 8000 Seldwyla CH QRR 21000000003147567445751474 Order from 15.10. 2020 EPD</qr-code> </section> <section class="amount"> <table class="amount"> <tr> <th class="currency" scope="col">Currency</th> <th class="amount" scope="col">Amount</th> </tr> <tr> <td class="currency">CHF</td> <td class="amount">1 521.52</td> </tr> </table> </section> <section class="information"> <dl> <dt>Account / Payable to</dt> <dd>CH44 0000 0000 0000 0</dd> <dt>Reference</dt> <dd>21 0000 00003 14756 74457 51474 Order from 26.01.2021</dd> <dt>Additional information</dt> <dd>Invoice 2344235 - Gardening work</dd> <dt>Payable by</dt> <dd>Max Muster & Söhne Musterstrasse 123 8000 Seldwyla</dd> </dl> </section> <section class="further-information"></section> </div> </div> </div> </body> </html>