Validating HTTP Requests

We sign all HTTP requests with an HMAC-MD5 hash, using the request’s query string and your account’s notification key.

We do not require you to validate any data we send you, but if you wish to validate a request, generate an HMAC-MD5 hash using the request’s query string and your notification key as the two variables in your scripting language’s HMAC MD5 function.

IP Range

TrialPay’s requests will originate from the following IP address range: 54.183.233.157, 54.183.231.95, 70.42.249.1 – 70.42.249.255, and 199.68.156.0 – 199.68.159.255.

Query String

For GET requests, the query string is everything that follows the question mark in the request’s URL.

For POST requests and POST requests formatted in XML, the query string is the entire POST body.

Sample Validation Code

PHP

If your version of PHP has a built-in HMAC-MD5 hashing function, use this code:

<?
// TrialPay provides this signature for the message
// Note: The actual HTTP header is "TrialPay-HMAC-MD5",
// but PHP renames all HTTP headers so we end up with:
$message_signature = $_SERVER['HTTP_TRIALPAY_HMAC_MD5'];

// Recalculate the signature locally
$key = '[YOUR MERCHANT KEY]';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
 // the following is for POST notification
 if (empty($HTTP_RAW_POST_DATA)) {
 $recalculated_message_signature = hash_hmac('md5', file_get_contents('php://input'), $key);
 } else {
 $recalculated_message_signature = hash_hmac('md5', $HTTP_RAW_POST_DATA, $key);
 }

} else {
 // the following is for GET notification
 $recalculated_message_signature = hash_hmac('md5', $_SERVER['QUERY_STRING'], $key);

}

if ($message_signature == $recalculated_message_signature) {
 // the message is authentic
} else {
 // the message is not authentic
}

If your version of PHP does not have a built-in HMAC-MD5 hashing function, use this code instead:

/**
 * Implementation of HMAC-MD5.
 * @param string $algo this must be 'md5'. Only 'md5' is supported.
 * @param string $message the message.
 * @param string $secret_key Your merchant key
 * @return the same format as md5 - a string of 32 lowercase hex characters
 */
function hash_hmac($algo='md5', $message, $secret_key) {
 $block_size_md5 = 64; // md5 block size is 64 bytes = 512 bits
 $opad = str_pad('', $block_size_md5, chr(0x5c));
 $ipad = str_pad('', $block_size_md5, chr(0x36));
 if (strlen($secret_key) > $block_size_md5) {
 $secret_key = md5($secret_key, true /*raw binary*/); // $secret_key is now 16 bytes long
 }

 $secret_key = str_pad($secret_key, $block_size_md5, chr(0x00));

 $ipad = $secret_key ^ $ipad; 

 $opad = $secret_key ^ $opad;

 return md5($opad . md5($ipad . $message, true /*raw binary*/));
}

ASP .NET/C#

using System;
using System.Web.UI;
using System.Security.Cryptography;
using System.Text;

public partial class MyTrialPayHandler : System.Web.UI.Page {
 protected void Page_Load( object sender, EventArgs e ) {
  string merchantKey = "[YOUR MERCHANT KEY]";
  byte[] merchantKeyBytes = Encoding.ASCII.GetBytes( merchantKey );
  byte[] queryStringBytes = Encoding.ASCII.GetBytes( Request.QueryString.ToString() );
  HMACMD5 md5 = new HMACMD5( merchantKeyBytes );
  // Compute the hash of the supplied query string:
  byte[] md5Hash = md5.ComputeHash( queryStringBytes );

  // Convert the hash into a hexadecimal string for comparison:
  StringBuilder sb = new StringBuilder();
  for ( int i = 0; i < md5Hash.Length; i++ ) {
  sb.Append( md5Hash[i].ToString( "x2" ) ); // "x2" means lowercase hexadecimal
  }

  // Compare the two strings:
  bool success = ( sb.ToString() == Request.Headers["TrialPay-HMAC-MD5"].ToLower() );

  // For testing purposes only:
  if ( success )
  Response.Write( "Success" );
  else
  Response.Write( "Fail" );

  // Add your own custom logic here to generate and return your unlock code.
  // You can also add IP address checks for added security using Request.UserHostAddress
 }

 protected override void Render( HtmlTextWriter writer ) {
  // Override the default rendering so only the unlock code is returned to TrialPay.
  // Not strictly necessary, but keeps things cleaner.

  //base.Render( writer );
 }
}

Perl (using Catalyst framework)

# In your controller

package My::App::Controller;

use strict;
use warnings;
use parent 'Catalyst::Controller';

use Digest::HMAC_MD5;

sub trialpay_setup : PathPart('trialpay') Chained('/') : CaptureArgs(0) {
 my ( $self, $c ) = @_;

 my $notification_key = '[YOUR MERCHANT KEY]';

 if ( ! $c->req->header('TrialPay-HMAC-MD5')) {

 $c->log->warn("Invalid call");

 $c->detach('/deny');
 }

 if (Digest::HMAC_MD5->new($notification_key)->add($c->req->uri->query)->hexdigest ne
 $c->req->header('TrialPay-HMAC-MD5')) {

 $c->log->fatal("TrialPay-HMAC-MD5 checksum failure!");

 $c->detach('/deny');
 }

 # do other set-up related things here
 #

}

sub order : PathPart('order') Chained('trialpay_setup') : Args(0) {
 my ( $self, $c ) = @_;

 # do something with the order notification
 #

}

1;

Java 2 Platform SE 5.0/JSP

import java.io.*;
import javax.servlet.*;
import javax.crypto.*;
import org.apache.commons.codec.binary.Hex; // Commons Codec 1.4 API

public static boolean validate(HttpServletRequest request) {
 try
 {
   String expectedHash = request.getHeader("TrialPay-HMAC-MD5").toLowerCase();  
   String notificationKey = "[YOUR MERCHANT KEY]";

   SecretKeySpec secretKeySpec =
     new SecretKeySpec(notificationKey.getBytes("UTF-8"), "hmacMD5");
   Mac mac = Mac.getInstance(secretKeySpec.getAlgorithm());
   mac.init(secretKeySpec);

   if (request.getMethod().equals("POST")) {
     // Use the following for POST notification
     Reader in = request.getReader();
     StringBuffer body = new StringBuffer();
     char[] buffer = new char[1024];
     int chars = 0;
     while((chars = in.read(buffer)) != -1) {
     body.append(buffer, 0, chars);
     }
     in.close();
     String test = body.toString();

   } else {
   String test = request.getQueryString(); // for GET notification
   }

   byte[] hashBytes = mac.doFinal(test.getBytes("UTF-8"));
   String actualHash = Hex.encodeHexString(hashBytes);

   return (actualHash.equals(expectedHash)); // returns true if validation is successful.

 }
 catch(Throwable t)
 {
 // Log this
 }
 return false; // validation has failed
}

Python/Google App Engine

class MyHandler(webapp.RequestHandler):
  def post(self):
    key = '[YOUR MERCHANT KEY]'
    tphash = self.request.headers['TrialPay-HMAC-MD5']
    if hmacmd5(key,self.request.body) != tphash:
      logging.info('invalid trialpay hash')
      return 

 # Add your own custom logic here to generate and return your unlock code.
 # You can also add IP address checks for added security

VB Script

Please refer to Microsoft’s page for HMAC-MD5 using VB Script.