Implementing Banking Detail Sharing in GenHelm

Apr 27, 2024 Written By: Scott Sinclair

In our previous blog post, we discussed a procedure designed to safely share banking details. In this post we show, step-by-step, how this procedure was implemented in GenHelm.

Implementing the Banking Detail Request Form

Let's start by learning how to implement the following form:

Banking Detail Request Form

Defining Form Field Definitions

In GenHelm, form fields can be defined on-the-fly, directly on the form or they can be predefined. The method you choose generally depends on the likelihood that you will need a similar field on other forms. If you think a field will be needed elsewhere, it is a good idea to formally define this field to improve reuse across various forms. When defining an HTML field, it can be based on a native HTML type such as text, number, email, etc., or it can be based on a custom HTML field type definition. One reason you might want to define your own base types is when the supplied HTML types do not provide the desired behaviour. As an example, the native HTML email type allows email addresses that don't contain a valid domain such as user@localhost. Generally, you would want your email validation to be more restrictive.

Using the html_field_type model

Here is an example of a GenHelm email definition created using the html_field_type model.

Email html field type definition

This definition is based on the native email HTML type but it extends this to make it more robust. Notice that some of the Property values begin with an asterisk. This means that these are not native input field properties but rather extended GenHelm properties. Let's review the properties that will be inherited when defining fields based on this email type definition.

PropertyDescription
*trimThis will cause blanks to be trimmed off the beginning and end of the email address.
*caseSince email addresses are not case-sensitive, this is used to automatically convert entered addresses to lower case.
sizeThis will apply a size property to the field which dictates how much real-estate it will take up.
*size-mobileWhen the field is rendered on a phone device, this alternate size property will be used so that the field takes up less space.
patternThis allows us to make the email validation more robust. For example, it must contain a period followed by a top-level-domain.
oninvalidHere we are overriding the message that will be shown when an invalid email is entered.
oninputThis is used to clear the invalid email message when the email is changed.

The email definition above could have been defined within the context of the current site for use only in this site. However, since most sites contain forms having email addresses, this definition was created in the pseudo site named system. This allows us to reuse this definition across any number of sites.

Using the html_field Model

Here we show a definition named contact_email that is based on the email definition shown above:

contact_email field definition

We have supplemented the base email field type properties with additional properties to define a label for the field and make it required. When the contact_email field is rendered it will include all properties from the base field and the specific field definition as shown here:

<label for="email_contact_email" id="email_contact_email_label">Email Address:</label>
<input type="email" size="30" pattern="[\w.\-]+@[\w.\-]+\.\w+" 
oninvalid="this.setCustomValidity('This is not a valid email address')" 
oninput="this.setCustomValidity('')" required="required" name="contact_email" id="email_contact_email" 
onblur="this.value = this.value.replaceAll(' ',''); this.value = this.value.toLowerCase();" />

Notice that some of the pseudo properties, like *trim and *case, have been automatically converted into suitable JavaScript used to implement the property. Additional server-side code is also executed to enforce these rules in case JavaScript is disabled.

Defining the Form

The form we need contains some fields on the main form as well as two separate fieldset objects, each with a group label. In GenHelm, these fieldsets are defined independently. This allows us to reuse groups of fields in several different forms, if required. Consider the lower section of the Banking Details Request form shown above. This fieldset gathers the Accounts Payable Contact information. Here we see the GenHelm definition of this form section.

ap_contact form definirtion

Column one contains two colons. This is a special symbol pair that is used to represent the *label property for the field defined in the next column. Column two contains the field definitions. These are introduced using a single leading colon. You can see that the first and last fields are defined on-the-fly by extending different base fields. The contact_email field is pre-defined so we don't need to supply any additional properties unless we want to override something that is set at the base field or field level.

Next we will look at the definition of the main form.

Banking details request form definition

Looking at the lower portion of the definition, you can see that there are two fields referenced to collect the invoice_number and the customer's email address. These fields are predefined however we are overriding certain properties.

Row 3 contains a special identifier that starts with two consecutive periods. This is known as a placeholder. We will see later that we can write code to assign the contents of this placeholder at runtime. 

Rows 4 and 5 contain references to nested forms. We have already seen what the ap_contact form looks like. The company_info form is similar so we won't bother showing this one.

Row 6 contains a pre-defined submit button and row 7 contains another placeholder. We will learn later that this same form definition can be used as the content of an email. In this context, we will use the second placeholder to embellish the form with additional details that we only want to include in the email.

Near the top of the definition you can see that this form references two separate objects:

  1. A form_handler is a PHP class used to perform special validation or other specialised form requirements.
  2. A transaction is used to perform certain handling when the form is submitted, such as emailing the form contents.

We will look at the form_handler next.

Form Handlers

Most of the form handling is part of the GenHelm framework and does not have to be coded. Nevertheless, there may be times when you require custom behaviour. Form handlers are PHP classes that you can code to validate and interact with the fields on a form. If a form_handler contains a method named post, this will be called automatically when the form is submitted. In the example below, you can see that the form_handler obtains the user's email address and order number from the form and uses this to validate that the user placed the indicated order and that the order has funds owning. GenHelm ships with a simple account management system for keeping track of orders and payments. We use this to confirm that the email addresses and order number entered are associated.

This same form is used to build the email that will be sent to the sales department. We use the context value to implement different behaviour when the form being rendered within the browser vs. being generated as email content.

When generating the email content, we call two of the more than 100 built-in dollar functions:

  1. geoloc is called to get the geo location of the current web user.
  2. link is called to build an encrypted link to the show_banking_details page. This page includes a setting so that it will only be rendered using an encrypted URL.

Function post(){
$return = '';
// Get the form values
$email = $this->field_list['email']->value();
$invoice_number = $this->field_list['invoice_number']->value();
$context = $this->site->get_context();
if ($context === 'browser') {
	// Make sure supplied invoice belongs to the supplied email
	$trans = $this->site->create_object('transaction_manager','',false,'text_cart');
	$trans->set_email($email);
	if (!$trans->load_transactions()) {
	   $trans->transfer_your_messages_to_me($this->form);
	   return false; // Don't submit the form
	}
	if ($trans->get_amount_due() <= 0.0){
		$this->set_placeholder('invoice_issue','Our records show that nothing is owing on the supplied invoice.');
		return false; // Don't submit the form
	}
	else {
		$invoices = $trans->get_invoices_due();
		if (!isset($invoices[$this->invoice_number])) {
			$this->set_placeholder('invoice_issue',
				'The supplied email address and/or invoice number does not match our records.'.
				'Please correct this or contact us for assistance.');
			return false;
		}
	 }
}
else { // email
	$location = $this->site->dollar_function('geoloc');
	$return = '<hr />Banking details were requested by the company named above.<br />'."\n".
		'The Geo Location associated with the sender\'s IP address is: '.$location.' (they may be using a VPN)<br />'.
		'After vetting this business, please send the following information via email to the AP contact...<br /><br />'
		."\n".$this->get_trusted_url();
	$this->set_placeholder('add_to_email',$return);
}
return true; // Submit the form
}
function get_trusted_url($email, $invoice_number) {
	$link = array(
		'pageid' => 'show_banking_details',
		'querystring' => 'email='.$email.'&invoice_number='.$invoice_number,
		'link_text' => 'Show Banking Details',
		'target' => '_blank',
		'encrypt' => 'encrypt');
		$result = $this->site->dollar_function('link',$link);
		return 'In order to pay for your order by bank wire transfer, please follow this'.
			' link to obtain Heavy Duty Tarp\'s banking information<br /><br />'.$result;
}

Recipients

The recipients model is used to organise lists of email addresses in order to send emails to all members of a group. For example, the recipient definition below includes a customer group. This list is used to send an email to a customer while also sending a blind copy to a user within Heavy Duty Tarps.

Recipients definition

Let's review some features of the recipients model.

Mailforms

The mailform model is used to send email to business users or customers. The definition below will send an email to the recipient list named sales. The posted form values are used to derive the sender's name and email address. The contents of the email will be derived from the banking-details form submitted by the customer. We are also overriding the layout used to send the email so that we can include additional information such as the date and time sent and the sender's IP address.

Mailform example

Transactions

The transaction model servers two main purposes:

  1. In situations where several dependent forms are required to gather details from the user, the transaction handles scrolling between these forms.
  2. When the user submits the final form in the series (in this example there is only one form) the transaction can perform various activities such as navigating to an acknowledgement page, saving reports on the server or sending emails.

Transaction definition

In the example above, we can see that the transaction will send the banking_details mailform that we reviewed above. Additionally, it will redirect the user to the page banking_details_acknowledgement which we will look at next.

Simple Acknowledgement Page

The acknowledgement page uses the tags model, which can generate any set of HTML tags. In this case we just generate a simple paragraph. This also demonstrates how you can set other page properties such as the title and header.

Form Acknowledgement Page

There are dozens more page properties which we don't show here since they were not used in this example. One of the advanced properties that is selected in the Noindex option. Since we don't want this acknowledgement page to appear in the generated XML sitemap, we check this option.

Conclusion

We have illustrated a typical series of models used in most GenHelm-generated websites. GenHelm ships with over 70 models of which we have shown eight. The source code is supplied for all models so these can be adapted to your requirements. GenHelm also includes a set of models used to build other models so customers can develop any number of new models to suit their requirements.