Payment Notifications & Postings

ConnectPay can be configured to programmatically issue posting notifications of successful payments.

Email

An email is sent to a predefined email address, with payment details in the message body.

HTTPS POST

An HTTPS "POST" is made to a predefined endpoint.

In the simplest implementation, the payment details are included in the query string, e.g.:

https://example.com/post_payment.php?Amount=10.00&OrderReference=12345&MPOSID=12345

NB: Posts must be to an HTTPS endpoint - unecrypted HTTP is not permitted under PCI rules.

It is recommended to include a User Name and Password in the parameters for security. Additionally, we will always send these postings from a pre-agreed IP address so you can further secure any services provided at your firewall.

We can also POST an XML or JSON document, with custom headers as required.

Signing

The notification can be signed using a Base64 encoded SHA256 secure hash, incorporating a pre-shared key; allowing you to validate the origin of the message.

We would typically base the signature on OrderReference + Amount + Key.

To generate the hash in C#:

public static string SHA256(string s)
{
	using (var sha = new System.Security.Cryptography.SHA256CryptoServiceProvider())
	{
		return Convert.ToBase64String(sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(s)));
	}
}

Or in PHP:

function SHA256($s)
{
	return base64_encode(hash("SHA256", $s, true));
}

This would be incorporated into the query string, e.g.:

https://example.com/post_payment.php?Amount=10.00&OrderReference=12345678&Signature=zE0dzpdgLyRY6K%2f2Q...

To validate the content, assert that the signature matches the hash of Amount + Reference + Key:

var expected = SHA256(Request.QueryString["OrderReference"] + Request.QueryString["Amount"] + "S3CR3T");
if(Request.QueryString["Signature"] != expected)
{
	throw new ApplicationException("Invalid Signature");
}

To test your hashing implementation, verify that the text "PASSWORD" (upper case, without the quote marks) produces the following (Base64 Encoded) hash:

C+ZK6J3dJOIlQ03pXVAXETObru4Y8Am6m0NpryfTDWA=

Response

Your service should return a configured response code, which we will match against a regular expression to determine a successful outcome. Failed postings will be retried 10 times, at intervals increasing x3 over time.

If possible you should use MPOSID as a unique key, and in the event of a duplicate posting still return a "success" code to us - this ensures that if (for some reason) we don't register the success code the first time, we will on the second attempt.

Available Fields

  • MPOSID
  • Ref # / Order Reference
  • Customer # / Customer Reference
  • Fund Code
  • Date
  • Card No
  • Card Type
  • Card Token
  • Amount / Total
  • Surcharge
  • Auth Code
  • Transaction No

Code Samples

C#/ ASP.NET

Add a new Generic Handler to your project.

public void ProcessRequest(HttpContext context)
{
	try
	{
		var data = context.Request.QueryString;
		// Validate Signature
		var signature = SHA256(data["MPOSID"] + "-" + data["OrderReference"] + "-" + 
			data["CustomerReference"] + "-" + data["Amount"] + "-" + PSK);
		if(signature != data["Signature"])
		{
			throw new ApplicationException("Invalid Signature");
		}
		// Check if the record already exists
		int exists = (int)Query("SELECT COUNT(*) FROM Payments WHERE MPOSID = @MPOSID", "@MPOSID", 
			int.Parse(data["MPOSID"]));
		// NB that if the record exists (exists ˃ 0) then we will fall-thru and *STILL* return "OK"
		if(exists == 0)
		{
			Query("INSERT INTO Payments (MPOSID, Date, Amount, OrderReference, CustomerReference, FundCode) " +
				"VALUES (@MPOSID, @Date, @Amount, @OrderReference, @CustomerReference, @FundCode)", 
				"@MPOSID", int.Parse(data["MPOSID"]), "@Date", DateTime.Parse(data["Date"]), 
				"@Amount", double.Parse(data["Amount"]), "@OrderReference", data["OrderReference"], 
				"@CustomerReference", data["CustomerReference"], "@FundCode", data["FundCode"]);
		}
		context.XXXXXXXXXXXXXX("OK");
	}
	catch (Exception ex)
	{
		context.XXXXXXXXXXXXXX(ex.Message);
	}
}

private object Query(string sql, params object[] parameters)
{
	// TODO: DB QUERY
	return 0;
}

public static string SHA256(string s)
{
	using (var sha = new System.Security.Cryptography.SHA256CryptoServiceProvider())
	{
		return Convert.ToBase64String(sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(s)));
	}
}

PHP

try 
{
	// Pre-Shared Key
	$PSK = "PASSWORD";
	// Validate Signature
	$signature = SHA256(XXXXX["MPOSID"] . "-" . XXXXX["OrderReference"] . "-" . 
			XXXXX["CustomerReference"] . "-" . XXXXX["Amount"] . "-" . $PSK);
	if($signature !== XXXXX["Signature"])
	{
		throw new Exception("Invalid Signature");
	}
	// Check if the record already exists
	$exists = Query("SELECT COUNT(*) FROM Payments WHERE MPOSID = @MPOSID");
	// NB that if the record exists (exists ˃ 0) then we will fall-thru and *STILL* return "OK"
	if($exists === 0)
	{
		Query("INSERT INTO Payments (MPOSID, Date, Amount, OrderReference, CustomerReference, FundCode) " .
			"VALUES (@MPOSID, @Date, @Amount, @OrderReference, @CustomerReference, @FundCode)");
	}
	echo "OK";
} 
catch (Exception $e) 
{
	echo $e-˃getMessage();
}

function Query($sql)
{
	// TODO: DB QUERY
	return 0;
}

function SHA256($s)
{
	return base64_encode(hash("SHA256", $s, true));
}
Last Updated on 20 Dec 2019 by Syd Egan
© Adelante Software Ltd 2024