You are viewing limited content. For full access, please sign in.

Discussion

Discussion

How to/ Question: Using a payment gateway's payment page with Forms

posted on February 24, 2021 Show version history

So this is a mix between a How-to and a question to help finish the how-to. This was created with my colleague Devin Goble. I have to give credit where it's due. So, here's a quick explanation of the overall goal of the how-to.

 

This how-to will explain way to use a form to fill in a payment gateway's payment portal to allow a customer to pay for a service without utilizing the supported payment gateways (Braintree & Authorize.net). The first part of the form will have the customer filling out basic information for the payment portal (Name, Address, Phone #, Email, etc.). The second part will consist of a way to identify what the customer is paying for (Permit #, Product #, etc.) as well as the price and any other identifying info on what's being paid for.

The Process:

Above is the test form that was created for this project. Your form can look different, but this is what I'm using to write this how-to, so this will be your point of reference.

So once the form has been filled out and the submit button clicked. We then generate XML and redirect the user to the payment gateway. This part will be an automatic generation and redirect, but for testing, it's easier to use a button to cause the redirect. Here is what the submission page looks like.

Keep in mind I'll go over the code for this after I show the process to allow you to understand it a little better with some context. Also, the XML in the multiline input is generated after clicking the button to send it to the payment gateway (in this case it's Point and Pay). I also expanded the field so you can see the layout of the XML that gets sent to the payment gateway. Now thankfully my payment gateway was nice enough to set up a demo payment portal so I can play around with the XML and how their portal reacts, but yours may differ.

Once it's sent to the payment gateway this is the screen I see. I'll show you this part to illustrate that Laserfiche can send XML to an outside source and allow it to work without anything other than some JavaScript. Here is the beginning payment page of my payment gateway.

The customer then enters their payment info, verifies their paying the correct item, and finishes the payment through the payment gateways portal. This is where the question will go out to any users or Laserfiche staff who might be able to answer it. But first, let's talk about the code to accomplish.

The How-to:

Looking back at the submission page, you need some Javascript to be able to generate the XML and to redirect the user to the payment gateway. First the complete code, then a breakdown.

$(document).ready(function(){
  $("input[type='submit']").on("click", function() {
    const xmlDocument = document.implementation.createDocument(null, "params");
    
    xmlDocument.documentElement.setAttribute("FirstName", $("#Field2").val() || "");
    xmlDocument.documentElement.setAttribute("LastName", $("#Field3").val());
    xmlDocument.documentElement.setAttribute("Address1", $("#Field4_DSG0").val());
    xmlDocument.documentElement.setAttribute("Address2", $("#Field4_DSG1").val());
    xmlDocument.documentElement.setAttribute("City", $("#Field4_DSG2").val());
    xmlDocument.documentElement.setAttribute("State", $("#Field4_DSG3").val());
    xmlDocument.documentElement.setAttribute("Zip", $("#Field4_DSG4").val());
    xmlDocument.documentElement.setAttribute("Phone", $("#Field6").val());
    xmlDocument.documentElement.setAttribute("Email", $("#Field5").val());
        
    const rows = $("#q7 tbody tr");
    
    const customerFullName = $('#Field2').val() + $('#Field3').val();
    const customerPhone = $('#Field6').val();
    const customerEmail = $('#Field5').val();
    
    rows.each(function() {
      const id = $(this).find("select[id^='Field8']").val();
      const referenceNumber = $(this).find("input[id^='Field9']").val();
      const businessName = $(this).find("input[id^='Field11']").val();
      const siteAddress = $(this).find("input[id^='Field12']").val();
      const description = $(this).find("input[id^='Field13']").val();
      const amount = $(this).find("input[id^='Field14']").val();
      const product = document.createElement("product");
      product.setAttributeNS(null, "ID", id);
      product.setAttributeNS(null, "ReferenceNumber", referenceNumber);
      product.setAttributeNS(null, "BusinessName", businessName);
      product.setAttributeNS(null, "SiteAddress", siteAddress);
      product.setAttributeNS(null, "Description", description);
      product.setAttributeNS(null, "CustomerFullName", customerFullName);
      product.setAttributeNS(null, "CustomerPhoneNumber", customerPhone);
      product.setAttributeNS(null, "CustomerEmail", customerEmail);
      product.setAttributeNS(null, "Amount", amount);
      xmlDocument.documentElement.appendChild(product);
    });
            
    const x = new XMLSerializer();
    const xmlString = x.serializeToString(xmlDocument);
    
    localStorage.setItem("pnp", xmlString);
  });
});

Let's start breaking it down. The first part of this code:

$(document).ready(function(){
  $("input[type='submit']").on("click", function() {

The first line is for telling the form to run the code below when it's loaded (.ready) and then the second line is to run the code after the input with the type submit is clicked (in this case the submit button the form automatically adds.

const xmlDocument = document.implementation.createDocument(null, "params");

This part creates a variable (this is a constant, which is a type of variable) which is where the XML document is created.

    xmlDocument.documentElement.setAttribute("FirstName", $("#Field2").val() || "");
    xmlDocument.documentElement.setAttribute("LastName", $("#Field3").val());
    xmlDocument.documentElement.setAttribute("Address1", $("#Field4_DSG0").val());
    xmlDocument.documentElement.setAttribute("Address2", $("#Field4_DSG1").val());
    xmlDocument.documentElement.setAttribute("City", $("#Field4_DSG2").val());
    xmlDocument.documentElement.setAttribute("State", $("#Field4_DSG3").val());
    xmlDocument.documentElement.setAttribute("Zip", $("#Field4_DSG4").val());
    xmlDocument.documentElement.setAttribute("Phone", $("#Field6").val());
    xmlDocument.documentElement.setAttribute("Email", $("#Field5").val());

This part of the code sets various "attributes" inside the XML document to the associated value from the field. For example, the Email attribute of the XML document is set to the value of the field with the ID of Field5.

const rows = $("#q7 tbody tr");

This part is related to the table at the bottom that is in relation to what the customer is paying for. This constant will be looking for each of the table rows (tr) in the body (body) of the table (#q7).

const customerFullName = $('#Field2').val() + $('#Field3').val();
    const customerPhone = $('#Field6').val();
    const customerEmail = $('#Field5').val();

This part is for the Full Name, Phone, and Email on the portal that the payment gateway has on the payments. It's something specific to the way Point n Pay has set this demo portal up, but may not be needed in your payment gateway.

rows.each(function() {
      const id = $(this).find("select[id^='Field8']").val();
      const referenceNumber = $(this).find("input[id^='Field9']").val();
      const businessName = $(this).find("input[id^='Field11']").val();
      const siteAddress = $(this).find("input[id^='Field12']").val();
      const description = $(this).find("input[id^='Field13']").val();
      const amount = $(this).find("input[id^='Field14']").val();
      const product = document.createElement("product");
      product.setAttributeNS(null, "ID", id);
      product.setAttributeNS(null, "ReferenceNumber", referenceNumber);
      product.setAttributeNS(null, "BusinessName", businessName);
      product.setAttributeNS(null, "SiteAddress", siteAddress);
      product.setAttributeNS(null, "Description", description);
      product.setAttributeNS(null, "CustomerFullName", customerFullName);
      product.setAttributeNS(null, "CustomerPhoneNumber", customerPhone);
      product.setAttributeNS(null, "CustomerEmail", customerEmail);
      product.setAttributeNS(null, "Amount", amount);
      xmlDocument.documentElement.appendChild(product);
    });

This is a chunk of code, but does something relatively simple. Lets break it down to smaller chunks.

rows.each(function() {

This checks each of the values in the constant "rows" has. In this case since it was for a table it will have each of the rows as it's value and so this will iterate through each row and run the code contained between the {}'s.

      const id = $(this).find("select[id^='Field8']").val();
      const referenceNumber = $(this).find("input[id^='Field9']").val();
      const businessName = $(this).find("input[id^='Field11']").val();
      const siteAddress = $(this).find("input[id^='Field12']").val();
      const description = $(this).find("input[id^='Field13']").val();
      const amount = $(this).find("input[id^='Field14']").val();

This part is setting constants for each of the fields in the row the code is currently on. $(this) tells the code to look at the currently selected element which in this case is the current row of the table. The .find part of the constant finds the input with the ID that is entered. These constants will be what fills in the product part of the XML.

const product = document.createElement("product");

This part creates a new element inside the XML (element being like an <a> or a <div> in HTML). This is what will be used to fill in the top part of that demo portal in the picture above.

      product.setAttributeNS(null, "ID", id);
      product.setAttributeNS(null, "ReferenceNumber", referenceNumber);
      product.setAttributeNS(null, "BusinessName", businessName);
      product.setAttributeNS(null, "SiteAddress", siteAddress);
      product.setAttributeNS(null, "Description", description);
      product.setAttributeNS(null, "CustomerFullName", customerFullName);
      product.setAttributeNS(null, "CustomerPhoneNumber", customerPhone);
      product.setAttributeNS(null, "CustomerEmail", customerEmail);
      product.setAttributeNS(null, "Amount", amount);

This is where the constants set earlier are set in the "product" elements attributes. The Attribute is the second portion of the lines, and the third part is the value, which in this case is the constant set earlier.

xmlDocument.documentElement.appendChild(product);
    });

This part is where the product element is appended as a child to the XML document we created in the beginning. The last line is closing the .each and the code inside it.

const x = new XMLSerializer();
    const xmlString = x.serializeToString(xmlDocument);
    
    localStorage.setItem("pnp", xmlString);
  });
});

The last part is how we get the XML to the payment gateway. This part is a bit complicated, but just know that it makes the XML look and act as it should for the payment gateway to be able to interpret it. The localStorage part of this is where we will store the XML to allow the generation and redirect to the payment gateway on the submission page.

 

The second part of the code is on the submission page. Here is where you would enter the code:

Now the code for the submission page:

<form id="pnpxml" action="https://demo.pointandpay.net/web/JacksonCountyRoadsLF" method="post" target="_blank">
<button onclick="document.getElementById('pnpxmlfield').value = localStorage.getItem('pnp'); return true;" id="send" type="submit">Send to PnP</button>
<textarea type="hidden" id="pnpxmlfield" name="paramXML"></textarea></form>

So for the submission page we create a form element so we can send the POST message to the demo payment gateway portal.

<form id="pnpxml" action="https://demo.pointandpay.net/web/JacksonCountyRoadsLF" method="post" target="_blank">

This part determines where the POST message is sent (action) and the fact that it is POSTing to the URL (method). The target is set to blank to make the redirection of the customer open a new tab. This can be changed if having it open a new tab is not ideal.

<button onclick="document.getElementById('pnpxmlfield').value = localStorage.getItem('pnp'); return true;" id="send" type="submit">Send to PnP</button>

This is the button seen in the picture of the submission page. The onclick portion is what returns the XML document we stored in localstorage from the form. You can see that it "gets the item" pnp from localstorage and sets the multi-line field or text area to the value of the XML document.

<textarea type="hidden" id="pnpxmlfield" name="paramXML"></textarea></form>

This is the text area seen in the picture that is filled with the XML. And also the closing tag for the form.

This finally brings us back to the last part of the whole project:

The Question:

Once the customer has entered their payment info and submitted it to the payment gateway the payment gateway will redirect them to another Laserfiche Form. This is where the problems start. Point n Pay current is sending an XML document back to Laserfiche Forms as they don't seem to be able to add parameters to the redirection URL (See this link for more on that: https://www.laserfiche.com/support/webhelp/Laserfiche/10/en-US/administration/Default.htm#../Subsystems/Forms/Content/populating-a-form-with-url-parameters.htm%3FTocPath%3DForms%7CCreating%2520a%2520Form%7C_____9).

So once the form attempts to load I receive a white screen. Below are some snapshots of the issue from the Elements, Network, and Console windows of the Developer console in Google Chrome.

If anyone has successfully got a POST message with encoded XML as a field in the request body to work with Forms this how-to could be completed. But until then, I can only get you that far. The XML from the POST message sent by the payment gateway seems to break the form.

Let me know in the comments if you have any questions at all about the parts that do work. I'll be happy to help you understand them and be able to modify them for your situation.

Thanks everyone for taking the time to make it through this. Hope everyone is having a great Empower 2021!

2 0
replied on February 25, 2021

Forms supports completing a message start event using HTTP POST, you can refer to https://doc.laserfiche.com/laserfiche.documentation/11/administration/en-us/Default.htm#../Subsystems/Forms/Content/Processes/Completing-a-Message-Start-Event-using-HTTP-POST.htm, the value of a field needs to be in the format as following which is JSON

data: JSON.stringify({'Single_Line':'123'})
0 0
replied on February 26, 2021

That is very useful information. I'll have to see if the payment gateway provider can send the parameters from the payment through JSON. If not I'll likely have to set up an external site to convert the XML to JSON and pass it to Forms. Thanks for your insight Xiuhong!

0 0

Laserfiche Cloud XML Redirects w/ Point and Pay

Sign in to reply to this post.