SecurePay in Power Pages

Andrew GrischenkoPower Apps10 months ago20 Views

This post explains how to integrate with SecurePay (Asutarlia’s payment gateway) in a Power Pages site. It’s based on the similar architecture pattern explained in the post Power Pages Paypal integration. If you are interested in this topic, you may also want to check out my posts on the Anatomy of the Stripe payments in Power Pages and How to improve Stripe payments in Power Pages.

Please note that some of the approaches demonstrated here are incomplete for the production level of use and don’t cover all important aspects (e.g., error handling, user experience, etc.).

SecurePay integration pattern

SecurePay offers various integration methods, including API integration. The sequence diagram for API integration from the documentation page is displayed below. If it looks a bit complicated, just remove mentally the “EMV 3D Secure JS” block and associated arrows, which we will not be exploring in this post, and it will be simpler to understand.

The sequence is essentially as follows:

  • (1) The SecurePay UI Component is initialised on the merchant website within the customer browser.
  • (2) Once the user has entered their card details, the UI Component’s tokenise method is called by the browser script.
  • (3) The card information is sent directly to the SecurePay server API endpoint, bypassing the merchant (yours) infrastructure or code.
  • (4) Tokenised card response is returned to the SecurePay UI Component.
  • (5) On successful tokenisation, the onTokeniseSuccess will be invoked which includes a token. This card token generated is temporary, expires after 30 minutes and is a once-off usage token for one successful payment.
  • (6,7,8) Related to 3D Secure, will skip for now.
  • (9) The merchant server will use token to make a payment to SecurePay API. You must make this payment request from your server.
  • (10) SecurePay API will process the anonymous payment and return a payment response
  • (11) The Merchant server should proxy the payment response to the merchant website so that an appropriate response can be returned to the customer browser.

In order to execute transactions in steps (9,10,11) securely we need a server-side component. We will use Power Automate cloud flows integration with Power Pages, thanks to the relatively recent GA availability of this excellent option. This is a good fit because it’s:

  • Synchronous – we can get the response from the flow execution as the script awaits it.
  • Secured with Power Pages infrastructure and Web role.
  • It is a low-code approach, and no additional infrastructure is required.

Specific architecture

The specific architecture of this implementation is pictured below. Can you recognise the corresponding components from the high-level diagram earlier? (Hint: there is no Bank on this one).

To implement this architecture we will need:

  • A SecurePay developer account with a REST API and keys (A).
  • A Power Pages website page (B) with a dynamically loaded script from the SecurePay website (C).
  • A Power Automate cloud flow to process a payment using a token via CreatePayment API (D).
  • A table to hold payment info on Dataverse (E).

Step-by-step guide

This guide assumes some basic knowledge of the Power Platform, including working with solutions, cloud flows, environment variables, etc. Screenshots are provided where required to clarify ambiguity or highlight critical points. Should you get lost with the steps or code changes, you can find the complete solution with Power Automate flow and script for the snippet in this GitHub repository.

SecurePay developer account sign-up

1. Sign up for a SecurePay developer account if you don’t have one yet.

2. In the SecurePay Dashboard, in the “Test your integration” section you will find the merchant code, client ID and client secret needed later to authenticate the API calls. So, keep the browser tab open.

Create a solution for customization

3. Go to the Power Pages maker environment https://make.powerpages.microsoft.com/ and in the Solutions section create a new solution and give it a name of your choice, e.g. SecurePay Integration.

4. Create 3 environment variables with the values from the corresponding fields in the previous step:

  • SecurePay Merchant Code
  • SecurePay Client ID
  • SecurePay Secret

NOTE: For the production use, you should use the “secret” type for the environment variable type. This would require an Azure account with the Secrets vault service created in it.

Create a Power Pages site and payment page

In this section we will do a few things:

  • Create a Power Pages website and a new Payment page.
  • Create a snippet to place a SecurePay form in.
  • Create a handling JavaScript.

6. Log in to the Power Pages maker portal and create a new site (unless you already have one).

5. Go to the Power Pages Studio and select the Data section on the left. You will see a settings cog near the Data label. Click that cog and select the Current solution you’ve just created, e.g. SecurePay Integration (SecurePay Demo on my screenshot).

7. Create a new page on the Power Pages site and name it Payment or similar.

8. Go to the Power Pages Management app, accessible from the ellipsis menu “…” just below the Set up section:

9. Create a Content Snippet in the Portal Management app name it SecurePay payment or similar and select type HTML. See the Power Pages documentation for more details if needed.

10. Create 2 records of tyle “Site Settings” as below with the corresponding values from the SecurePay Dashboard:

  • SecurePay/ClientID
  • SecurePay/MerchantCode

11. Copy the following code into the content of the snippet. It does a few things:

  • <div> contains HTML structure and loads the form via the external script hosted by SecurePay.
  • Inline <script> tag is a function to initialise the securePayUI object. Make note of the onTokeniseSuccess function, which is a placeholder now, and we will add a bit of code to it at the next step.
  • There is also the resultMessage function, which is a placeholder for proper user experience on successful or failed payments. I hope you will make it better!
<div>
    <form onSubmit="(function(){ return false; })();">
        <div><label for="id" style="margin-bottom: 10px">Amount</label></div>
        <div><input id="amount" type="number" style="margin-bottom: 10px"></div>
        <div id="securepay-ui-container"></div>
        <button type="button" onClick="(function(){ mySecurePayUI.tokenise();})();">Submit</button>
        <button type="button" onClick="(function(){ mySecurePayUI.reset();})();">Reset</button>
        <p id="result-message"></p>
    </form>
  <script id="securepay-ui-js" src="https://payments-stest.npe.auspost.zone/v3/ui/client/securepay-ui.min.js"></script>
</div>
<script language="JavaScript">
  var mySecurePayUI = new window.securePayUI.init({
      containerId: 'securepay-ui-container',
      scriptId: 'securepay-ui-js',
      clientId: "{{ settings["SecurePay/ClientID"] }}",
      merchantCode: "{{ settings["SecurePay/MerchantCode"] }}",
      card: { // card specific config and callbacks
        allowedCardTypes: ['visa', 'mastercard'],
        showCardIcons: false, 
        onTokeniseSuccess: async function(tokenisedCard) {
          // code will be added later
        },
        style: {
          backgroundColor: 'rgba(135, 207, 250, 0.1)',
          label: {
            font: {
                family: 'Arial, Helvetica, sans-serif',
                size: '1.1rem',
                color: 'darkblue'
            }
          },
          input: {
           font: {
               family: 'Arial, Helvetica, sans-serif',
               size: '1.1rem',
               color: 'darkblue'
           }
         }  
        },
  
      },
      onLoadComplete: function() {
          // the SecurePay UI Component has successfully loaded and is ready to be interacted with
          console.log("card is ready to use");
          console.log(JSON.stringify(mySecurePayUI));
      }
    });

    function resultMessage(message) {
      const container = document.querySelector("#result-message");
      container.innerHTML = message;
    }
</script>

12. In the Power Pages Management app, in the Web Pages section, find the Payment page you created. In the Localized Content section, you should see another record, Payment, which represents the localised version of the page. This is the one we need to put the content on—click on it.

13. Add the following code to the page and note the use of the content snippet you’ve just created (Liquid markup):

<div id="i3jhx6" class="row sectionBlockLayout text-left" style="display: flex; flex-wrap: wrap; margin: 0px; min-height: auto; padding: 8px;">
  <div id="ibdmvz" class="container" style="padding: 0px; display: flex; flex-wrap: wrap;">
    <div id="iykqyu" class="col-md-4 columnBlockLayout" style="flex-grow: 1; display: flex; flex-direction: column; min-width: 250px; word-break: break-word;"><img src="/Cat-PC.png" id="iz0lbv" alt="Cat-PC" name="Cat-PC.png" style="width: 100%; height: auto; max-width: 100%;" /></div>
    <div id="i0sicq" class="col-md-8 columnBlockLayout" style="flex-grow: 1; display: flex; flex-direction: column; min-width: 250px; word-break: break-word;">
      <p id="i0kgrm">Payment</p>
      {{ snippets["SecurePay payment"] }}
    </div>
  </div>
</div>

14. In the Power Pages studio, click “Sync” to bring in the changes made in the Power Pages Management app. Once it’s done, select Preview -> Desktop. If everything has been done correctly, you should see the page with a cat, a field amount and the SecurePay form initialised.

Payment table

If you already using the Power Pages Stripe integration (in Preview) or if you want to have a solution compatible with the payments solutions for Power Pages from Microsoft, you may want to use the Payments table that comes when this feature is enabled. It comes as part of Stripe integration, but don’t worry, you don’t need to have or use Stripe to enable the Payment table. It’s not payment gateway specific and can be extended nicely as explained below.

Follow these steps to enable it:

15. In the Power Pages Studio, select the Set up section on the left panel and then find the External apps (preview) section.

16. You will see 2 integrations available – DocuSign and Stripe. We are interested in the latter today, so select Install in the Stripe row below. Click Start installation in the pop up dialog to confirm and wait until it completes.

17. Go to your solution created at step 3 and Add existing -> Table and select the Payment table that has been added to your environment.

18. In the same solution, use Add existing -> More -> Choice and select Provider choice. Customize the choice by adding a new provider – SecurePay.

This is it, you don’t need to do anything else. No need to set up Stripe keys as you are not going to use them.

Payment cloud flow

Now we have the required components to create the payment processing cloud flow. This is an overview of the flow steps described below. You can find the solution (unmanaged) with all flows and other components in this GitHub repository.

19. Create a new Instant Power Automate flow.

  • Name it “Process SecurePay transaction” or similar and skip the trigger selection at that step. For some reason, Power Pages trigger is still not available in the list of triggers on the initial creation page. I hope it will be fixed soon.
  • Select trigger When Power Pages calls a flow:
  • Add two input parameters:
    • type String and name token
    • type Number and name amount.

20. Add the following actions:

  • Action: Add a new row (Dataverse)
    • Name: Create payment record
    • Table name: Payments
    • Amount: @{div(triggerBody()['number'], float(100))}
    • Payment date time: @{utcNow()}
    • Payment identifier: @{triggerBody()['text']}
    • Payment method: Card
    • Payment status: Created
    • Provider: SecurePay
  • Action: HTTP Request (authentication request to receive access token)
    • Name: Authentication HTTP request
    • Method: POST
    • URI: https://welcome.api2.sandbox.auspost.com.au/oauth/token
    • Headers: "Content-Type": "application/x-www-form-urlencoded"
    • Body: grant_type=client_credentials&audience=https://api.payments.auspost.com.au
    • Authentication: Basic
    • Username: Environment variable SecurePay Client ID
    • Password: Environment variable SecurePay Secret
  • Action: Parse JSON (pase the results of the previous action to retrieve access_token for the next step)
    • Name: Parse Authenticate response
    • Content: @{body('Authentication_HTTP_request')}
    • Schema:
{
    "type": "object",
    "properties": {
        "access_token": {
            "type": "string"
        },
        "expires_in": {
            "type": "integer"
        },
        "token_type": {
            "type": "string"
        }
    }
}
  • Action: HTTP Request (actual call to the Order API to create an order)
    • Name: Processing HTTP request
    • Method: POST
    • URI: https://payments-stest.npe.auspost.zone/v2/payments
    • Headers:
      • "Content-Type": "application/json"
      • "Idempotency-Key": "@{guid()}"
    • Authentication: Raw
    • Value: Bearer @{body('Parse Authenticate response')?['access_token']}
    • Body:
{
  "merchantCode": "@{parameters('SecurePay Merchant Code (tech_SecurePayMerchantCode)')}",
  "amount": @{triggerBody()['number']},
  "token": "@{triggerBody()['text']}",
  "ip": "127.0.0.1"
}
  • Action: Parse JSON (parse results of the previous HTPP call)
    • Name: Parse payment response
    • Content: @{body('Processing_HTTP_request')}
    • Schema:
{
    "type": "object",
    "properties": {
        "createdAt": {
            "type": "string"
        },
        "amount": {
            "type": "integer"
        },
        "currency": {
            "type": "string"
        },
        "status": {
            "type": "string"
        },
        "bankTransactionId": {
            "type": "string"
        },
        "gatewayResponseCode": {
            "type": "string"
        },
        "gatewayResponseMessage": {
            "type": "string"
        },
        "errorCode": {
            "type": "string"
        },
        "customerCode": {
            "type": "string"
        },
        "merchantCode": {
            "type": "string"
        },
        "ip": {
            "type": "string"
        },
        "token": {
            "type": "string"
        },
        "orderId": {
            "type": "string"
        }
    }
}
  • Action: Condition
    • Input: @body('Parse_payment_response')?['status']
    • Condition: is equal
    • Value: paid
    • “If yes” flow:
      • Action: Update a row (Dataverse)
        • Table name: Payments
        • Row ID: @{outputs('Create_payment_record')?['body/pp_paymentid']}
        • Payment identifier: @{body('Parse_payment_response')?['bankTransactionId']}
        • Payment status: Succeeded
        • Payment details: @{body('Parse_payment_response')?['gatewayResponseMessage']}
        • Payment status reason: OK (@{body('Parse_payment_response')?['gatewayResponseCode']})
      • Action: Return value(s) to Power Pages
        • String parameter name: status
        • Parameter value: @body('Parse_payment_response')?['status']
    • “If no” flow:
      • Action: Update a row (Dataverse)
        • Table name: Payments
        • Row ID: @{outputs('Create_payment_record')?['body/pp_paymentid']}
        • Payment identifier: @{body('Parse_payment_response')?['bankTransactionId']}
        • Payment status: Failed
        • Payment details: @{body('Parse_payment_response')?['gatewayResponseMessage']}
        • Payment status reason: @{body('Parse_payment_response')?['gatewayResponseCode']} (@{body('Parse_payment_response')?['errorCode']})
      • Action: Return value(s) to Power Pages
        • String parameter name: status, Parameter value: @body('Parse_payment_response')?['status']
        • String parameter name: error_code, Parameter value: @body('Parse_payment_response')?['errorCode']
        • String parameter name: error_message, Parameter value: @body('Parse_payment_response')?['gatewayResponseMessage']

The last condition block should look like this:

21. Register the created flow on the Power Pages site. Go to the Design Studio of the website, section Set up and select Cloud flows. Select the button Add existing flow:

22. In the pop-up panel on the right, select the Process SecurePay transaction and add a web role allowed to access this flow: Authenticated Users and click Add. Here is our security for the flow endpoints out of the box!

23. Once the flow is added, a specific endpoint URL will be assigned. Copy it to the clipboard.

24. In the Portal Management app, create a Site Settings record with the name “SecurePay/FlowURL” and the value of the power automate flow endpoint from the previous step:

25. In the same Portal Management app, go to Content Snippets, find the one you created earlier and in the snippet content replace the placeholder onTokeniseSuccess() function with the following block:

onTokeniseSuccess: async function(tokenisedCard) {
          
          console.log("Tokenised: "+JSON.stringify(tokenisedCard))
          const url = "{{ settings["SecurePay/FlowURL"] }}";

          const payload = {
            eventData: JSON.stringify({
              amount: (document.getElementById("amount").value)*100,
              token: tokenisedCard.token
            })
          };
          var result;

          await shell
            .ajaxSafePost({
              type: "POST",
              contentType: "application/json",
              url: url,
              data: JSON.stringify(payload),
              processData: false,
              global: false,
            })
            .done(function (response) {
              console.log("Received response: "+response);
              result = JSON.parse(response);

              if (result.status == "paid") {
                resultMessage('Your payment has been successfull processed!');
              } else {
                resultMessage(`Transaction failed: ${result.error_message} (code: ${result.error_code})`);
              }
            })
            .fail(function (error) {
              console.error(error);
              resultMessage(`Could not process SecurePay payment...<br><br>${error}`);
            });

          return result.status;
        },

Let’s break down what’s happening here:

  • Reading flow URL from the “SecurePay/FlowURL” site settings.
  • Packing a payment amount and card token into payload object.
  • Flow endpoint (trigger) call – note use of shell.ajaxSafePost
  • Checking the response status if “paid” or not.
  • Error handling for failed payments or failed API calls.

Should you get lost with the steps or code changes, you can find the complete solution with Power Automate flow and script for the snippet in this GitHub repository.

Time for the final end-to-end test!

Test the integration

The test is now to confirm that the transaction was successfully recorded on the Dataverse side and that the Payments table is updated with the SecurePay API information.

26. In the Power Pages studio, click “Sync” to import the changes made in the Power Pages Management app. Once it’s done, select Preview -> Desktop. As we made flow integration for Authenticated user role, you will need to Sign in. Use Azure AD option and login with the credentials you used for thr development environment.

Positive scenario

27. Enter the amount (whole number, no cents) and test card details below and click Submit. You can find more testing card numbers for different scenarios on the SecurePay website.

  • Amount: 100
  • Card number: 4111111111111111
  • Expiry date: any date in the future, e.g. 12/28
  • CVV: any 3digit number, e.g. 123

You should see a successful message after some delay and the browser console log will show you the API response details:

If you don’t have the expected results, check for other error messages in the console and check if you have provided the correct credentials in the environment variables and site settings. You may also want to check the Process SecurePay transaction flow history and make sure that this flow has run successfully or fix errors if there were any.

28. Return to Power Pages Studio, select the Data section, and select the Payments table. You should see a row or a few. Sort by the column Payment date time to see the latest transaction, and add the columns Payment details, Payment identifier, and Payment status if they haven’t been selected yet. You should see the transaction details.

Negative scenario

In order to test a negative scenario and simulate a error, we can use decimal values of the payment amount, see the documentation on the error codes.

For card payments, testing different response codes is configured by payment gateway. Most of the card payment made with cent values (e.g. $1.50 – 150 cents) in Test environment will trigger negative scenario which will result in payment failure. The example cent amount mentioned in `Test Data` is subset of data to test negative scenarios. Full dollar amount (e.g. 1000) and cent values of 1008 and 1511 can be used to test successful payments.

29. We will simulate error “Insufficient funds” and enter amount $100.51 and test card details below and click Submit.

  • Amount: 100.51
  • Card number: 4111111111111111
  • Expiry date: any date in the future, e.g. 12/28
  • CVV: any 3digit number, e.g. 123

You should see an error message after some delay and the browser console log will show you the API response details:

30. Return to Power Pages Studio, select the Data section and select the Payments table. You should see a row with a failed payment as below:

Conclusion and what’s next

The solution described here is not complete. To bring this to your actual site you need to take care of at least the following:

  • Integrate into the flow of where your purchase journey starts and the payment amount is determined. Replace the hard-coded value with the actual amount to be paid.
  • Create a proper successful payment confirmation page or receipt and ensure a redirect to it.
  • Do proper error handling for different scenarios and display relevant messages to customers.

I hope this guide helped you to start on this journey and provided a demonstration of Power Automate cloud flows integration in Power Pages. Please let me know what you want to know more! Please comment or reach out to me at any time at andrew@technomancy.com.au.

Original Post https://cloudminded.blog/2024/04/09/securepay-in-power-pages/

0 Votes: 0 Upvotes, 0 Downvotes (0 Points)

Leave a reply

Follow
Sign In/Sign Up Sidebar Search
Popular Now
Loading

Signing-in 3 seconds...

Signing-up 3 seconds...