PayID Checkout app

Use the Payment Request integration to give full control to your payer and account receivable automation for you

Introduction

Azupay's PayID Checkout App has been designed to optimise the payer experience using PayID to pay to you.

While PayID based payments offer great benefits the experience is new so an optimum UX is important to help your payers complete their journeys successfully. Based on our user research and understanding of payment behaviours, we have designed and tested the App to maximise conversion rate of payments and reduce the integration effort required by you.

The PayID Checkout App involves you sending Azupay a PaymentRequest API call, you showing your payer our PayID Checkout App UX and your system listening for updates from our webhooks. Our PayID Checkout App has the following features:

  • Fully screen responsive 
  • Available as an embedded iFrame or as a full checkout experience
  • Allows for customisable logos, colours and URL redirection. These can be managed within the Client Dashboard
  • Surfaces minimal content to users to reduce cognitive overload that leads to dropouts during the payment experience

How do I use it?

  1. Call the PaymentRequest API
  2. In the response body of the POST /v2/paymentRequest call, there will be a field called PaymentRequestStatus.checkoutUrl
  3. Present this URL to your customer using any means appropriate, you could:
    1. Embed into an iframe element and include it in your website. To do this make sure the checkoutUrl has a query parameter type=iframe
    2. Surface in a new pop-out window
    3. Send the link via an email
    4. Send the link via an sms
    5. Embed it into a QR code
  4. Once the payment is completed the payer will see a confirmation

Key Configurations

The payment experience can be configured to suit the needs of your use case. In the table below configurations may be set by URL parameters or values in the API calls.

ExperienceConfigurationAmountComments
Duration allowed for paymentExpiry TimeOnce a PayID has expired the payer is unable to initiate payment.

Use a short expiry time if payment must be confirmed quickly, e.g. a travel booking must be paid before reserved tickets are released or repriced.

If no Expiry Time is set the PayID can continue to receive payments indefinitely.
Exact MatchMultipayment default to FALSESet AmountExact Match allows a single payment exactly matching a set target amount only.

Use this to ensure perfect instant reconciliation for every payment to your transaction. With this setting you will have zero manual reconciliation, zero unallocated funds and zero underpaid invoices.

Multipayment is not set to TRUE and an Amount must be set.
Refund OverpaymentsMultipayment = TRUESet AmountRefund Overpayments will receive any amounts but if the total of all payments made to this PayID exceeds the total target amount the surplus will be instantly returned to the payer.

You will end up with a perfectly paid transaction but could have transactions with partial payments if your payer does not send enough.
Any AmountMultiplayment = TRUENo Set AmountAny accepts all payment amounts.
URL TypeiFrame / Full FrameiFrame embeds the App inside your page.

If iFrame is not set the App will surface in a pop-up page with white label branding.
RedirectRedirect URLWhen the payer journey has completed (either following success or terminating at some unhappy path ending) the payer will be returned to this URL
CancelcancelRedirectURLIf a cancelRedirectURL is provided the payer sees a cancel button. Hitting the cancel button will send the payer to the specified URL and will deregister the PayID preventing anyone accidentally paying to this PayID

There are other Configurations explained elsewhere in these Guides and the API Reference. The list above is the subset having the most impact on overall experience and should be considered before you start your implementation journey.

Example Configurations

Dynamic PayID

This is a short-lived PayID that only accepts a single payment and is uniquely generated in real time as required. Once a payment for the exact amount of the PayID is made by a payer, the PayID is deregistered and no further payments to the PayID can be made.

This PayID setup is most suited to your customers making a one-off payment for goods or services and you want perfect instant match of payment to the purchase.

📘

Sounds technical but its the easiest configuration

This was the original Azupay product so just set your amount and off you go...

Configure like this:

  • Set amount for each transaction in the API call
  • Make sure you do NOT make Multipayment=TRUE and do NOT enter a value for PayID string
  • Other configurations (e.g. expiry time) can be varied as suits your specific needs

Open Static PayID

This is an enduring PayID that accepts multiple payments of any amount at any time.

This PayID setup is most suited to account based payments where you want to know which of your customers have sent you money but you don't need specific amounts at specific times. Simply create a single Open Static PayID for each of your customers. Examples include topping up digital wallets or adding credit to a customer account.

👍

Hint: Encourage your customers to save their PayID in their banking app

For faster repeat payments they can use the same PayID each time

Configure like this:

  • Multipayment = TRUE
  • Expiry Time either empty or a far future date (e.g. 1 year away)
  • Optional: set PayID string to something short but meaningful to your customer, e.g. their initials plus 4 random digits @ your domain.
  • Other configurations (e.g. URL Type) can be varied as suits your specific needs

Static Recycled PayID

This is a recurring PayID that accepts specified amounts periodically. When the specified amount is received the PayID is deregistered (preventing further payments to it), until you make another Payment Request using the same PayID at which point it becomes available again to be paid to with a new target amount.

This PayID setup is most suited to customers with periodic or usage-based recurring billing. Simply make a Payment Request with Amount and PayID string specified, ensure you use the same PayID string for each customer every time. Your customer will pay to the same PayID every time they receive a bill and each time they do so they will get a description of the amount owing for that specific bill and there will be controls to ensure they don't overpay or accidentally pay it twice. Improve customer satisfaction and reduce contact centre calls!

👍

Hint: Encourage your customers to save their PayID in their banking app

For faster repeat payments they can use the same PayID each time

Configure like this:

  • Multipayment = TRUE
  • Expiry Time either empty or a far future date (e.g. 1 year away)
  • Set Amount
  • Set a PayID string for each of your customers and keep a record of it
  • Optional: set PayID string to something short but meaningful to your customer, e.g. their initials plus 4 random digits @ your domain
  • Important: every time you call the Payment Request for a specific customer you must always use the same PayID string for that customer

Example match type experiences

There are various scenarios managed by the PayID Checkout App.

Incorrect amount paid

This happens when the PaymentRequest is NOT a multiPayment and the payer pays the wrong amount. Will indicate that the full amount was refunded and ask for the correct payment to be done.

Under payment

This happens when the PaymentRequest IS multiPayment, has a paymentAmount and the payer pays less than the amount required.
In this case the checkout widget will indicate that there is a remaining balance to be paid.

Overpayment

This happens when the PaymentRequest is multiPayment, has a paymentAmount and the payer pays more than the amount required.
In this case the checkout widget will indicate the payer that there was a refund for the excess and will take them to the confirmation page.
This is considered a success scenario.

Expired payments

The PayID Checkout App polls for expired payments, which means once a customer fails to make a payment within the time set by you, the payment request expires and can no longer accept payments. The PayID associated with the payment request is deregistered. You can set the payment expiry time in the paymentExpiryDatetime field in PaymentRequest.

Redirect after payment

If you supply a query string parameter called redirectURL at the end of URL supplied in PaymentRequestStatus.checkoutUrl, then we will redirect your user to this URL after the payment has been received. The redirectURL must be url encoded. For example:

https://pay.azupay.com.au/v2/checkout/ZDk2YzkxYTUyYjYzYTIwMzk3?redirectURL=https%3A%2F%2Fwww.mywebsite.com%3Fmyparam%3Dsomething

The payer will be redirected to https://www.mywebsite.com?myparam=something

If you use the iframe version, the page will send events with changes on the Payment Request status so you can use these events to remove the iframe instead. Keep reading for more information about how to use the events generated by the iFrame.

Cancel redirect

The page will display a Cancel button if you supply a query string parameter cancelRedirectURL. The PayID associated with the payment request is deregistered when a user clicks Cancel.

The cancelRedirectURL must be url encoded. For example:

https://pay.azupay.com.au/v2/checkout/ZDk2YzkxYTUyYjYzYTIwMzk3?cancelRedirectURL=https%3A%2F%2Fwww.mywebsite.com%3Fpage%3Dhome
When the user click the Cancel button, then we will redirect your user to this URL (i.e. https://www.mywebsite.com?page=home (opens new window))

👍

Full version only

This behaviour is not available in the iframe version

Embedding the PayID Checkout App as an iframe

You can render a stripped down version of this App without logo or theme customisations that is more suitable for embedding inside you own web application as an HTML iframe. In this case the returned checkoutUrl will have a query parameter type=iframe. If the checkoutUrl that you received does not have it you can always append it, or request Azupay to set it up for you by default.

Below is a sample page with the iframe version of the PayID Checkout App embedded into it.

Receiving events from the iframe

The host web app can react to changes within the PayID Checkout App by listening to message events being emitted within the iframe. This would give the host web app more control over the entire checkout process and thus allow for a better user experience.

Consuming events from the host HTML page

In order to consume the events from the host web app you will add an event listener to the main window and capture
events of type message. Then the main payload of the event will be inside the event.data. There are other fields inside the event that can be helpful, for example the origin field as shown in line 3 can be used to filter out if you have more than one iframe rendered sending message events at the same time.

window.addEventListener('message', (event) => {
    console.log('Event Data ' + JSON.stringify(event.data))
    console.log('Event Origin ' + JSON.stringify(event.origin))
    console.log('Event Ports ' + JSON.stringify(event.ports))
    if (event.data.EventType === 'UserInterface') {
        const clientIframe = document.getElementById('client-iframe');
        clientIframe.height = event.data.Data.height;
        const height = document.getElementById('height');
        height.innerText = event.data.Data.height;
    }
    if (event.data.EventType === 'PaymentRequest') {
        const status = document.getElementById('payment-request-status');
        status.innerText = event.data.Data.PaymentRequestStatus.status;
    }
})

If you want to see a working example and try the iframe approach you can use our iframe tester HTML.

Currently, there is two types of events fully described below.

Resizing Events

These type of events will notify the parent window about changes to the width of the content rendered within the iframe. This way the parent page can make the iframe bigger to prevent it from rendering scrolling bars.

The following is an example of a resizing event:

{
  "EventType": "UserInterface",
  "Timestamp": "2022-12-02T05:20:06.506Z",
  "Data": {
    "height": 651
  }
}

🚧

App Responsiveness and QR code visibility

Note that the PayID Checkout App will hide the QR code when the width of the iframe is less than 600px

Payment Request status events

These type of events notify on the full PaymentRequest and PaymentRequestStatus object from the API so that the parent web App can react to changes on the state. For example, remove the iframe when the payment has been completed.

The following is an example of the Payment Request event

{
  "EventType": "PaymentRequest",
  "Timestamp": "2022-12-02T05:18:56.250Z",
  "Data": {
    "PaymentRequest": {
      "clientId": "0038ae8a0989a81ccf92bfead1bdfe18",
      "multiPayment": true,
      "clientTransactionId": "61d0f44d-3d84-4fd1-a02e-7cc9957256d6",
      "payID": "[email protected]",
      "paymentAmount": 20,
      "paymentExpiryDatetime": "2022-12-03T19:58:51.027Z",
      "paymentDescription": "Enter your order number in reference field"
    },
    "PaymentRequestStatus": {
      "createdDateTime": "2022-12-02T05:15:51.147Z",
      "paymentRequestId": "499112286f20771a678d6af05bb36e6c",
      "status": "WAITING"
    }
  }
}

Did Not Receive Confirmation event

This event is raised when the user indicates that they have made a payment but it has not been received yet.

{
  "EventType": "DidNotReceiveConfirmation",
  "Timestamp": "2022-12-02T05:18:56.250Z",
  "Data": {}
}

Full example

Below is the sample code for the entire HTML page


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script>
        window.addEventListener('message', (event) => {
            console.log('Event Data ' + JSON.stringify(event.data))
            console.log('Event Origin ' + JSON.stringify(event.origin))
            console.log('Event Ports ' + JSON.stringify(event.ports))

            if (event.data.EventType === 'UserInterface') {
                const clientIframe = document.getElementById('client-iframe');
                clientIframe.height = event.data.Data.height;
                const height = document.getElementById('height');
                height.innerText = event.data.Data.height;
            }
            if (event.data.EventType === 'PaymentRequest') {
                const status = document.getElementById('payment-request-status');
                status.innerText = event.data.Data.PaymentRequestStatus.status;
            }
        })

        const updateUrl = () => {
            const input = document.getElementById('payment-request');
            const url = input.value;
            const iframe = document.getElementById('client-iframe');
            iframe.src = url;
        }

    </script>
</head>
<body>
<div>
    <div>
        <label for="payment-request">Put the encoded id of PR</label>
        <input id="payment-request" data-test="url-input"/>
        <button onclick="updateUrl()" data-test="submit">Submit</button>
    </div>
    <div>
        <div>Status:</div>
        <div data-test="payment-request-status" id="payment-request-status"></div>
    </div>
    <div>
        <div>Height:</div>
        <div data-test="height" id="height"></div>
    </div>
    <iframe id="client-iframe"
            title="PayID payment"
            src=""
            style="border: 1px solid #000000; width: 100%;"
    />
</div>
<div>
</div>
<script>

</script>
</body>
</html>