Hello again! Today, our main course will be accepting credit card payments in a web application developed with the OutSystems platform. Knowing that this may be a make it or break it paragraph, our main goal consists is showing how to create a payment form using Client-Side Encription (CSE). Then, we’ll complement our system with a cool — yet not very complex — control panel and some neat additional features.

This is not the first article in the subject. If you haven’t done so, I would recommend reading the previous article (linked below, or here) so you can be acquainted with a few terms. Knowing it’s an estimated 17-minute read, I’ll give a brief summary: we defined our business case (accepting payments), our constraints (not being PCI-compliant) and we delve into the implementation of the core services module — API integration — and the mobile plugin. All of this using Adyen’s, a well-known payment gateway provider. If you don’t know Adyen, I would suggest that next time you’re in the departures of Lisbon’s airport, just look at the displays on the self-service machines that sell pretty much anything from pillows to international chargers.

Moving forward to this article, I’m going to show you how to implement the web form — using a web block — as well as how the control panel is built. But, without further ado, let’s recap and update our business case.

The business case

So, the business case is fairly simple: we want to accept payments in our application, in both of our channels — web and mobile. (…) we wanted to support (payments) on the web as well since, due to technical difficulties or a through a different sale channel, a one-time web payment form could be issued.

The excerpt above was taken from the previous article and, as an overall picture, remains untouched. We are going to add a few details on our control panel, though.

Our control panel should allow us to issue a new payment request and, with that same request, either generate and forward a payment form to our customer or request the credit card details and type the payment details ourselves — you can get the details maybe by phone, which seems rather usual in some cultures. Despite not being “a huge fan” of this option, I’ll keep it available and then, on case-by-case basis, you can evaluate if you should allow this or not. It’s up to you.

Adding to that, our control panel will also allow us to gather some insights about the number of payments in the last 30 days, the successful/unsuccessful payment ratio and, finally, the top payments in the last few days — 30 days will be our baseline. Despite this not being a very comprehensive set of features, should be more than enough to jump-start your payment processing — and, as you might have guessed by now, work as a “framework” so you can extend the features as per your needs.

Before moving forward, let me make an additional comment here which is totally related to the previous paragraph: if you find that a given feature is missing or if you would like to “merge” a set of changes to any of the components depicted on these articles, feel free to drop me a note and I’m sure we can manage something out of it. After all, sharing is caring.

Figure 1 — Something close to the final result.

Now that we have given a close to our business case — and took a sneak peek of the final form — , let’s move on to the implementation of the web block used for processing the payment.

The payment form

Figure 2 — Web form skeleton

If you recall, we had an *awesome* UI on our mobile plugin. This time, contrary to what was done on the mobile version, we are using London as a base theme — and that’s why we don’t have “colorless” buttons on our web block, as seen on Figure 2. We do this for a couple of reasons, with the most relevant being the ability to properly draw a layout.

Figure 3 — Web block signature.

Using the same methodology as before, we are building the form using the most simple/basic elements that the platform provides. By using this, we allow you to customize pretty much anything you want on the form by just applying a stylesheet. As it might seem obvious, you always have the chance to open the module and modify it for your likings or needs. Related to the web block itself, we have two input parameters — the client-side encryption Key and the identifier of the payment: PaymentId. Additionaly, we have a local variable to store the form input data. It has all the fields present on the web block’s user interface.

So, if I may, let me do a quick run over all the interface elements: we have the basic fields like name, card number, expiration and CVC/CVV inputs. We also have two “self explanatory” buttons along with a third one, named Process.

Before going into the “what, how and why?” of the Process button, let me quickly explain something: see the highlighted container on Figure 2? Below that container — if your screen allows you to see it, since mine is a real s***er — , you can see two additional input fields. The first one is used to generate the current timestamp of the payment attempt while the second one is used to store the output of the encryption algorithm. The generation timestamp is a crucial field since it’s required by the encryption algorithm to successfully work and, as a security measure, makes sure you can’t process a payment more than 24 hours after the user typed the details. Personally, I don’t see a business case where you would want to delay the processing of the payment but we’ll never know. If you, dear reader, think of such scenario, leave it in the comments so we can discuss it as well.

Figure 4 — Process button container.

Let’s now move into the Process button. As you can see on Figure 2, the Process button is inside a container that, by default, is hidden through the Display property being marked as false — check Figure 4. It’s always good to remember that putting the Display property as false only adds “display: none;” to the style of the widget — it is still rendered/available on the screen. If you want to avoid this, use an IF widget instead.

Notes aside, we do this (hidden Process button) because we want to “automatically” trigger the execution of the screen action tied to this button. We’ll achieve this (the trigger) by using JavaScript but, for now, let’s take a closer look into this screen action assigned to this button.

Figure 5 — Initial flow of the Process screen action.

On Figure 5 you can take a quick peek at what we are doing in the “beginning” of the screen action. Similarly to what we have done on the mobile side, we retrieve the payment and, then, we check two different things:

If the payment is in a valid state, namely: Staged, Refused or Service Unavailable, and

If the encrypted data is valid.

In order to confirm if the encrypted data is valid, we check the contents of one of those hidden inputs — the second one, for the matter — , which is tied to the EncryptedData variable on the form. And we do our validations using two factors: that it’s not empty — since the encryptData function returns false for non-successful cases — and that the first seventeen (17) characters match a given string, namely adyenjs_0_1_20_1$. On Figure 6 you can see the full IF condition that we are applying.

Quick note: there is a newer version (0.1.21) of the library but, as documented on the previous article, we’ll stay on 0.1.20 for now.

Figure 6 — Full IF condition.

Figure 7 — Service call preparation and invokation.

Then, after this validation check — assuming everything was properly validated — , we obtain the user’s IP address, assign the values to our Adyen_CapturePayment request structure and, well… execute the request. Instead of using a custom (and native!) plugin to obtain the user’s IP address, this time we use the GetIP action available at the HTTPRequestHandler extension. It’s not the only function we’ll use from this extension since, as you can see on Figure 5 and Figure 8, all those EnableButtons<*> actions are the RunJavaScript. We do this in order to enable the buttons again after the end of the execution. But it’s not only that, so let’s not get much ahead of ourselves…

Figure 8 — Processing the response.

Upon the invocation the Adyen_CapturePayment action, as understandable, we process the given response. This follows pretty much in the same tone as the mobile plugin: we check for Success or for a Technical Error. In these cases, we notify the parent block (or screen) so we can take the required flow. If the payment is refused, we just alert the user so the payment details can be fixed/changed.

If you recall Figure 2, you’ll see that we have two different buttons beyond the Process button. As with every OutSystems button, we need to have a screen/navigation action associated with it. Unfortunately, this does not suit our needs since we want — and need — to encrypt the data on the client before doing anything at server level. So, the question remains: how can we use the default OutSystems buttons and still achieve this?

Figure 8 — The most complex screen action that you’ll ever see. Like, EVER!

First step: define empty screen actions. The Pay and Clear screen actions look exactly like Figure 8. It’s probably the most complex screen action you’ll ever see in your entire life. Kidding aside, we just define this so we can publish our code and go fool around with chairs and fire extinguishers.

Second step: define JavaScript functions. We’ll define two JavaScript functions in our web block so we can use them in our buttons. See the gist below with those functions.

I’m not a JavaScript expert so, if you find something that can be optimized, let me know so I can apply the new version.

The first function (ClearData) is what we’ll use on our Clear button. It just gets all the inputs inside the form and clears up their value. Simple.

The second function (EncryptData) is a little bit more complex. Summing up, we are doing the exactly same thing as in our mobile counterpart — the difference is that, here, we have to do everything in a “plain” JavaScript function.

We start by disabling the buttons so we *prevent* the user from tampering the data or do a duplicate request — so he knows something is running on the back-end. Then we generate our generation time — redundancy intended — and create our encryption instance. We fill the creditData structure and pass it to the validator. If valid, we invoke the encrypt function and use the output of the function by assigning it as the input value. If not, we iterate over all the properties, marking the applicable ones as invalid. Finally, we enable the buttons again. Simple, right?

A possible follow up question might be: where are you loading the JavaScript file with the definition of the adyen.encrypt function?

Figure 10 — JavaScript resource imported.

The first thing we do is to import the resource file and then mark the Deploy Action as Deploy to Target Directory. Then, you need to copy the content of the Runtime Path variable.

Figure 11 — Web block Preparation.

Once you have this Runtime Path, we go into the Preparation of the web block which contains our form and we use a different action present on the HTTPRequestHandler extension: AddJavaScriptTag. For this purpose, the plain HTTPRequestHandler extension is up for the task — and there’s no need to use the Buffed version. We’ll talk in a different article about this Buffed version.

Moving on, we just paste the Runtime Path that we’ve copied before into the JavaScriptURL input parameter and, et voilà, that’s it. It might sound a bit scarier than what it really is.

Figure 12 — Clear button onclick.

Ok, it’s time to proceed to our last chapter in terms of the payment form. How do we invoke the JavaScript functions? Another simple explanation. On each of the buttons, we define an extended property for the onclick. For the Clear button, we use the ClearData function while for the Pay button we’ll use EncryptData function — as seen on Figure 13.

Figure 13 — Pay button onclick.

Before proceeding — and still related to this subject — I would like to highlight two things:

See the semicolon at the end of the function invokation? That. Is. Critical. Why? Because OutSystems binds a set of JavaScript functions to the onclick attribute and, as such, what we define in the extended property is what will be executed first. So, the rendered code will be something like Figure 14.

Figure 14 — Generated code for Pay button. If wondering, this is Sublime Text.

If you don’t explicitly state the semicolon, it will throw a JavaScript error — and we don’t want that, at all. As such, if you’re adding cool things like JavaScript functions to your OutSystems onclick extended properties, DO NOT FORGET THE SEMICOLON!

The second remark is that I’m having a lot of trouble using manually generated buttons. When I say “manually generated buttons”, it’s something like this: