Payments in Power App Portals – Part 1

I recently came across a requirement where I had to support payments within Power Apps Portals and a few other interesting stuff that I believe is worth a record. To summarize all the lessons learned during this implementation I decided to write this post.

By the end of the article, we will have a Power Portals integrating with a payment gateway using a hosted payment page (Page hosted by the payment provider company, so you don’t need to handle payment sensitive details) and storing the results in the Microsoft Dataverse.

For the purposes of this demo, I will use GlobayPayments as my payment gateway provider but you can follow this example with any other payment provider.

Let’s start modeling our Demo environment. The first thing to do is to create a Payment table to hold the payment details and responses from the payment provider.

The Order ID will be used to identify the payment record within the payment gateway provider, Response Code and Response Message are there to store the result sent back once the transaction is executed and the Return URL is the URL that we will send to the payment gateway provider so they can redirect the user once the payment is processed.

For educational purposes, we will also create a Product table so our client can choose a product with a specific price that will be then sent to the payment gateway.

We will also need a Products page so the client can choose what they want to buy and for that, we will use Web Pages and Entity List.

Start by going to the Power Portals Designer, which offers a simpler way to create Web Pages and Entity List

Click on New page on the top left menu and fill in the required fields

once the page is created, click on the second menu item “Components” and select the list that we will use to show the available products to the client. Select the Products table and the desired view.

While adding the list you have the option to choose from an existing list or create a new one.

When you click on browse website the page should look like this if you added records to your table:

I want to give the customer the chance to view details for each product before purchasing it. To do that we need to create a Product Page, that will be rendered once the user clicks on a product from our Product List.

Once the Product Page is created, enable the View Details option in the Products List, set the Target Type to WebPage, and select the Product Page

Now when the user clicks on a record from our Product List he will see the Product Page which is where we will add a “Pay Now” button

To add the Pay Now button we will use the Custom Javascript field in the Advanced tab of the Localized Content Page for the Product’s Page that we just created.

The following code will add a PAY NOW button to the form once the page is ready

$(document).ready(function () {

$("div.actions").append("<input type=’button’ id='paybtn' name=paybtn value='PAY NOW' class='submit-btn btn btn-primary form-action-container-left' />");

});

We now need to connect the created button with the Payment Gateway.

The way most of the Payment Gateway Providers work is by asking you to generate a request to their service with your account information plus the payment details that you want to process, send it to them via an HTTP along with a URL which they will send an HTTP request back letting you know the result of the operation.

In order to create the HTTP request to the payment provider we need to send some account information such as merchant ID, secrets, and credentials, and we don’t want to send the request from the client-side, to prevent exposing sensitive information, so we need to create the request from the server to keep the information private.

If you have a web application available you can write the HTTP request and response for the payment provider there, in this example I’m assuming you don’t so we will use Azure Functions which basically gives us the ability to process server-side code without needing to worry about infrastructure.

Each Payment Provider will have its own particularities, as for the Global Payments, it requires us to send a specific JSON to their service and we can easily do that by using the examples they provide in their documentation, changing the account details to our own:

            var service = new HostedService(new GpEcomConfig
            {
                MerchantId = "yourmerchantid",
                AccountId = "youraccountid",
                SharedSecret = "yoursharedsecret",
                ServiceUrl = "https://pay.sandbox.realexpayments.com/pay",
                HostedPaymentConfig = new HostedPaymentConfig {Version="2"}
            });

var hppJson = service.Charge(amount).WithCurrency("EUR").Serialize();

The hppJson object is the JSON that should be sent to the payment gateway to start the transaction.

But before creating the request we need to know which product is the client purchasing and get the value for the product from our Product table in the Microsoft Dataverse.

For security reasons we shouldn’t send the product price from the client, instead, we will send the product ID and on the server-side, fetch the product price using the product id.

The following function returns the object expected by the Payment provider to start the transaction

[FunctionName("Request")]
        public static async Task<IActionResult> RealexRequest(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            // Get the query string parameter for the chosen product
            var productId = req.Query["productId"].ToString();

            // Initialize Payment Gateway Service
            var service = new HostedService(new GpEcomConfig
            {
                MerchantId = "yourmerchantid",
                AccountId = "youraccountid",
                SharedSecret = "yoursharedsecret",
                ServiceUrl = "https://pay.sandbox.realexpayments.com/pay",
                HostedPaymentConfig = new HostedPaymentConfig { Version = "2" }
            });

            try
            {
                // Initialize DataVerse API
                var api = InitDataVerseAPI();

                // Get Payment Information
                dynamic product = await api.Get("bit365_products", new Guid(productId), new CRMGetListOptions { Select = new[] { "bit365_price" } });

                var price = (double)((dynamic)product).bit365_price;

                var hppJson = service.Charge(Decimal.Parse(price.ToString())).WithCurrency("EUR").Serialize();

                var orderId = ((JValue)((JObject)(JsonConvert.DeserializeObject(hppJson))).GetValue("ORDER_ID")).Value;

                dynamic paymentUpdated = new ExpandoObject();
                paymentUpdated.bit365_orderid = orderId;

                await api.Create("bit365_payments", paymentUpdated);

                return new OkObjectResult(hppJson);

            }
            catch (ApiException exce)
            {
                return new JsonResult($"Error {exce.Message}");
            }

        }

In the next post, we will have a look at the response sent by the payment provider and how we can handle that within Portals.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *