IPN code samples
Last updated: 29-Jul-2024
Rate this article:
Use the code samples below to verify the validity of the IPN trigger.
PHP
<?php
/* Instant Payment Notification */
/* Return HTTP 200 response for initial IPN endpoint setup */
if ($_SERVER['REQUEST_METHOD'] === 'GET'){
http_response_code(200);
return;
}
/*
* possible values: sha256, sha3-256
* sha3-256 only for php version > 7.1
*/
$usedHashAlgorithm = 'sha256';
/* pass to compute HASH. Retrieve your secret key by accessing https://secure.2checkout.com/cpanel/webhooks_api.php */
$secretKey = "AABBCCDDEEFF";
function serializeArray($array) {
$ret_value = "";
foreach ($array as $key => $val) {
/* skip signature hashes from computed hash */
if (in_array($key , ['HASH', 'SIGNATURE_SHA2_256', 'SIGNATURE_SHA3_256'], true)) {
continue;
}
if (is_array($val)) {
$ret_value .= serializeArray($val);
} else {
$ret_value .= strlen($val) . $val;
}
}
return $ret_value;
}
$signature_sha2 = isset($_POST["SIGNATURE_SHA2_256"]) ? $_POST["SIGNATURE_SHA2_256"] : ''; /* sha256 HASH received */
$signature_sha3 = isset($_POST["SIGNATURE_SHA3_256"]) ? $_POST["SIGNATURE_SHA3_256"] : ''; /* sha3-256 HASH received */
$stringForHash = serializeArray($_POST);
$computedHash = hash_hmac($usedHashAlgorithm, $stringForHash, $secretKey);
$validHash = false;
switch ($usedHashAlgorithm) {
case "sha256":
if ($computedHash == $signature_sha2) {
$validHash = true;
}
break;
case "sha3-256":
if ($computedHash == $signature_sha3) {
$validHash = true;
}
break;
}
if ($validHash === false) {
/* hash verification failed */
http_response_code(400);
mail("your_address@example.com","BAD IPN Signature", print_r($_POST, TRUE),"");
return;
}
// hash is valid. We proceed with success response
$responseDate = date('YmdHis');
$arrayForResponseHash = [
$_POST["IPN_PID"][0],
$_POST["IPN_PNAME"][0],
$_POST["IPN_DATE"],
$responseDate
];
$stringForResponseHash = serializeArray($arrayForResponseHash);
$responseString = '';
switch ($usedHashAlgorithm) {
case "sha256":
$responseHash = hash_hmac('sha256', $stringForResponseHash, $secretKey);
$responseString = '<sig algo="sha256" date="' . $responseDate . '">' . $responseHash . '</sig>';
break;
case "sha3-256":
$responseHash = hash_hmac('sha3-256', $stringForResponseHash, $secretKey);
$responseString = '<sig algo="sha3-256" date="' . $responseDate . '">' . $responseHash . '</sig>';
break;
}
http_response_code(200);
echo $responseString;
Java
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
@RestController
@RequestMapping("/ipn")
public class IpnController {
// retrieve your secret key by accessing https://secure.2checkout.com/cpanel/webhooks_api.php
private String secretKey = "yoursecretkey";
// sha256; for sha3-256, use HmacSHA3-256
private String algorithm = "HmacSHA256";
// sha256; for sha3-256, use SIGNATURE_SHA3_256
private String signatureField = "SIGNATURE_SHA2_256";
// sha256; for sha3-256, use sha3-256
private String algoResponseString = "sha256";
@RequestMapping(method = RequestMethod.POST)
public String handleRequest(HttpServletRequest request) {
Map<String, String[]> parameters = request.getParameterMap();
String receivedRequestSignature = request.getParameter(signatureField);
String calculatedRequestSignature = calculateRequestSignature(parameters);
String responseDate = getResponseDate();
String responseSignature = calculateResponseSignature(request, parameters, responseDate);
if(calculatedRequestSignature.equals(receivedRequestSignature)) {
String response = "Verified OK! <sig algo=\"" + algoResponseString + "\" date=\"" + responseDate + "\">" + responseSignature + "</sig>";
handleIpn(parameters);
return response;
} else {
// LOG OR EMAIL THIS OCCURRENCE
return "Bad request!";
}
}
private String calculateResponseSignature(HttpServletRequest request, Map<String, String[]> parameters, String responseDate) {
String responseStringToSign = parameters.get("IPN_PID")[0].length() + parameters.get("IPN_PID")[0] +
parameters.get("IPN_PNAME")[0].length() + parameters.get("IPN_PNAME")[0] +
request.getParameter("IPN_DATE").length() + request.getParameter("IPN_DATE") +
responseDate.length() + responseDate;
return hmacSha(responseStringToSign);
}
private String getResponseDate() {
String pattern = "yyyyMMddHHmmss";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
return simpleDateFormat.format(new Date());
}
private String calculateRequestSignature(Map<String, String[]> parameters) {
String requestStringToVerify = "";
for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
if (List.of("HASH", "SIGNATURE_SHA2_256", "SIGNATURE_SHA3_256").contains(entry.getKey())) continue;
// convert parameter values array to ArrayList to be able to use streams
ArrayList<String> valueList = new ArrayList<String>(Arrays.asList(entry.getValue()));
// append to request string each length and value
requestStringToVerify = valueList
.stream()
.reduce(requestStringToVerify,
(partialResult, value)
-> partialResult + (value == null ? 0 : value.getBytes().length + value));
}
return hmacSha(requestStringToVerify);
}
private void handleIpn(Map<String, String[]> parameters) {
// INSERT YOUR HANDLING CODE HERE
}
private String hmacSha(String stringForHash) {
SecretKeySpec key = new SecretKeySpec((secretKey).getBytes(), algorithm);
try {
Mac mac = Mac.getInstance(algorithm);
mac.init(key);
byte[] bytes = mac.doFinal(stringForHash.getBytes());
return toHexString(bytes);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
return null;
}
}
private String toHexString(byte[] bytes) {
Formatter form = new Formatter();
String result;
for (byte b : bytes) {
form.format("%02x", b);
}
result = form.toString();
form.close();
return result;
}
}
Perl
#!/usr/bin/perl
# Simple IPN implementation for 2Checkout
# PERL version
use strict;
use CGI;
use Digest::SHA qw(hmac_sha256_hex);
# modify your settings here
# pass to compute HASH. Retrieve your secret key by accessing https://secure.2checkout.com/cpanel/webhooks_api.php
my $myKey='AABBCCDDEEFF';
# / end modifications area
my $q = new CGI;
my @name = $q->param;
print "Content-type: text/html\n\n";
my $in_sha256='';
my $first_product = '';
my $first_pid = '';
# Main loop
foreach my $par (@name) {
next if ($par=~/HASH|SIGNATURE_SHA2_256|SIGNATURE_SHA3_256/i);
if ($q->param($par) ne '') { #parameter not empty
my @myList = $q->param($par);
if (scalar(@myList)) { #multi valued
foreach my $el (@myList) {
$in_sha256.=length($el).$el;
if ($par eq "IPN_PNAME[]") { $first_product = $myList[0]; }
if ($par eq "IPN_PID[]") { $first_pid = $myList[0]; }
}
} else { #single valued
$in_sha256.=length($q->param($par)).$q->param($par);
}
} else { #empty parameter
$in_sha256.='0';
}
}
my $mySha = $q->param('SIGNATURE_SHA2_256');
my $shaVerify = hmac_sha256_hex($in_sha256, $myKey);
my $response='';
if ($mySha=~/^$shaVerify$/i) { # ok
# The notification is genuine / verified
# Write your own code here to insert the data into a database or email it in a specified format
# The code below will confirm the request
$response = length($first_pid).$first_pid.length($first_product).$first_product.length($q->param("IPN_DATE")).$q->param("IPN_DATE");
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
my $now = (1900+$year).sprintf("%02d", $mon+1).sprintf("%02d", $mday).sprintf("%02d", $hour).sprintf("%02d", $min).sprintf("%02d", $sec);
$response.=length($now).$now;
my $shaResponse = hmac_sha256_hex($response, $myKey);
print '<sig algo="sha256" date="' . $now . '">' . $shaResponse . '</sig>';
print "\n";
} else { # not ok
print '<EPAYMENT>Error. Cannot verify signature.</EPAYMENT>';
print "\n";
}
C#
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace VerifoneIPNSample
{
public class IpnSignatureHandler
{
public string calculateSignature(
IEnumerable<KeyValuePair<string, string>> requestPayloadValues,
string secretKey,
string currentDate
)
{
int size = 0;
string hash = string.Empty;
UTF8Encoding encoding = new UTF8Encoding();
System.Text.StringBuilder PlainDataToHash = new System.Text.StringBuilder();
var res = requestPayloadValues.GetEnumerator();
while (res.MoveNext())
{
switch (res.Current.Key)
{
case "IPN_PID[]":
size = res.Current.Value.Length;
PlainDataToHash.Append(size);
PlainDataToHash.Append(res.Current.Value);
break;
case "IPN_PNAME[]":
size = res.Current.Value.Length;
PlainDataToHash.Append(size);
PlainDataToHash.Append(res.Current.Value);
break;
case "IPN_DATE":
size = res.Current.Value.Length;
PlainDataToHash.Append(size);
PlainDataToHash.Append(res.Current.Value);
break;
}
}
size = currentDate.Length;
PlainDataToHash.Append(size);
PlainDataToHash.Append(currentDate);
Console.WriteLine(PlainDataToHash);
string pass = secretKey;
byte[] passBytes = encoding.GetBytes(pass);
// sha256; for sha3-256, replace HMACSHA256 with HMACSHA3_256
HMACSHA256 hmacSha = new HMACSHA256(passBytes);
string HashData = PlainDataToHash.ToString();
byte[] baseStringForHashBytes = encoding.GetBytes(HashData);
byte[] hashBytes = hmacSha.ComputeHash(baseStringForHashBytes);
hash = ByteToString(hashBytes);
return hash;
}
private static string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("x2"); // hex format
}
return (sbinary);
}
public string generateTag(
IEnumerable<KeyValuePair<string, string>> requestPayloadValues,
string secretKey
)
{
var now = DateTime.Now.ToString("yyyyMMddHHmmss");
var responseHash = calculateSignature(requestPayloadValues, secretKey, now);
// sha256; for sha3-256, replace algo=\"sha256\" with algo=\"sha3-256\"
return $"<sig algo=\"sha256\" date=\"{now}\">{responseHash}</sig>";
}
}
}
C# - IPN Signature Handler Test Example
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace VerifoneIPNSample.Tests
{
[TestClass()]
public class IpnSignatureHandlerTests
{
[TestMethod()]
public void TestCalculateSignature() {
List<KeyValuePair<string, string>> requestPayloadValues = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("IPN_PID[]", "1"),
new KeyValuePair<string, string>("IPN_PNAME[]", "Software program"),
new KeyValuePair<string, string>("IPN_DATE", "20050303123434"),
};
string testedDate = "20050303123434";
string secretKey = "test";
IpnSignatureHandler ipnHandler = new IpnSignatureHandler();
var responseHash = ipnHandler.calculateSignature(requestPayloadValues, secretKey, testedDate);
Console.WriteLine(responseHash);
// sha256; for sha3-256, compare with "2726fe88a5b2bf7907034053124964eb614b46b929acc804034f7071121a4efb"
Assert.AreEqual(responseHash, "7efee0c4b7b129f9b96ab279366c1ded92578d947596fb639060d1796ac431b3");
}
}
}
Rate this article: