Semi-integrated Devices
The Heartland SDK is compatible with semi-integrated devices that connect directly with Heartland, providing a variety of methods which make performing transactions simple and easy, keeping all payment data away from the Point of Sale software. With the Heartland SDK library, you can quickly implement Credit, Debit, and Gift/Loyalty capabilities into your Heartland integrations.
Connecting to Device
The SDK supports communication with the Semi-integrated Devices. The ConnectionConfig
object is used by the SDK to define device communication protocols. To configure the connection, you will need to select a ConnectionMode
and supply any additional pertinent information for the mode specified. Please refer to your device documentation for supported connection modes.
Device setup
<?php
// RequestIdProvider
use GlobalPayments\Api\Terminals\Interfaces\IRequestIdProvider;
class RequestIdProvider implements IRequestIdProvider
{
public function getRequestId()
{
return 10000 + random_int(0, 99999);
}
}
// Device connection
use GlobalPayments\Api\Terminals\ConnectionConfig;
use GlobalPayments\Api\Terminals\Enums\ConnectionModes;
use GlobalPayments\Api\Terminals\Enums\DeviceType;
use GlobalPayments\Api\Services\DeviceService;
$config = new ConnectionConfig();
$config->ipAddress = '192.168.47.79';
$config->port = '8081';
$config->deviceType = DeviceType::UPA_SATURN_1000;
$config->connectionMode = ConnectionModes::TCP_IP;
$config->timeout = 30;
$config->requestIdProvider = new RequestIdProvider();
$device = DeviceService::create($config);
// RequestIdProvider
using GlobalPayments.Api.Terminals;
public class RandomIdProvider : IRequestIdProvider {
private Random random;
public RandomIdProvider() {
random = new Random(DateTime.Now.Millisecond);
}
public int GetRequestId() {
return random.Next(100000, 999999);
}
}
// Device connection
var device = DeviceService.Create(new ConnectionConfig {
DeviceType = DeviceType.NUCLEUS_SATURN_1000,
ConnectionMode = ConnectionModes.TCP_IP,
IpAddress = "10.138.141.32",
Port = "8081",
TimeOut = 30000,
RequestIdProvider = new RandomIdProvider()
});
// RequestIdProvider
import java.util.Random;
import org.joda.time.DateTime;
import com.global.api.terminals.IRequestIdProvider;
public class RandomIdProvider implements IRequestIdProvider {
private Random random;
public RandomIdProvider() {
random = new Random(DateTime.now().getMillisOfSecond());
}
public int getRequestId() {
return 100000 + random.nextInt(999999);
}
}
// Device configuration
import com.global.api.entities.enums.ConnectionModes;
import com.global.api.entities.enums.DeviceType;
import com.global.api.entities.exceptions.ApiException;
import com.global.api.services.DeviceService;
import com.global.api.terminals.ConnectionConfig;
import com.global.api.terminals.abstractions.IDeviceInterface;
ConnectionConfig config = new ConnectionConfig();
config.setPort(8081);
config.setIpAddress("192.168.0.198");
config.setTimeout(45000);
config.setRequestIdProvider(new RandomIdProvider());
config.setDeviceType(DeviceType.UPA_VERIFONE_T650P);
config.setConnectionMode(ConnectionModes.TCP_IP);
IDeviceInterface device = DeviceService.create(config);
# see .Net/Java
# see .Net/Java
// see .Net/Java
#import "HpsUpaDevice.h"
HpsConnectionConfig *config = [[HpsConnectionConfig alloc] init];
config.ipAddress = @"10.130.159.67";
config.port = @"8081";
config.connectionMode = HpsConnectionModes_TCP_IP;
HpsUpaDevice * device = [[HpsUpaDevice alloc] initWithConfig:config];
Credit Transactions
The following credit transactions to the terminal are supported:
CreditSale
CreditSale authorizes a credit card transaction. These authorizations are automatically added to the batch to be settled. If a batch is not already open, this transaction will create one.
Credit Sale Example
<?php
$response = $device->creditSale(11)
->execute();
var response = device.CreditSale(11m)
.Execute();
import com.global.api.terminals.TerminalResponse;
import java.math.BigDecimal;
TeminalResponse response = device.creditSale(new BigDecimal("11"))
.execute();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsUpaSaleBuilder* builder = [[HpsUpaSaleBuilder alloc] initWithDevice:device];
builder.ecrId = @"3";
builder.amount = [[NSDecimalNumber alloc] initWithDouble:10.33];
[builder execute:^(HpsUpaResponse * response, NSError * error) {}];
CreditVerify
CreditVerify is used to verify that the associated account is in good standing with the Issuer. This is a zero dollar transaction with no associated authorization. Since VISA and other Issuers have started assessing penalties for one dollar authorizations, this provides a way for merchants to accomplish the same task while avoiding these penalties.
There are differences in the processing of this transaction based on card type.
- VISA: an account verification is done at the Issuer,
- MasterCard: an account status check is done at the Issuer,
- Discover: an account verification is done at the Issuer,
- AmEx/Other: an AVS only verification is done; this still ensures that the account is valid but requires that AVS data is supplied (zip code at a minimum).
Credit Verify Example
<?php
$response = $device->creditVerify()
->execute();
var response = device.Verify()
.Execute();
import com.global.api.terminals.TerminalResponse;
TeminalResponse response = device.creditVerify()
.execute();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsUpaVerifyBuilder* builder = [[HpsUpaVerifyBuilder alloc] initWithDevice:device];
builder.ecrId = @"3";
builder.requestMultiUseToken = true;
[builder execute:^(HpsUpaResponse * response, NSError * error) {}];
CreditVoid
CreditVoid is used to cancel an open auth or remove a transaction from the current open batch. The original transaction must be a credit auth, sale, or refund. Once a batch is closed, associated transactions can no longer be voided. In these cases, a CreditRefund can be used to adjust a customer’s account. If a transaction has been fully or partially returned, it cannot be voided. This transaction can also be performed directly against Portico, see Transactions via Portico.
Credit Void Example
<?php
$saleResponse = $device->creditSale(16)
->execute();
$response = $device->creditVoid()
->withTransactionId($saleResponse->->terminalRefNumber)
->execute();
var saleResponse = device.CreditSale(16m)
.Execute();
var voidResponse = device.CreditVoid()
.WithTransactionId(saleResponse.TerminalRefNumber)
.Execute();
import com.global.api.terminals.TerminalResponse;
import java.math.BigDecimal;
TeminalResponse saleResponse = device.creditSale(new BigDecimal(16))
.execute();
TeminalResponse voidResponse = device.creditVoid()
.withTerminalRefNumber(saleResponse.getTerminalRefNumber())
.execute();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsUpaSaleBuilder *builder = [[HpsUpaSaleBuilder alloc] initWithDevice:device];
builder.amount = [[NSDecimalNumber alloc] initWithDouble:1.00];
builder.gratuity = [[NSDecimalNumber alloc] initWithDouble:0.00];
builder.ecrId = @"1";
[builder execute:^(HpsUpaResponse *payload, NSError *error) {
HpsUpaVoidBuilder *vbuilder = [[HpsUpaVoidBuilder alloc] initWithDevice:device];
vbuilder.ecrId = @"1";
vbuilder.terminalRefNumber = payload.terminalRefNumber;
[vbuilder execute:^(HpsUpaResponse *vpayload, NSError *verror) {}];
}];
CreditAuth
CreditAuth authorizes a credit card transaction. These authorization only transactions are not added to the batch to be settled. If you prefer to have the authorization automatically added to the batch, use CreditSale.
Credit Auth Example
<?php
$response = $device->creditAuth(12)
->execute();
var response = device.Authorize(12m)
.Execute();
import com.global.api.terminals.TerminalResponse;
import java.math.BigDecimal;
TeminalResponse response = device.creditAuth(new BigDecimal("12"))
.withAllowDuplicates(true)
.execute();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsUpaAuthBuilder *builder = [[HpsUpaAuthBuilder alloc] initWithDevice:device];
builder.amount = [[NSDecimalNumber alloc] initWithDouble:1.00];
builder.ecrId = @"1";
[builder execute:^(HpsUpaResponse *payload, NSError *error) {}];
CreditCapture
CreditCapture is primarily used to add a previously approved open authorization to the current open batch.
Credit Capture Example
<?php
$authResponse = $device->creditAuth(12)
->execute();
$response = $device->creditCapture(12)
->withTransactionId($authResponse->transactionId)
->execute();
var response = device.CreditAuth(12m)
.Execute();
var captureResponse = device.CreditCapture(12m)
.WithTransactionId(response.TransactionId)
.Execute();
import com.global.api.terminals.TerminalResponse;
import java.math.BigDecimal;
TeminalResponse response = device.creditAuth(new BigDecimal("12"))
.execute();
TeminalResponse captureResponse = device.creditCapture(new BigDecimal("12"))
.withTransactionId(response.getTransactionId())
.execute();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsUpaCaptureBuilder *cbuilder = [[HpsUpaCaptureBuilder alloc] initWithDevice:device];
cbuilder.issuerRefNumber = payload.issuerRefNumber;
cbuilder.amount = [[NSDecimalNumber alloc] initWithDouble:15.00];
cbuilder.gratuity = [[NSDecimalNumber alloc] initWithDouble:0.00];
cbuilder.ecrId = @"1";
[cbuilder execute:^(HpsUpaResponse *cpayload, NSError *cerror) {
}];
CreditRefund
CreditRefund allows the merchant to return funds back to the cardholder. Refund can be for the entire amount associated with the original sale or a partial amount. The transaction is placed in the current open batch. If a batch is not open, this transaction creates an open batch. This transaction can also be performed directly against Portico, see Transactions via Portico.
For added fraud protection, CreditRefund can be run utilizing the GatewayTxnId from a previous sale. When this feature is used, the gateway tracks refunds against the original sale and applies several rules.
The following rules apply when refunding by GatewayTxnId:
- The original transaction must be a CreditAuth, CreditSale, CreditOfflineAuth, CreditOfflineSale, or RecurringBilling and must be in a batch. It cannot be an open authorization that still needs to be added to a batch.
- The total of all refunds cannot exceed the original sale amount. This is true for processing a single refund as well as multiple refunds against the same original transaction.
- A refund amount must be greater than zero.
- The refund must be run within 90 days.
- CreditReversal, CreditVoid, and CreditTxnEdit are not allowed against original transactions for which a full or partial refund has been run.
- A refund can be voided. If this results in the total refund amount being adjusted back to zero, CreditVoid, CreditReversal, and CreditTxnEdit are allowed on the original transaction once again.
- If CardData is also supplied, then the supplied card number and the card number of the original transaction must match.
Note: If the original transaction is in the current open batch, a CreditVoid or CreditReversal may be used instead. However, only a refund can be used once the batch is closed.
Credit Refund Example
<?php
$saleResponse = $this->device->creditSale(10)
->execute();
$response = $this->device->creditRefund()
->withTransactionId($saleResponse->transactionId)
->execute();
var saleResponse = device.CreditSale(16m)
.Execute();
var refundResponse = device.Refund(14m)
.WithTransactionId(saleResponse.TransactionId)
.Execute();
import com.global.api.terminals.TerminalResponse;
import java.math.BigDecimal;
TeminalResponse saleResponse = device.creditSale(new BigDecimal("16"))
.execute();
TeminalResponse refundResponse = device.creditRefund(new BigDecimal("16"))
.withTransactionId(saleResponse.getTransactionId())
.execute();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsUpaReturnBuilder *builder = [[HpsUpaReturnBuilder alloc] initWithDevice:device];
builder.amount = [[NSDecimalNumber alloc] initWithDouble:1.00];
builder.ecrId = @"1";
[builder execute:^(HpsUpaResponse *payload, NSError *error) {}];
Debit Transactions
DebitSale
DebitSale authorizes a debit card transaction. These authorizations are automatically added to the batch to be settled. If a batch is not already open this transaction will create one.
Debit Sale Example
<?php
$response = $device->debitSale(10)
->execute();
var response = device.Sale()
.WithPaymentMethodType(PaymentMethodType.Debit)
.WithAmount(567.10m)
.Execute();
import com.global.api.terminals.TerminalResponse;
import java.math.BigDecimal;
TerminalResponse response = device.debitSale(new BigDecimal("10"))
.withAllowDuplicates(true)
.execute();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsUpaSaleBuilder* builder = [[HpsUpaSaleBuilder alloc] initWithDevice:device];
builder.ecrId = @"3";
builder.amount = [[NSDecimalNumber alloc] initWithDouble:4.00];
builder.gratuity = [[NSDecimalNumber alloc] initWithDouble:0.00];
[builder execute:^(HpsUpaResponse * response, NSError * error) {}];
DebitRefund
DebitRefund allows the merchant to return funds from a prior debit sale back to the cardholder. Refunds can be for the entire amount associated with the original sale or a partial amount. The transaction is placed in the current open batch. If a batch is not open, this transaction creates an open batch. This transaction can also be performed directly against Portico, see Transactions via Portico.
For added fraud protection, DebitRefund can be run utilizing the GatewayTxnId from a previous debit sale. When this feature is used, the gateway tracks refunds against the original sale and applies several rules.
The following rules apply when returning by GatewayTxnId:
- The total of all refunds cannot exceed the original sale amount. This is true for processing a single refund as well as multiple refunds against the same original transaction.
- A refund amount must be greater than zero.
- DebitReversal is not allowed against original transactions for which a full or partial refund has been run.
- The supplied card number (from the track data or token) and the card number of the original transaction must match.
Note: If the original transaction is in the current open batch, a DebitReversal may be used instead. However, only a refund can be used once the batch is closed.
Debit Refund Example
<?php
$saleResponse = $this->device->debitSale(16)
->execute();
$response = $this->device->debitRefund(10)
->withTransactionId($saleResponse->transactionId)
->execute();
var saleResponse = device.DebitSale(16m)
.Execute();
var response = device.DebitRefund(10m)
.WithTransactionId(saleResponse.TransactionId)
.Execute();
import com.global.api.terminals.TerminalResponse;
import java.math.BigDecimal;
TerminalResponse response = device.debitSale(new BigDecimal("10"))
.execute();
int tranID = response.getTransactionId();
TerminalResponse response2 = device.debitRefund(new BigDecimal("10"))
.withTransactionId(tranID)
.execute();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsUpaReturnBuilder *rbuilder = [[HpsUpaReturnBuilder alloc] initWithDevice:device];
rbuilder.ecrId = @"1";
rbuilder.amount = [[NSDecimalNumber alloc] initWithDouble:4.00]
[rbuilder execute:^(HpsUpaResponse *rpayload, NSError *rerror) {}];
Gift Transactions
GiftSale
GiftCardSale is used to redeem value from a stored value account.
Note: Partial approvals are supported by default. If the account balance is non-zero but insufficient to cover the full redemption amount, the remaining balance is drained and the amount still owed is returned in the response for additional payment. The merchant may accept any additional tender to cover the amount still owed. If the account holder is unable to provide additional payment and the purchase is cancelled, this transaction should be voided to return the balance back to the account. See the “split tender card amount” and “split tender balance due amount” fields in the response.
Gift Sale Example
# coming soon
# coming soon
# coming soon
# see .Net/Java
# see .Net/Java
// see .Net/Java
# coming soon
GiftVoid
GiftCardVoid is used to cancel a prior successful transaction. When voiding a transaction, all changes to the account are reversed, including any additional value added by rewards programs or automated promotions. This transaction can also be performed directly against Portico, see Transactions via Portico.
This can be used on prior transactions of the following types:
- GiftCardAddValue
- GiftCardSale
Gift Void Example
# coming soon
# coming soon
# coming soon
# see .Net/Java
# see .Net/Java
// see .Net/Java
# coming soon
GiftAddValue
GiftCardAddValue loads an amount onto a stored value account. Depending on the gift processor, this request may also be used to automatically activate the account.
Gift AddValue Example
<?php
# coming soon
# coming soon
# coming soon
# see .Net/Java
# see .Net/Java
// see .Net/Java
# coming soon
GiftBalance
GiftCardBalance is used to retrieve the balance(s) for each currency supported by a stored value account.
Gift Balance Example
# coming soon
# coming soon
# coming soon
# see .Net/Java
# see .Net/Java
// see .Net/Java
# coming soon
EOD
The EOD (End of Day) processing command is used to initiate the device’s end of day processing from the POS. During EOD, any stored offline transactions (Pending SAF, attachments and reversals) are uploaded to the host.
The response will denote with the status of each transaction as (SUCCESS/FAIL/NOT APPLICABLE). Transactions supported are REVERSAL, OFFLINE DECLINE, TRANSACTION CERTIFICATE, ADD ATTACHMENT, SENDSAF, BATCH CLOSE, EMV PDL and HEARTBEAT
EOD Example
<?php
$response = $device->eod();
var response = _device.EndOfDay();
import com.global.api.terminals.abstractions.IEODResponse;
IEODResponse response = device.endOfDay();
# see .Net/Java
# see .Net/Java
// see .Net/Java
[device processEndOfDay:^(id<IHPSDeviceResponse> payload, NSError *error) {}];
Line Items
The POS may display information about the items being rung up by the clerk such as a description, cost, and running total, on the UPA device by using the “LineItemDisplay” command. Each line item consists of a left-side (required) and right-side (optional) value with a limit of 30 characters.
Line Items Example
use GlobalPayments\Api\Terminals\UPA\Entities\LineItem;
$lineItemDetails = [];
$lineItem1 = new LineItem();
$lineItem1->lineItemLeft = "Line Item #1";
$lineItem1->lineItemRight = "10.00";
$lineItemDetails[] = $lineItem1;
$lineItem2 = new LineItem();
$lineItem2->lineItemLeft = "Line Item #2";
$lineItem2->lineItemRight = "10.00";
$lineItemDetails[] = $lineItem2;
$lineItem3 = new LineItem();
$lineItem3->lineItemLeft = "Line Item #3";
$lineItem3->lineItemRight = "10.00";
$lineItemDetails[] = $lineItem3;
$response = $this->device->lineItem($lineItemDetails);
//LineItem Without Right-Side Text
var response = device.LineItem("11");
//LineItem With Right-Side Text
var response = device.LineItem("Toothpaste", "12")
import com.global.api.terminals.abstractions.IDeviceResponse;
IDeviceResponse response1 = device.addLineItem("Line Item 1", "111.11");
IDeviceResponse response2 = device.addLineItem("Line Item 2", null); // no right-side text
IDeviceResponse response3 = device.addLineItem("Line Item 3", "333.33");
# see .Net/Java
# see .Net/Java
// see .Net/Java
// Line Item w/o Right-Side Text
[device lineItem:@"Toothpaste" withResponseBlock:^(id<IHPSDeviceResponse> payload, NSError *error) {}];
// Line Item w/Right-Side Text
[device lineItem:@"Toothpaste" withRightText:@"12" withResponseBlock:^(id<IHPSDeviceResponse> payload, NSError *error) {}];
SAF Transactions
Store and Forward processing enables merchants to continue to process transactions when communications between the UPA device and the Portico host are down.
SAF (Store and Forward) transactions are stored in a local transaction file and piggybacked onto the next online transaction. A response is required for each transaction being sent. If a response is not received, it remains in the local transaction file to be sent on the next online transaction. To minimize delays in processing the next online transaction only one SAF is piggybacked onto the next online transaction.
All remaining SAF transactions are sent to the host when the POS sends a SendSAF request or End of Day processing is performed. If the SAF is declined, or receives an invalid response, it is marked as declined in the local transaction file. This allows processing further transactions for the merchant and avoids getting stuck on failed SAF transactions. If partially approved, it is marked as partially approved.
NOTE: for SAF approved transactions the ResponseCode and ResponseText elements are not sent in the transaction response so the POS cannot use the ResponseCode to determine if the transaction was approved. SAF approved transactions are identified by the StoreAndForward element being returned with a ResultCode of “0” and a ResultText of “APPROVED”. If a transaction is not eligible for SAF and is declined (as would happen if the SAF amount limit is exceeded) then the StoreAndForward element is not returned. The ResultCode will be set to an error code and the ResultText will have an error message.
NOTE: By using SAF, the merchant accepts the risk of storing transactions for later transmission to Portico.
The POS may request a SAF Report with the GetSAF command. The SAF Report contains summary and detail information for pending and declined SAFs. Note that declined SAFs are kept in the local transaction file until the local transaction file is cleared during End of Day processing. Hence if multiple GetSAF commands are performed before End of Day processing, the same declined SAF transactions will be reported.
SAF reports are also generated during End of Day processing. For these SAF Reports the reports include not only the pending and declined transactions but also the approved and partially approved transactions. Again these transactions remain in the local transaction file until the local transaction file is cleared during End of Day processing.
SAF Example
<?php
$response = $device->sendSaf();
var response = _device.SendStoreAndForward();
import com.global.api.terminals.abstractions.ISAFResponse;
ISAFResponse response = device.sendStoreAndForward();
# see .Net/Java
# see .Net/Java
// see .Net/Java
HpsHpaSafBuilder *builder= [[HpsHpaSafBuilder alloc] initWithDevice:device];
builder.referenceNumber = [device generateNumber];
[builder execute:^(HpsHpaSafResponse* payload, NSError *error) {
}];
Supporting Information
Handling Tips
When handling tips there are three options:
- Sales transaction that includes the full amount (Sale + Tip)
- Auth/Capture transaction where you authorize for the sale amount and when performing a capture you add in the tip amount at that time.
- An indivual using the Portico Virtual Terminal (VT) to do a sales transaction edit. (email our Specialty Products Group
- Peforming an SDK Sales Transaction Edit using the credentials of the device the transaction was performed on.
Transactions via Portico
The SDK has all the capabilities to allow you to do transactions such as Reversals, Voids, Edits, and Returns for Credit, Debit, Gift and Loyalty directly against the Portico gateway. However it is important to note that when you choose to go this route, the credentials used must match the credentials of the terminal the original transaction was placed on.
You can find the credentials on a UPA device configured for Portico by going to MENU > UDS Client > DOWNLOAD > PARTIAL on the physical device.
Static vs Dynamic IPs
Unless otherwise specified during configuration the UPA Terminal will use DHCP to obtain an IP address. Should you want to change this to a static IP you can update this configuration via the terminal by navigating to Settings > Network & Internet > Advanced options. Keep in mind that if you change this via the terminal only then those are kept with the terminal and not loaded on Heartland’s systems. This means that should the terminal need to redownload its parameters again the settings will be lost. To prevent this contact Heartland with your final settings so they they can be updated in our system.