ACH Test Accounts, Error Responses, and Status Event Postbacks

Be Advised

The use of live account info in a test environment is strongly discouraged.

Sandbox Testing

The following list of test account numbers are examples of values that can be used during testing.

Test Account Data - Declines for AchSale and AchReturn

Any 9 digit number can be used as the routing number to trigger the decline messages below. Any validly formatted account number that is not listed in the table will trigger an approval.

Account NumberRouting NumberResult Message
0000000000Any 9 digit numberDeclined
1111111111Any 9 digit numberThe transaction contains invalid data or exceeds a limit
2222222222Any 9 digit numberAn upstream error occurred
3333333333Any 9 digit numberThe account number is invalid or failed validation

Test Account Data - Invalid Routing Number for AchSale and AchReturn

Any validly formatted account number, other than those listed in the Declines table above, can be used to trigger the invalid routing number response. Any other 9 digit number used as the routing number will trigger an approval.

Account NumberRouting NumberResult Message
Any validly formatted account number not in the Declines table above000000000The routing number is invalid

Postback ACH Status Events

ACH transaction status events are sent to the postback url configured on your account for receiving transaction responses. However we add the query string messageType=achEvent to the end of your postback url, so you can distinguish the receipt of transaction responses from the receipt of ACH status events.

HMAC Signature Validation

Post-back results should be verified using the HMAC signature found in the request header.

  • one-way SHA-512 message digest
  • encrypted using a secret passphrase you setup with your integration specialist and is then base64 encoded
  • digest is included in the request header as hmac-signature

Verify HMAC signature

  1. Encrypt the request body using the SHA512 hash algorithm and your secret key
  2. Base64 encode the result
  3. Compare the resulting value with the hmac-signature header value

What's a 'secret passphrase'?

Similar to the OID and authToken, the secret passphrase is a unique variable that is tied to each individual merchant account.

You can use these samples to verify the HMAC signature 
on your postback messages.
var express = require("express");
var cors    = require("cors");
var crypto  = require("crypto");

var app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());

app.post("/PostBackListener?messageType=achEvent", (req, res) => {
    var hmacSignature = req.header("hmac-signature");
    var rawData = req.body;
    var jsonData = JSON.stringify(rawData);

    var signatureMatched = false;

    if (hmacSignature) {
        signatureMatched = verifyHmacSignature(hmacSignature, jsonData);
    }

    //if the hmac signature matched, the response body data is valid
    if (signatureMatched) {
        //do something with the transaction result
    }

    res.sendStatus(200);
});

function verifyHmacSignature(hmacSignature, data) {
    //this is the secret pass phrase you supplied to ChargeItPro
    var secretKey = "cipDemoListenerKey";

    var hmac = crypto.createHmac("sha512", secretKey);
    hmac.update(data);
    return hmac.digest("base64") === hmacSignature;
}

console.log("listening on port 5555");
app.listen(5555);
import * as express from 'express';
import * as cors from 'cors';
import * as crypto from 'crypto';

const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());

app.post("/PostBackListener?messageType=achEvent", (req, res) => {
    const hmacSignature = req.header("hmac-signature");
    const rawData = req.body;
    const jsonData = JSON.stringify(rawData);

    let signatureMatched = false;

    if (hmacSignature) {
        signatureMatched = verifyHmacSignature(hmacSignature, jsonData);
    }

    //if the hmac signature matched, the response body data is valid
    if (signatureMatched) {
        //do something with the transaction result
    }

    res.sendStatus(200);
});

function verifyHmacSignature(hmacSignature: string, data: string): boolean {
    //this is the secret pass phrase you supplied to ChargeItPro
    const secretKey = "cipDemoListenerKey";

    const hmac = crypto.createHmac("sha512", secretKey);
    hmac.update(data);
    return hmac.digest("base64") === hmacSignature;
}

console.log("listening on port 5555");
app.listen(5555);
<?php

$headers = getallheaders();
$body = file_get_contents("php://input");
$jsonData = json_encode(json_decode($body));

$signatureMatched = false;

if (array_key_exists("hmac-signature", $headers)) {
    $hmacSignature = $headers["hmac-signature"];

    $signatureMatched = verifyHmacSignature($hmacSignature, $jsonData);
}

// if the hmac signature matched, the response body data is valid
if ($signatureMatched) {
    // do something with the transaction result
}

function verifyHmacSignature(string $hmacSignature, string $data) {
    // this is the secret pass phrase you supplied to ChargeItPro
    $secretKey = "yourSecretPassPhrase";

    $hmac = hash_hmac("sha512", $data, $secretKey, true);

    return base64_encode($hmac) == $hmacSignature;
}
[HttpPost]
public void PostBackListener()
{
  var reader = new StreamReader(Request.Body);
  var bodyContents = reader.ReadToEnd();
  var transactionResult = JsonConvert.DeserializeObject(bodyContents);
  var rawData = JsonConvert.DeserializeObject(bodyContents);
  var jsonData = JsonConvert.SerializeObject(rawData);

  //verify the hmac signature with our secret pass phrase
  bool hmacSignatureExists = Request.Headers.TryGetValue("hmac-signature", out var hmacSignature);
  bool signatureMatched = false;

  if (hmacSignatureExists)
  {
    string signature = hmacSignature.ToString();
    byte[] data = Encoding.UTF8.GetBytes(jsonData);
    signatureMatched = VerifyHmacSignature(signature, data);
  }

  //if the hmac signature matched, the response body data is valid
  if (signatureMatched)
  {
        //do something with the transaction result
  }
}

private bool VerifyHmacSignature(string hmacSignature, byte[] data)
{
  //this is the secret pass phrase you supplied to ChargeItPro
  const string secretKey = "yourSecretPassPhrase";

  using (HMACSHA512 hmac = new HMACSHA512(Encoding.UTF8.GetBytes(secretKey)))
  {
    byte[] computedHash = hmac.ComputeHash(data);
    string computedSignature = Convert.ToBase64String(computedHash);
    return hmacSignature == computedSignature;
  }
}

Status Event Body

The message body of the postback request will contain a JSON object with a top level property called eventData that contains an object with the following properties.

NameDetailsDescription
eventIdType: numberUnique id for the event
eventDateTimeType: numberEpoch timestamp for the event
eventTypeDescriptionType: stringDescription of the event
transactionStatusType: stringCurrent status of the transaction
settlementStatusType: stringCurrent settlement status of the transaction
returnCodeType: stringA descripion of the returned status of the transaction. NOTE: If this value is not null, the ACH transaction has been returned, and this event is actionable.
externalTransactionIdType: stringExternal transaction id submitted with original transaction, included for identification purposes
uniqueTransIdType: stringUnique transaction id returned in original transaction response, included for identification purposes