collapse logo
expand logo
Developer Portal
Get Started Card Payments Check Payments Recurring Payments Gift & Loyalty Payments Fraud Plugins & Integrations Reporting Demo Semi Integrated Fully Integrated Terminals POS Middleware Connectors Portico Gateway Documentation API Overview Gift & Loyalty Bill Pay Payroll Introduction Developer Support Authentication Credit Card Gift Card eCheck & ACH Recurring Reporting Error Handling Semi-Integrated Devices Global Payments eCommerce Tokenization Demo Testing Knowledge Center GitHub Partnerships
Sign-In Get sandbox account
Sign-In Get sandbox account

eCheck & ACH Payments

While accepting credit/debit cards could cover most of your payment acceptance needs, allowing your customers to pay by check my be preferred by your customers in certain situations, business-to-business transactions being one such possible situation.

Accept ACH/eCheck Information

As with any payment form, your integration will need to gather enough information depending on your merchant account’s configuration. The below is a good baseline for ACH/eCheck transactions with Heartland ACH, but the required fields may vary for Sage/GETI.

Basic HTML Payment Form

<form id="payment_form" method="post" action="/process">

   <!--  account fields -->

   <label for="account_number">Account Number</label>
   <input id="account_number" name="account_number" type="tel">

   <label for="routing_number">Routing Number</label>
   <input id="routing_number" name="routing_number" type="tel">

   <label for="account_type">Account Type</label>
   <select id="account_type" name="account_type">
     <option value="CHECKING">Checking</option>
     <option value="SAVINGS">Saving</option>
   </select>

   <label for="check_type">Check Type</label>
   <select id="check_type" name="check_type">
     <option value="PERSONAL">Personal</option>
     <option value="BUSINESS">Business</option>
   </select>

   <!-- address fields -->

   <label for="check_holder_name">check_holder_name</label>
   <input id="check_holder_name" name="check_holder_name" type="text">

   <label for="billing_street_address_1">billing_street_address_1</label>
   <input id="billing_street_address_1" name="billing_street_address_1" type="text">

   <label for="billing_city">billing_city</label>
   <input id="billing_city" name="billing_city" type="text">

   <label for="billing_state">billing_state</label>
   <input id="billing_state" name="billing_state" type="text">

   <label for="billing_zip">billing_zip</label>
   <input id="billing_zip" name="billing_zip" type="text">

</form>

Charge the Account

Once the consumer has entered their ACH/eCheck information and submitted the payment form, the next step is to charge the account. Let’s configure the server-side SDK and gather our data together in preparation.

Configure authentication

using GlobalPayments.Api.Services;

ServicesContainer.ConfigureService(new PorticoConfig {
   SecretApiKey = "skapi_cert_MTyMAQBiHVEAewvIzXVFcmUd2UcyBge_eCpaASUp0A",
   DeveloperId = "000000",
   VersionNumber = "0000",
   ServiceUrl = "https://cert.api2.heartlandportico.com"
});
<?php
use GlobalPayments\Api\ServicesConfig;
use GlobalPayments\Api\ServicesContainer;

$config = new ServicesConfig();
$config->secretApiKey = "skapi_cert_MTyMAQBiHVEAewvIzXVFcmUd2UcyBge_eCpaASUp0A";
$config->developerId = "000000";
$config->versionNumber = "0000";
$config->serviceUrl = "https://cert.api2.heartlandportico.com";
ServicesContainer::configure($config);
import com.global.api.ServicesContainer;
import com.global.api.serviceConfigs.GatewayConfig;

GatewayConfig config = new GatewayConfig();
config.setSecretApiKey("skapi_cert_MTyMAQBiHVEAewvIzXVFcmUd2UcyBge_eCpaASUp0A");
config.setDeveloperId("000000");
config.setVersionNumber("0000");
config.setServiceUrl("https://cert.api2.heartlandportico.com");
ServicesContainer.configureService(config);
# coming soon
from globalpayments.api import ServicesConfig, ServicesContainer

config = ServicesConfig()
config.secret_api_key = 'skapi_cert_MTyMAQBiHVEAewvIzXVFcmUd2UcyBge_eCpaASUp0A>'
config.developer_id = '000000'
config.version_number = '0000'
config.service_url = 'https://cert.api2.heartlandportico.com'
ServicesContainer.configure(config)
import { ServicesConfig, ServicesContainer } from "globalpayments-api";

const config = new ServicesConfig();
config.secretApiKey = "skapi_cert_MTyMAQBiHVEAewvIzXVFcmUd2UcyBge_eCpaASUp0A";
config.developerId = "000000";
config.versionNumber = "0000";
config.serviceUrl = "https://cert.api2.heartlandportico.com";
ServicesContainer.configure(config);

Prepare to charge an ACH/eCheck account

var check = new eCheck {
   AccountNumber = GetRequestParam("account_number"),
   RoutingNumber = GetRequestParam("routing_number"),
   AccountType = GetRequestAccountTypeParam("account_type"),
   CheckType = GetRequestCheckTypeParam("check_type"),
   CheckHolderName = GetRequestParam("check_holder_name")
};

var address new Address {
   StreetAddress1 = GetRequestParam("billing_street_address_1"),
   City = GetRequestParam("billing_city"),
   State = GetRequestParam("billing_state"),
   PostalCode = GetRequestParam("billing_zip")
};
<?php
$check = new eCheck();
$check->accountNumber = getRequestParam("account_number");
$check->routingNumber = getRequestParam("routing_number");
$check->accountType = getRequestAccountTypeParam("account_type");
$check->checkType = getRequestCheckTypeParam("check_type");
$check->checkHolderName = getRequestParam("check_holder_name");

$address = new Address();
$address->streetAddress1 = getRequestParam("billing_street_address_1");
$address->city = getRequestParam("billing_city");
$address->state = getRequestParam("billing_state");
$address->postalCode = getRequestParam("billing_zip");
eCheck check = new eCheck();
check.setAccountNumber(getRequestParam("account_number"));
check.setRoutingNumber(getRequestParam("routing_number"));
check.setAccountType(getRequestAccountTypeParam("account_type"));
check.setCheckType(getRequestCheckTypeParam("check_type"));
check.setCheckHolderName(getRequestParam("check_holder_name"));

Address address = new Address();
address.setStreetAddress1(getRequestParam("billing_street_address_1"));
address.setCity(getRequestParam("billing_city"));
address.setState(getRequestParam("billing_state"));
address.setPostalCode(getRequestParam("billing_zip"))
# coming soon
# coming soon
const check = new eCheck();
check.accountNumber = getRequestParam("account_number");
check.routingNumber = getRequestParam("routing_number");
check.accountType = getRequestAccountTypeParam("account_type");
check.checkType = getRequestCheckTypeParam("check_type");
check.checkHolderName = getRequestParam("check_holder_name");

const address = new Address();
address.streetAddress1 = getRequestParam("billing_street_address_1");
address.city = getRequestParam("billing_city");
address.state = getRequestParam("billing_state");
address.postalCode = getRequestParam("billing_zip");

With our eCheck and Address objects created, we can use them to charge the consumer’s account. Heartland’s server-side SDKs expose the charge action as a method off of the payment objects, including eCheck.

Charge an ACH/eCheck account

var response = check.Charge(10.00m)
   .WithCurrency("USD")
   .WithAddress(address)
   .Execute();
<?php
$response = $check->charge(10)
   ->withCurrency("USD")
   ->withAddress($address)
   ->execute();
Transaction response = check.charge(new BigDecimal("10"))
   .withCurrency("USD")
   .withAddress(address)
   .execute();
# coming soon
# coming soon
const response = await check.charge(10)
   .withCurrency("USD")
   .withAddress(address)
   .execute();

All payment transactions, including charge, will return a Transaction object when using our server-side SDKs. Any data exposed on the Transaction object can be stored in your application’s database or displayed to the consumer.

Handle Declines and Errors

Declines from issuing banks and general errors are bound to happen, so it is wise for your application to be prepared. Our server-side SDKs will throw an exception when the issuing bank declines a transaction or if the supplied data fails the SDKs’ built-in validation. The best way to account for this is to wrap your code in a try/catch:

Exception Handling

# coming soon
# coming soon
try {
   check.Charge(-5m)
   .WithCurrency("USD")
   .WithAddress(address)
   .Execute();
}

catch (ApiException e) {
   // handle error
}
<?php
try {
   $check->charge(-5)
     ->withCurrency("USD")
     ->withAddress($address)
     ->execute();
}

catch (ApiException $e) {
   // handle error
}
try {
   check.charge(new BigDecimal("-5"))
   .withCurrency("USD")
   .withAddress(address)
   .execute();
}

catch (ApiException e) {
   // handle error
}
check.charge(-5)
   .withCurrency("USD")
   .withAddress(address)
   .execute()
   .catch((e) => {
     switch (e.name) {
       case ApiError.constructor.name:
       default:
         // handle error
         break;
     }
   });

Please refer to our Error Handling section for more information on specialized types to narrowing the original cause of the exception.

Next Steps

Learn more about Heartland Ecommerce check payments with: