Currently set to Index
Currently set to Follow

Overview

NetAccess is used by Integrated Software Vendors (ISV’s) to integrate Netcash services into a host/remote application with HTML content. The initial data comes from the host application and the services are then provided and managed via NetAccess in the host application. “Base pages” have been developed to provide customized service offerings for payroll and accounting software.

See the service overview here.

NetAccess was formerly known as Sage Connected Services.

Programmers Guide

To start, refer our Programmers guide for more detail on how to apply the required methodology in this document.
Please ensure that the settings for Developers are correctly set before you proceed with the implementation below specification.

Introduction

  • NetAccess is designed to allow Netcash system data to be presented in real time within a remote application with HTML content
  • It is strongly recommended that the Netcash functions are displayed in a frameless popup window of 560w x 650h (in pixels) within the remote application (the “iFrame”)
  • Integration is initiated from the remote software. The NetAccess within the remote application controls the functions available within the iFrame.
  • Base pages” have been developed to provide customised service offerings to remote payroll and accounting software applications.
  • Permission settings applied to the Netcash account are also applicable to the content of the iFrame

Technical Information

NetAccess is designed to allow Netcash system data to be presented in real time within a remote application with HTML content. This specification is designed for Software vendors to integrate NetAccess functionality from Netcash into a host application. The functions are exposed in an ‘iFrame’ context in the application and the content is managed by Netcash. The HTML Inline Frame Element (<iFrame>) represents a nested browsing context, effectively embedding the Netcash HTML page into your infrastructure. The host application provides the technology as stipulated below -and supplies the initial data to the Netcash system via the NetAccess infrastructure.

It is suggested to open NetAccess in a “frameless pop up window” from your application. The dimensions Netcash has designed the content for is 560w x 650h (pixels).

“Base pages” have been developed to provide customised service offerings to remote software applications.

User permissions

The Netcash user’s website login credentials are used in the execution of the command. All user rights -and permissions are managed by the Netcash online merchant account.
EXAMPLE : If a user can’t authorize a batch online in his Netcash Merchant Account due to rights -and permission restrictions; -he will not have authorization rights via NetAccess in your infrastructure either. All user rights and permission are controlled by the Netcash Merchant Account.
Please refer to NetConnector for details regarding the storage of the Netcash website user credentials for usage in this technical specification.

How to integrate

Access to the production environment

Only active, valid Netcash Accounts can integrate to NetAccess.
Authentication is by means of the login credentials allocated to the Netcash account: –

  1. Username
  2. Password
  3. Pin

Refer to NetConnector for details regarding storage of the Netcash website user credentials for usage in this technical specification.

Note:

The username should not be encrypted.

The NetAccess infrastructure is accessed via a form POST to https://mobi.netcash.co.za/authenticate.aspx

The form post requires two parameters must be present in the form POST body:

  1. key
  2. message

Where the “Key’ is the Netcash username (unencrypted) and the “Message” is an encrypted XML string

Example

<form target="mobiframe" action="https://mobi.netcash.co.za/authenticate.aspx" method="post" id = "mobiform">
  <input type = "hidden" value = "{Netcash username here}" name = "key" />
  <input type = "hidden" value = "{encrypted content here}" name = "message" />
  <input type="submit"/>
</form>
<iframe name=”mobiframe” >
</iframe>

The message parameter is encrypted using a custom Netcash encryption algorithm with the user’s PIN as the encryption key.

Refer to NetConnector for details regarding storage of the Netcash website user credentials for usage in this technical specification.

Process

Before accessing the NetAccess, the user should have all the mandatory data. (Please see the Mandatory Field table)

The host application binds the relevant data to the XML schema and passes it to the Netcash system as an encrypted string.

The schema is generic and caters for all possible data requirements. Only the data specific to the execution of the required action needs to be populated. Unused XML tags may be omitted.

The XML schema and XSD are available online at:

https://mobi.netcash.co.za/documentation/netcashBase.xml
https://mobi.netcash.co.za/documentation/netcashRequest.xml
https://mobi.netcash.co.za/documentation/netcashRequestExample.xml

  • Since the message contains sensitive data, the XML Schema is encrypted using the Netcash Encryption algorithm.
  • The remote application posts the unencrypted ‘Username’ (the Key) and the encrypted XML data (the Message) to the Netcash system.
  • Using the Key, the Netcash system can determine the PIN which is used to decrypt the XML message.
  • Only if the decryption is successful and the login credentials are valid, will the base page be displayed.
  • The user credentials are used to apply the permissions as they are set on the Netcash website.

Notes

User credentials cannot be reset or restored from the iframe. The user has to reset his/her user credentials on the Netcash website https://merchant.netcash.co.za and recapture it into the remote application.
All applications should follow the recommendation to open the NetAccess in a frameless pop up window of the specified size.
The mandatory fields are required every time NetAccess is accessed from the remote application.
The client-specific mandatory fields are required only when NetAccess is accessed from within a Debtor/Creditor/Employee record in the remote application.
The non-mandatory fields are not required, but any data included in these fields will be posted back to the remote application.

Mandatory data

The following data is mandatory for every request:

Mandatory Field Explanation
Yes Username These are the user credentials which will allow access to the Netcash web site
Yes Password These are the user credentials which will allow access to the Netcash web site
Yes PIN These are the user credentials which will allow access to the Netcash web site
Yes Software Vendor Key The key issued by Netcash to identify the software origin of transactions. (only used by Netcash ISV‘s else use the default value: 24ade73c-98cf-47b3-99be-cc7b867b3080)
Yes Base page

The initial web page to which the user will be directed:

  • (0) PayrollServices
  • (1) AccountingServices
  • (2) Employee
  • (3) Debtors
  • (4) Creditors

Where NetAccess is launched from within a client page the following mandatory data must be appended:

Mandatory Field Explanation
Yes Account reference Employee / Debtors / Creditors reference
Yes Identifier Consumer: SA ID number
or
Commercial: Company registration number

Note: The request will fail if no consumer / commercial identifier is not passed

The following fields are non-mandatory but it is recommended that they are included for tracing:

Mandatory Field Explanation
No Extra 1 Any data which is specific to the remote application can be placed in these fields. Each field holds a maximum of 50 characters.
Example: the unique transaction identifier which will allow the application to update a record from data posted back by the Netcash system
No Extra 2 Any data which is specific to the remote application can be placed in these fields. Each field holds a maximum of 50 characters.
Example: the unique transaction identifier which will allow the application to update a record from data posted back by the Netcash system
No Extra 3 Any data which is specific to the remote application can be placed in these fields. Each field holds a maximum of 50 characters.
Example: the unique transaction identifier which will allow the application to update a record from data posted back by the Netcash system

Any data inserted into these fields can be returned in a postback to a supplied URL.

Base pages

The base page is the specific web (landing) page to which the user is directed when NetAccess is opened. The base page is determined by the remote application and is sent in the XML request.

Enumeration index Base page Function
0 PayrollServices All functions activated for the merchant, specifically tailored to Payroll applications.
1 AccountingServices All functions activated for the merchant, specifically tailored to Accounting applications.
2 Employee Functions specific to operations within a selected employee account.
3 Debtors Functions specific to operations within a selected debtor account.
4 Creditors Functions specific to operations within a selected creditor/supplier account.

Encryption Examples

The {Encrypted xml message} in the string uses a custom Netcash encryption algorithm with the user’s PIN as the key.

The username is sent in the clear in order for the Netcash system to locate the PIN for decryption of the data string.

VB.net Example encryption example:

Public Shared Function Encrypt(clearText As String, passKey As String) As String
 Dim clearBytes As Byte() = System.Text.Encoding.Unicode.GetBytes(clearText)
 Dim pdb As New PasswordDeriveBytes(passKey, New Byte(){&H49,&H76,&H61,&H6E,&H20,&H4D,&H65,&H64,&H76,&H65,&H64,&H65,&H76})
 Dim encryptedData As Byte() = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16))
 Dim customreplace As String = Convert.ToBase64String(encryptedData)
    'Ensure Web-Safe
        customreplace = customreplace.Replace("/", "_")
        customreplace = customreplace.Replace("+", "|")
        customreplace = customreplace.Replace("=", "]")
        Return HttpUtility.HtmlEncode(customreplace)
        End Function

Private Shared Function Encrypt(clearData As Byte(), Key As Byte(), IV As Byte()) As Byte()
 Dim ms As New MemoryStream()
 Dim alg As Rijndael = Rijndael.Create()
     alg.Key = Key
     alg.IV = IV
 Dim cs As New CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write)
     cs.Write(clearData, 0, clearData.Length)
     cs.Close()
 Dim encryptedData As Byte() = ms.ToArray()
  Return encryptedData
End Function

.php Example encryption method:

<?php
/*
 * NetcashEncrypt:
 *   Encryption class to be used with NetAccess.
 *
 * Requires the following PHP extensions:
 *   mbstring
 *   mcrypt (deprecated after version PHP 7.1) or openssl
 *
 * Example:
 *   $encryptor = new NetcashEncrypt();
 *   $encryted_data = $encryptor->Encrypt('<use account PIN>', '<data to be encrypted>');
 *
 * Date: 2020/06/04
 */
class NetcashEncrypt
{
    private $salt_priv = [ 0x49, 0x76, 0x61, 0x6E, 0x20, 0x4D, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 ];
    private $base = '';
    private $extra = '';
    private $extracount = 0;
    private $hashno = 0;
    private $state = 0;
    private $iterations = 100;

    private function ResetPBKDF1()
    {
        $base = '';
        $extra = '';
        $extracount = 0;
        $hashno = 0;
        $state = 0;
    }

    public function PBKDF1($passwd, $salt, $cb)
    {
        if ($this->state == 0)
        {
            $this->hashno = 0;
            $this->state = 1;
            $this->base = sha1($passwd . $salt, true);
            for($i = 2; $i < $this->iterations; $i++)
            {
                $this->base = sha1($this->base, true);
            }
        }

        $result = "";

        if ($this->extracount > 0)
        {
            $rlen = strlen($this->extra)-$this->extracount;
            if ($rlen >= $cb)
            {
                $result = substr($this->extra, $this->extracount, $cb);
                if ($rlen > $cb)
                {
                    $this->extracount += $cb;
                }
                else
                {
                    $this->extra=null;
                    $this->extracount=0;
                }
                return $result;
            }
            $result = substr($this->extra, $rlen, $rlen);
        }

        $current = "";
        $clen = 0;
        $remain = $cb - strlen($result);
        while ($remain > $clen)
        {
            if ($this->hashno == 0)
                $current = sha1($this->base, true);
            else if ($this->hashno < 1000)
            {
                $n = sprintf("%d", $this->hashno);
                $current .= sha1($n . $this->base, true);
            }
            $this->hashno++;
            $clen = strlen($current);
        }

        if ($clen>$remain)
        {
            $this->extra = $current;
            $this->extracount = $remain;
        }
        return $result . substr($current, 0, $remain);
    }

    public function Encrypt($pin, $data)
    {
        $this->ResetPBKDF1();
        
        $utf16le_data = mb_convert_encoding($data, 'utf-16le');
        $salt = implode(array_map('chr', $this->salt_priv));
        $key = $this->PBKDF1($pin, $salt, 32);
        $iv = $this->PBKDF1($pin, $salt, 16);

        $encrypted = null;
        if (function_exists('openssl_encrypt'))
        {
            $encrypted = openssl_encrypt($utf16le_data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
        }
        else if (function_exists('mcrypt_encrypt') && function_exists('mcrypt_get_block_size'))
        {
            $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
            $padding = $block - (strlen($utf16le_data) % $block);
            $utf16le_data .= str_repeat(chr($padding), $padding);
            $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $utf16le_data, MCRYPT_MODE_CBC, $iv);
        }
        else
        {
            throw new Exception('No supported encryption extension available');
        }
        
        return 
            htmlspecialchars(str_replace(['/', '+', '='], ['_', '|', ']'], base64_encode($encrypted)));
    }
}
?>

.php Example post example:

include_once('encrypt.php');

$software_vendor_key = 'GUID key here';
$pin = 'your_pin_here'; // The Netcash website pin is used for encryption of the payload. Not to be transmitted.

$username = 'your_username_here'; // The Netcash website username is to be transmitted in the clear for 
                                     Netcash to look up the PIN on server side to decrypt the payload.
$password = 'your_password_here';

// Example of the XML to post.
$xml_to_post = '<?xml version="1.0"?>
<netcashRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <BasePage>2</BasePage>
  <ConsentObtained>Yes</ConsentObtained>
  <CreditAmount>0</CreditAmount>
  <CreditTerms>COD</CreditTerms>
  <AccountReference>netcashTest-1022-3942</AccountReference>
  <Extra1>sfsdfsdfdsfdsf</Extra1>
  <Extra2 />
  <Extra3 />
  <SecurityHeader>
    <SoftwareVendorKey>'.$software_vendor_key.'</SoftwareVendorKey>
    <Username>'.$username.'</Username>
    <Password>'.$password.'</Password>
    <Pin>'. $pin .'</Pin>
  </SecurityHeader>
  <BusinessDetails />
  <ConsumerDetails>
    <Identifier>6001234567890</Identifier>
    <Firstname>Mike</Firstname>
    <Surname>Larry</Surname>
    <DateOfBirth>1960-02-10T00:00:00</DateOfBirth>
  </ConsumerDetails>
  <Address />
  <TradeReferences />
  <Qualifications />
  <ClientsReferences />
  <BankAccounts />
  <LegalDetails />
  <AdditionalInfo />
</netcashRequest>';

// Encrypt the XML using the PIN as the secret key.
$sp = new netcashEncrypt();
$sp->PassKey = $pin;
$encrypted = $sp->Encrypt($xml_to_post);

// Send to iframe as a form post.
// Avoid using GET URL's - firewall/proxy servers tend to block.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://mobi.netcash.co.za/authenticate.aspx');
curl_setopt($ch, CURLOPT_POST, 2);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'key='.$username.'&message='.$encrypted);

$result = curl_exec($ch);
curl_close($ch);

Java Example encryption example:

import java.lang.reflect.Field;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class netcashEncrypt {
    /**
     * Allow AES key longer than 128 bits. 128 is default in java. 256-bit (32-byte) key is used.
     * Either use this solution or install JCE Unlimited Strength Jurisdiction Policy files:
     * http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
     */

    static {
        try {
            Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
            field.setAccessible(true);
            field.set(null, java.lang.Boolean.FALSE);
        } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException ex) {
        }
    }

    /**
     * PasswordDeriveBytes is Microsoft's non-standard (typical) implementation. The method is now
     * deprecated, but unfortunately still in use and not easily switchable against existing clients.
     * PBKDF1 class is implementation that produce the same results as PasswordDeriveBytes for use
     * with Netcash.
     */
    private class PBKDF1 {

        private final String passKey;
        /**
         * Netcash salt
         */
        private final byte[] salt = {0x49, 0x76, 0x61, 0x6E, 0x20, 0x4D, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};

        private byte[] _base;
        private byte[] _extra;
        private int _extracount = 0;
        private int _hashno = 0;
        private int _state = 0;
        private MessageDigest _crypt;

        /**
         * 
         * @param pk PIN
         */
        public PBKDF1(String pk) {
            passKey = pk;
        }

        /**
         * Concatenate 2 byte arrays
         * @param first array of bytes
         * @param second array of bytes
         * @return single byte array contain first byte array, then second
         */
        private byte[] concat(byte[] first, byte[] second) {
            byte[] result = Arrays.copyOf(first, first.length + second.length);
            System.arraycopy(second, 0, result, first.length, second.length);
            return result;
        }

        /**
         * PasswordDerivedBytes GetBytes implementation.
         * @param cb Number of pseudo-random key bytes needed
         * @return byte array of pseudo-random key bytes
         * @throws Exception 
         */
        public byte[] GetBytes(int cb) throws Exception {

            if (_crypt == null)
                _crypt = MessageDigest.getInstance("SHA-1"); // SHA-1 as used by PasswordDeriveBytes

            if (_state == 0) {
                _hashno = 0;
                _state = 1;

                _base = _crypt.digest((passKey + new String(salt)).getBytes("ASCII"));
                for (int i = 2; i < 100; i++) { // Default 100 iterations is done by PassswordDeriveBytes
                    _base = _crypt.digest(_base);
                }
            }

            byte[] result = {};

            if (_extracount > 0) {
                int rlen = _extra.length - _extracount;
                if (rlen >= cb) {
                    result = Arrays.copyOfRange(_extra, _extracount, _extracount + cb);
                    if (rlen > cb) {
                        _extracount += cb;
                    } else {
                        _extra = null;
                        _extracount = 0;
                    }
                    return result;
                }
                result = Arrays.copyOfRange(_extra, rlen, rlen + rlen);
            }

            byte[] current = {};
            int clen = 0;
            int remain = cb - result.length;

            while (remain > clen) {
                if (_hashno == 0) {
                    current = _crypt.digest(_base);
                } else if (_hashno < 1000) {
                    current = concat(current, _crypt.digest(concat(
                            Integer.toString(_hashno).getBytes("ASCII"), _base)));
                }
                _hashno++;
                clen = current.length;
            }

            if (clen > remain) {
                _extra = current;
                _extracount = remain;
            }
            return concat(result, Arrays.copyOfRange(current, 0, remain));
        }
    }

    /**
     * Returns clearText input encrypted using given passKey.
     * @param clearText Plain text to encrypt
     * @param passKey PIN
     * @return Encrypted text
     * @throws Exception 
     */
    public String Encrypt(String clearText, String passKey) throws Exception 
    {
        byte[] clearBytes = clearText.getBytes("UTF-16LE");

        PBKDF1 msPasswordDerivedBytes = new PBKDF1(passKey);

        byte[] key = msPasswordDerivedBytes.GetBytes(32);
        byte[] iv = msPasswordDerivedBytes.GetBytes(16);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
        byte[] encryptedData = cipher.doFinal(clearBytes);
        String customreplace = new String(Base64.getEncoder().encode(encryptedData));

        return customreplace;
    }
}

public class JavaApplication1 {
    public static void main(String[] args) throws Exception {

        String clearText = "some random text";
        String passKey = "1111"; // PIN
        try {
            String encryptedData = new netcashEncrypt().Encrypt(clearText, passKey);
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(JavaApplication1.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

Risk Reports alternate bank name

The Risk Reports XML enum requires a camel-notated bank name in the Bank element. Since this is not acceptable to display on an application, the NetAccess input XML has been extended to include an alternate (user-friendly) bank name.

This allows the software integrator to use either the Risk Reports stipulated bank name or the bank name retrieved from the NIWS_Validation webservice method

The BankAccounts element has been extended to include the new mandatory element AlternateBank. The Bank element will automatically be ignored when data is passed through the AlternateBank element. Conversely, if the AlternateBank element is blank/empty, the Netcash system will use the data contained in the Bank element.

<xs:element name="BankAccounts" nillable="true" minOccurs="0" maxOccurs="1">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="AccountDetails" nillable="false" minOccurs="0" maxOccurs="15">
        <xs:complexType>
          <xs:all>
            <xs:element name="AccountHolder" type="bs:CompanyVal" minOccurs="1" maxOccurs="1" />
            <xs:element name="Bank" type="bs:Banks" minOccurs="1" maxOccurs="1"  />
            <xs:element name="AccountNumber" type="bs:AccountBranchNumber" minOccurs="1" maxOccurs="1" />
            <xs:element name="BranchCode" type="bs:AccountBranchNumber" minOccurs="1" maxOccurs="1"  />
            <xs:element name="AccountType" type="bs :AccountTypes" minOccurs="1" maxOccurs="1" />
            <xs:element name="AlternateBank" type="xs:string" minOccurs="1" maxOccurs="1" />
          </xs:all>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:element>

 

Testing

See Testing section for more details. If you require any integration assistance contact our technical support team

Netcash may provide example/sample/demo ‘code snippets’ and/or external links in this Technical Document. Such are for guidance purposes only and may not function on every developer’s system/s. Netcash disclaims any and all liability for the usage of guidance resources provided -and you as the Developer; must accept full responsibility for the usage of such. While every possible effort has been taken to ensure compatibility across multiple system configurations, the contents of this document cannot be guaranteed to work on all systems, with all operating systems -and/or with all system configuration/s.

Version 2020.1

Copyright © 2020 Netcash (PTY) Ltd