Skip to main content

Dynamic QR Payment

DuitNow QR Payment Process

The following is a sample payment process of dynamic QR code, using vending machine as a case study.

Step 1 : Select Payment Method

Consumer selects DuitNow QR as Payment Method after purchasing goods.

Image below displays a sample vending machine checkout page.


Step 2 : Generate and Display Dynamic QR Code

The vending machine utilizes the QR Payload Generation API to generate the dynamic QR code and displays it.


Step 3 : Customer Scans QR Code

The customer then scans the QR code to initiate the payment process. Simultaneously, the vending machine expects to receive a payment notification within 30 seconds.

Step 4 : Payment Notification

  • If the vending machine does not receive the payment notification at the designated callback URL in that 30 seconds duration, it will trigger the QR Payment Query API to check the payment status.
  • If QR Payment Response is not received, the vending machine will repeat the query every 5 seconds for a duration of up to 5 minutes, awaiting a successful or declined status.
  • If the 3-minute duration elapses without receiving a concrete status update (Approved or Failed), the vending machine shall abort the purchase attempt.
tip

The recommended timeout for a QR transaction is around 10 seconds before elapsed time, i.e: 10 seconds before 5 minutes.

Step 4.0 : Payment Success

Image below displays a sample notification screen from vending machine when the payment is processed successfully.


Step 4.1 : Payment Failed

Image below displays a sample notification screen from vending machine when the payment is failed.


Sample Code

The following sample codes can be used to convert the QR payload data into a digital QR image, enabling users to easily scan and process the information encoded within.

Step 1 : QR code placeholder.

The design of the placeholder is following the DuitNow guideline.

CSS
html, body {
height: 100%;
margin: 0;
flex-direction: column;
}

@font-face {
font-family: "gotham";
src: url("../fonts/GothamRoundedBold.ttf") format("truetype");
}

.gotham-font {
font-family: "gotham";
}

.content {
width: 300px; /* set the width of the content */
height: 200px; /* set the height of the content */
position: absolute;
top: 25%;
left: 50%;
transform: translate(-50%, -25%);
}

.duitnow-border {
border:10px solid;
background-color: rgb(237, 46, 103);
border-bottom-width: 1px;
border-color: rgb(237, 46, 103);
border-top-left-radius: 8px;
border-top-right-radius: 8px;
max-width:240px;
}

.duitnow-bg {
border-radius: 8px;
background-color: white;
padding: 15px;
}

.duitnow-border-txt {
background-color: rgb(237, 46, 103);
max-width: 260px;
padding-top: 12px;
padding-bottom: 12px;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
}

.duitnow-txt {
background-color: rgb(237, 46, 103);
width: 250px;
margin: 0;
color: white;
font-size: 15px;
}
Html/jsp
<body class="content">
<div align="center">
<div class="duitnow-border" align="center">
<div class="duitnow-bg" align="center">
<img id="qrCode" height="210" width="210">
</div>
</div>
<div class="duitnow-border-txt">
<p class="gotham-font duitnow-txt">MALAYSIA NATIONAL QR</p>
</div>
</div>
<form action="demoservlet" method="get">
<div align="center" style="margin-top: 20px">
<input type="submit" value="send">
</div>
</form>
</body>

The returned link contains the QR payload. Extract the payload and put in the placeholder.

javascript
<script>
var urlParams = new URLSearchParams(window.location.search);
var myParam = urlParams.get('f370_QRPayload');

if (myParam === null || myParam === undefined) {
console.log("Parameter is not set");
} else {
$.get("qrservlet?myParam=" + myParam, function(base64Image) {
$("#qrCode").attr("src", "data:img/png;base64," + base64Image);
});
}
</script>
Servlet (java) – doGet method
String qrCodeData = request.getParameter("myParam");

QRCodeWriter writer = new QRCodeWriter();
try {

Map<EncodeHintType, Object> hints = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.MARGIN, 1);

BitMatrix bitMatrix = writer.encode(qrCodeData, BarcodeFormat.QR_CODE, 450, 450, hints);

// Define the color you want to use using RGB values
int customColor = 0xFFED2E67; // Pink color

// Create a new MatrixToImageConfig object with the desired foreground and background colors
MatrixToImageConfig config = new MatrixToImageConfig(customColor, 0xFFFFFFFF);

// Write the image data to the response output stream using the modified configuration
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, "png", outputStream, config);
byte[] imageBytes = outputStream.toByteArray();
String base64Image = Base64.getEncoder().encodeToString(imageBytes);
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(base64Image);
} catch (WriterException e) {
e.printStackTrace();
}