Skip to main content

IPN code samples

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:

Need help?

Do you have a question? If you didn’t find the answer you are looking for in our documentation, you can contact our Support teams for more information. If you have a technical issue or question, please contact us. We are happy to help.

Not yet a Verifone customer?

We’ll help you choose the right payment solution for your business, wherever you want to sell, in-person or online. Our team of experts will happily discuss your needs.

Verifone logo