Programmers guide
To start, please refer our programmer’s guide for more detail on how to apply the required methodology.
Introduction
Sage Connected Services is used by Software vendors to integrate Sage Pay 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 Sage Connected Services in the host application. “Base pages” have been developed to provide customized service offerings for payroll and accounting software.
Testing
Please note that testing with live transactional data will incur charges. Please speak to your Sage Pay account manager for more details.
Technical Information
This specification is designed for Software vendors to integrate Sage Connected Services functionality from Sage Pay into a host application. The functions are exposed in an ‘iFrame’ context in the application and the content is managed by Sage Pay. The HTML Inline Frame Element (<iframe>) represents a nested browsing context, effectively embedding the Sage Pay HTML page into your infrastructure. The host application provides the technology as stipulated below -and supplies the initial data to the Sage Pay system via the Sage Connected Services infrastructure.
It is suggested to open Sage Connected Services in a “frameless pop up window” from your application. The dimensions Sage Pay has designed the content for is 560w x 650h (pixels).
User permissions
The Sage Pay user’s website login credentials are used in the execution of the command. All user rights -and permissions are managed by the Sage Pay online merchant account. EXAMPLE : If a user can’t authorise a batch online in his Sage Pay Merchant Account due to rights -and permisison restrictions; -he will not have authorization rights via Sage Connected Services in your infrastructure either.
All user rights and permission are controlled by the Sage Pay Merchant Account. Please refer to Sage Connect for details regarding storage of the Sage Pay website user credentials for usage in this technical specification.
Acces to the production environment
The Sage Connected Services infrastructure is accessed via a form POST to https://mobi.sagepay.co.za/authenticate.aspx?key={SagePayUsername}&message={Encrypted xml message}
The username and serialised, encrypted XML string is appended to the URL when accessing Sage Connected Services. Please refer to Sage Connect for details regarding storage of the Sage Pay website user credentials for usage in this technical specification.
Encryption
The {Encrypted xml message} in the string uses a custom Sage Pay encryption algorithm with the user’s PIN as the key.
The username is sent in the clear in order for the Sage Pay system to locate the PIN for decryption of the data string.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
class SagePayEncrypt { public $PassKey; private $salt = [ 0x49, 0x76, 0x61, 0x6E, 0x20, 0x4D, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 ]; private function PBKDF1($cb) { static $base; static $extra; static $extracount = 0; static $hashno; static $state = 0; if ($state == 0) { $hashno = 0; $state = 1; $base = sha1($this->PassKey . implode(array_map('chr', $this->salt)), true); for($i = 2; $i < 100; $i++) { $base = sha1($base, true); } } $result = ""; if ($extracount > 0) { $rlen = strlen($extra)-$extracount; if ($rlen >= $cb) { $result = substr($extra, $extracount, $cb); if ($rlen > $cb) { $extracount += $cb; } else { $extra=null; $extracount=0; } return $result; } $result = substr($extra, $rlen, $rlen); } $current = ""; $clen = 0; $remain = $cb - strlen($result); while ($remain > $clen) { if ($hashno == 0) $current = sha1($base, true); else if ($hashno < 1000) { $n = sprintf("%d", $hashno); $current .= sha1($n . $base, true); } $hashno++; $clen = strlen($current); } if ($clen>$remain) { $extra = $current; $extracount = $remain; } return $result . substr($current, 0, $remain); } private function rijndael_encrypt($key, $iv, $data) { $utf16le_data = mb_convert_encoding($data, 'utf-16le'); $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $padding = $block - (strlen($utf16le_data) % $block); $utf16le_data .= str_repeat(chr($padding), $padding); return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $utf16le_data, MCRYPT_MODE_CBC, $iv); } public function Encrypt($clearText) { return htmlspecialchars( str_replace(['/', '+', '='], ['_', '|', ']'], base64_encode($this->rijndael_encrypt($this->PBKDF1(32), $this->PBKDF1(16), $clearText)))); } } // TEST $value = 'content here'; $secret_key = 'passKey'; $sp = new SagePayEncrypt(); $sp->PassKey = $secret_key; $encrypted = $sp->Encrypt($value); echo $encrypted; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
include_once('encrypt.php'); $software_vendor_key = 'GUID key here'; $pin = 'your_pin_here'; // The Sage Pay website pin is used for encryption of the payload. Not to be transmitted. $username = 'your_username_here'; // The Sage Pay website username is to be transmitted in the clear for Sage Pay 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"?> <SagePayRequest 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>SagePayTest-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 /> </SagePayRequest>'; // Encrypt the XML using the PIN as the secret key. $sp = new SagePayEncrypt(); $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.sagepay.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); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
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 SagePayEncrypt { /** * 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 Sage Pay. */ private class PBKDF1 { private final String passKey; /** * Sage Pay 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 SagePayEncrypt().Encrypt(clearText, passKey); } catch (UnsupportedEncodingException ex) { Logger.getLogger(JavaApplication1.class.getName()).log(Level.SEVERE, null, ex); } } } |
Authentication data
Each user accessing the Sage Connected Services infrastructure needs to be a registered user on the Sage Pay web site.
Authentication is by means of:
- Username
- Password
- PIN
These user login details should be securely stored in the host application in a non-readable/encrypted format and updated when the user changes login details on the Sage Pay web site via Administrator login in the host system.
Process
The host application binds the relevant data to the XML schema and passes it to the Sage Pay 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.sagepay.co.za/documentation/SagePayBase.xml
https://mobi.sagepay.co.za/documentation/SagePayRequest.xml
https://mobi.sagepay.co.za/documentation/SagePayRequestExample.xml
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 Sage Pay web site |
Yes | Password | These are the user credentials which will allow access to the Sage Pay web site |
Yes | PIN | These are the user credentials which will allow access to the Sage Pay web site |
Yes | Software Vendor Key | The key issued by Sage Pay to identify the software origin of transactions. (only used by Sage Pay business partners else use default value: 24ade73c-98cf-47b3-99be-cc7b867b3080) |
Yes | Base page | The initial web page to which the user will be directed:
|
Where Sage Connected Services 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 Commercial: Company registration number |
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 Sage Pay 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 Sage Pay 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 Sage Pay system |
Any data inserted into these fields can be returned in a postback to a supplied URL.
Sage Connected Services Infrastructure/Control:
- Login details for the Sage Connected Services infrastructure in Sage Connect is encrypted in Sage Connect and/or the application database. A restore/password reminder service for these credentials must not be made available to the user. The user can reset his/her login details on the Sage Pay website and re-enter them in Sage Connected Services and/or the application database via the Administrator login from the host system.
- All applications follow the Sage Pastel Evolution methodology in opening Sage Connected Services in a “frameless pop up window” / iFrame window from the respective application. The dimensions Sage Pay has designed the content for is 560 x 560 (pixels).
Base pages
The base page is the specific web page to which the user is directed when Sage Connected Services 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. |
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 Sage Connected Services 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 Sage Pay system will use the data contained in the Bank element.

View change log