Card Payments with Heartland Tokenization
Card payments will be at the core of your payment integration with Heartland, and Heartland’s single-use tokenization offering will ensure you are accepting card information (number, expiration date, and security code) in a PCI compliant way.
Heartland’s patent-pending technology greatly minimizes PCI scope and cost, because card data processed through Heartland Tokenization never has to touch your server. The exchange of sensitive information occurs directly between the consumer and Heartland Payment Systems through our Portico Gateway. Our JavaScript streamlines this process so you don’t have to worry about obtaining tokens. The library handles that request and allows you to append the resulting token to your form before it posts.
Accept Card Information
The most secure way to get up and running with Heartland Tokenization is to use our iFrame-hybrid tokenization solution.
Basic HTML Payment Form
<form id="payment-form" action="/Payment/Charge" method="get">
<!-- Other input fields to capture relevant data -->
<label for="billing_zip">Billing Zip Code</label>
<input id="billing_zip" name="billing_zip" value="47150" type="tel" />
<!-- Target for the credit card form -->
<div id="credit-card"></div>
</form>
Where are the payment fields?
You may have noticed there are no actual input
fields in the HTML above. Congratulations! You’ve discovered how we can reduce your PCI-DSS scope by moving a large portion of the payment acceptance process away from your site.
To achieve this, our JavaScript library inserts tiny iFrames pointed to solitary input
fields on our payment gateway into those empty div
elements above. Even though the fields themselves are displayed through iFrames and are hosted on our payment gateway, they integrate seemlessly into your existing payment form on your web site, keeping your customers where they should be…on your web site. This can help reduce your PCI scope down to the SAQ-A form (aka the short form) since the customer is inputting their card data onto our servers and all your site has access to is a single-use payment token.
Heartland Tokenization is packaged as a JavaScript library. All you need to do is include the Heartland Tokenization library, and add a few lines of initialization code. It’s that simple!
Necessary JavaScript for Tokenization
<script src="https://js.globalpay.com/v1/globalpayments.js"></script>
<script type="text/javascript">
// Configure account
GlobalPayments.configure({
publicApiKey: "pkapi_cert_jKc1FtuyAydZhZfbB3"
});
// Create Form
const cardForm = GlobalPayments.creditCard.form("#credit-card");
cardForm.on("token-success", (resp) => {
// add payment token to form as a hidden input
const token = document.createElement("input");
token.type = "hidden";
token.name = "payment_token";
token.value = resp.paymentReference;
// Submit data to the integration's backend for processing
const form = document.getElementById("payment-form");
form.appendChild(token);
form.submit();
});
cardForm.on("token-error", (resp) => {
// show error to the consumer
});
</script>
Validation
Worried about bad data being entered? Our iFrame-based fields have a set of functions to help your customers during their checkout process. All fields only allow customers to input numbers, preventing the accidental letter and/or symbol from creating a problem, and each one also adds a CSS class of .valid
or .invalid
to make styling good or bad data easier, pin-pointing problems for your customers as early as possible.
While on the subject of visual cues, the card number field will also add a CSS class of the form .card-type-{card brand}
, allowing the card brand logos to be displayed for the customer and adding one more visual validation that the card information is correct.
See It in Action
Head over to the Tokenization Demo to learn more about our JavaScript tokenization solution and to see it in action!
Charge the Token
Once the consumer has entered their card information and submitted the payment form, the next step is to charge the token. 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 = '%%SECRET_API_KEY%%'
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 a credit card token
using GlobalPayments.Api.Entities;
using GlobalPayments.Api.PaymentMethods;
var card = new CreditCardData {
Token = "single-use token"
};
var address new Address {
PostalCode = "12345"
};
<?php
use GlobalPayments\Api\Entities\Address;
use GlobalPayments\Api\PaymentMethods\CreditCardData;
$card = new CreditCardData();
$card->token = "single-use token";
$address = new Address();
$address->postalCode = "12345";
import com.global.api.entities.Address;
import com.global.api.paymentMethods.CreditCardData;
CreditCardData card = new CreditCardData();
card.setToken("single-use token");
Address address = new Address();
address.setCode("12345");
# coming soon
from globalpayments.api.entities import Address
from globalpayments.api.payment_methods import CreditCardData
card = CreditCardData()
card.token = 'single-use token'
address = Address()
address.postal_code = '12345'
import { Address, CreditCardData } from "globalpayments-api";
const card = new CreditCardData();
card.token = "single-use token";
const address = new Address();
address.code = "12345";
With our CreditCardData
and Address
objects created, we can use them to charge the consumer’s card. Heartland’s server-side SDKs expose the charge action as a method off of the payment objects, including CreditCardData
.
Charge a credit card token
var response = card.Charge(10.00m)
.WithCurrency("USD")
.WithAddress(address)
.Execute();
<?php
$response = $card->charge(new BigDecimal("10"))
->withCurrency("USD")
->withAddress($address)
->execute();
import com.global.api.entities.Transaction;
import java.math.BigDecimal;
Transaction response = card.charge(new BigDecimal("10"))
.withCurrency("USD")
.withAddress(address)
.execute();
# coming soon
response = card.charge(10) \
.with_currency('USD') \
.with_address(address) \
.execute()
const response = await card.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
from globalpayments.api.entities.exceptions import ApiException
try:
response = card.charge(-5) \
.with_currency('USD') \
.with_address(address) \
.execute()
except ApiException as e:
// handle error
using GlobalPayments.Api.Entities;
try
{
card.Charge(-5m)
.WithCurrency("USD")
.WithAddress(address)
.Execute();
}
catch (ApiException e)
{
// handle error
}
<?php
use GlobalPayments\Api\Entities\Exceptions\ApiException;
try
{
$card->charge(-5)
->withCurrency("USD")
->withAddress($address)
->execute();
}
catch (ApiException $e)
{
// handle error
}
import com.global.api.entities.exceptions.ApiException;
import java.math.BigDecimal;
try
{
card.charge(new BigDecimal("-5"))
.withCurrency("USD")
.withAddress(address)
.execute();
}
catch (ApiException e)
{
// handle error
}
import { ApiError } from "globalpayments-api";
card.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.