ABOUT THIS API

The Fortis Gateway (Zeamster) API is in maintenance mode. This documentation site remains accessible for supporting existing integrations to the API.
If you are doing a new integration to Fortis please use our new Fortis.Tech payment platfom API. We will only certify new integrations to the Fortis.Tech API.

Quick Start Guide

The purpose of this document is to detail the steps necessary to understand how to develop a proper integration to our API. This document should answer many of the basic questions around how our API operates and how to get integrated quickly and easily.

Developer Portal Access

Have you requested Developer Portal access yet? Click here to fill out the Developer Portal application to request access.

Getting Started

Before digging into the API endpoints, there are a few pieces of information that are needed in order to help understand the docs and make the integration more successful.

If you are not familiar with REST API's and you will be doing a direct integration as described in the next section, please be sure to read the details in the API Overview. The API Overview section not only explains some of the basics about API's in general, but also provides more detail about how our API works. Understanding this section will be critical in completing a robust integration.

Integrator Primary Key Support (*_api_id)

The platform has some endpoints that allow an integrated software to use their own unique id instead having to use the platform's id for each record. Endpoints that support this feature have a field with the suffix "_api_id" which is forced unique per Location in the platform. The endpoints that support this method of id are:

  • contacts
  • accountvaults
  • transactions
  • locations

An example of the typical use case is that when a contact is created, the integrator inputs their identifier for the contact in the contact_api_id field.  When this is done, the contact can now be looked up using this is field instead of the id field that is normally used. When using the api_id to look up a record, the location_id needs to be supplied, and the system also needs an additional flag in the url to know that it should be looking up using the api_id field instead of the id field. Here is an example of what it looks like searching for a contact using the contact_api_id field:

GET /v2/contacts/{contact_api_id}?location_id={location_id}&api_id=true

One important thing to note is that when using the api_id field to look up records in this way, the contact_api_id cannot contain any spaces. So if you are planning on using api_id's thoughout the system, be sure to plan ahead and make sure that no records are created that contain a space in the api_id fields.

When using the api_id field to track contacts, you can use this field throughout the system in place of the contact's id. For example, now when running a transaction for a contact, you can replace the contact_id field with the contact_api_id fiel like so:

{
    "transaction": {
        "action": "sale",
        "payment_method": "cc",
        "account_number": "5454545454545454",
        "exp_date":"1220",
        "transaction_amount": 1,
        "location_id": "{location_id}",
        "contact_api_id": "{contact_api_id}"

    }
}

 

Note: Some endpoints in the system require the api_id field to be submitted. For example, when running a transaction using the Payform method, you must supply a transaction_api_id in order for the transaction request to be successful.

 

Preventing Duplicate Transactions

Using the api_id field can provide many benefits. For example, when used on the transactions endpoint, the api_id will allow you to track a transaction and retrieve the transaction information, even in the event you lose connectivity during the transaction. Since api_id's are unique per location, you can be sure the the transaction will never duplicate if you use specific transaction_api_id's when sending the request. The transaction_api_id can be used when running a transaction like so:

{
    "transaction": {
        "action": "sale",
        "payment_method": "cc",
        "account_number": "5454545454545454",
        "exp_date":"1220",
        "transaction_amount": 1,
        "location_id": "{location_id}",
        "contact_api_id": "{contact_api_id}"

        "transaction_api_id": "{transaction_api_id}" // must be unique per transaction per location
    }
}

Running transactions, especially using a terminal, can take upwards of a couple minutes. Some client libraries may have troube consistently keeping these connections open for such long periods. If this is the case, and the integrated software loses connectivity during the transaction request, the transaction can then be retrieved using the transaction_api_id like so:

GET /v2/transactions/{transaction_api_id}?location_id={location_id}&api_id=true

 

Identifiers and Keys

There are a variety of identifiers and keys used within the platform, here is a short list of what they are called and how they are used.

  • Development Company - These are setup at the integration company level, if you are the merchant then you would receive this too.
  • Location - This would be setup at the merchant's corporate level, typically there is only one for most small merchants, however larger merchants can have an unlimited number.  In more complex situations this can be setup using a hierarchy to provide access.
  • User - These are user settings and are important as we have audit logs that track actions/changes within the system.  Users are also assigned "roles" for permissions to do certain items, for example a user can be restricted to not allow them to do a void or refund.
Intended for Name Purpose
Development Company developer_id This is used to identify the developer that is making the request in our logs.
Development Company developer_encryption_key This is used when implementing SSO (Single Sign-On) integration method.
Location location_id This is typically a single merchant, however a user can belong to multiple. 
Location ticket_hash_key This is used when implementing Ticketing and Hosted Payment Page (HPP) integration methods.
Location product_transaction_id This is equivelant to a merchant account (Deposit account), a location can have multiple configured for use.  When integrating and your customers may have multiple merchant accounts this product_transaction_id should be passed when running a transaction otherwise the system will default to what is configured for the location.
User user_api_key This is like a password and should be protected and not shared with anyone.
User user_hash_key This is used when implementing PayForm & AccountForm or HMAC Authorization Process
User user_id Every user is assigned an id.

 

Integration Methods

One main thing that needs to be determined is the integration method that will be used. This will dictate the method of authentication, and how much coding will need to be done. There are currently 3 main ways to integrate to the API, and each of these methods are listed below.

  • Direct Integration
    • Most common industries: Retail, Lodging, Restaurant
    • This method is usually used in any of the following scenarios:
      • Running transactions through an EMV terminal (to remain out of scope for PCI DSS).
      • Creating contacts, account vaults, recurrings, etc.
      • Directly connecting to the API to run transactions or download data.
      • Most situations where requests will be made server to server using the integrated software.
      • Software vendors wish to remain out of scope for PCI by running transactions through an EMV terminal.
  • Single Sign On
    • Most common industries: Retail, MOTO
    • This method is usually used in the following scenario:
      • Where users will be using our pre-built user interface to run their transactions.
  • Hosted Payment Page
    • Most common industries: E-commerce
    • This method is usually used in the following scenarios:
      • Donation pages
      • Anonymous payment pages
      • Shopping carts and other online purchases
      • Other external payment accepting pages
  • AccountForm / PayForm
    • Most common industries: Retail, MOTO, Lodging, Restaurant
    • This method is usually used in the following scenarios:
      • POS software that incorporates payments
      • Internal web sites where users authenticate internally and accept payment
    • This method should not be used for external payments. For external payments the Hosted Payment Page method should be used.

If done correctly, each of the above methods will allow you to limit your scope of PCI by not accepting, transmitting, or storing cardholder data. The following method has been Deprecated as it is no longer considered Out of Scope for PCI Compliance.

  • Ticketing
    • Deprecated.  We encourage developers to look at our other integration methods.
    • Most common industries: E-commerce
    • This method is usually used in the following scenarios:
      • Collecting payments through a web site or other online tool.
      • A shopping cart integration that supports payment gateways.

Certification

Once the implementation is complete, we have a brief certification process:

Hosted Solutions (Hosted Payment Page, AccountForm, PayForm)
Reach out to us at devsupport@zeamster.com to schedule your certification call. During the call we’ll make sure transaction data is passing correctly and properly formatted.
Non-Hosted Solution (Ticketing, Direct Integration)
Email devsupport@zeamster.com a copy of your PCI/PA-DSS certificate from an Approved Scanning Vendor (ASV). Once we review your certificate we’ll provide a link to setup your certification call. During the call we’ll make sure transaction data is passing correctly and properly formatted.

Note: Using tokenized transaction data or EMV/P2PE devices may alter PCI scope for Non-Hosted Solutions.

 

Direct Integration Method

The direct integration method might be as little as one API call, or as many as one hundred calls depending on your integration needs. For example, in order to run a transaction using an EMV capable terminal, this is simply one API call.

Authentication

In order to authenticate an API request, there needs to be authentication data sent along with the request. Authentication data consists of the three following pieces of information:

  • user-id (usually provided on a per user/merchant basis)
  • user-api-key (usually provided on a per user/merchant basis)
  • developer-id (will be hard coded in your software)

The user-id and user-api-key will be specific to each merchant account (location) that is connecting to the gateway. The developer-id is something that should be hard coded into your software. This is only for you to use and should be embedded in your software so that you shouldn't have to openly provide it to merchants/customers.

There will be two different environments for code, one for sandbox and one for production. Each environment will have their own URL and developer-id for submitting requests. The below code represents how you might setup your environment variables for submitting requests.

// Define everything initially as sandbox environment
bool isProduction = false;
var url = "https://api.sandbox.server.com";
var developer_id = "12345678";

// If it is production, update the variables
if (isProduction)
{
	url = "https://api.server.com";
	developer_id = "87654321";
}

// Setup url scheme
var router_transaction_endpoint = url + "/v2/routertransactions";
var transaction_endpoint = url + "/v2/transactions";
var contact_endpoint = url + '/v2/contacts';
// Define everything initially as sandbox environment
var isProduction = false;
var url = "https://api.sandbox.server.com";
var developer_id = "12345678";

// If it is production, update the variables
if (isProduction) {
	url = "https://api.server.com";
	developer_id = "87654321";
}

// Setup url scheme
var router_transaction_endpoint = url + "/v2/routertransactions";
var transaction_endpoint = url + "/v2/transactions";
var contact_endpoint = url + '/v2/contacts';
N/A
#!/usr/bin/ruby

# Define everything initially as sandbox environment
isProduction = false;
url = "https://api.sandbox.server.com";
developer_id = "12345678";

# If it is production, update the variables
if isProduction
	url = "https://api.server.com"
	developer_id = "87654321"
end

# Setup url scheme
router_transaction_endpoint = url + "/v2/routertransactions";
transaction_endpoint = url + "/v2/transactions";
contact_endpoint = url + '/v2/contacts';

Sending Requests

Once you have established the basic variable setup for your environment, you should be able to send API requests. The following code demonstrates how to create a contact in the API. In order to create a contact, you will need the location_id, as well as the necessary credentials to authenticate the request. In addition, you will need to specify whether your are sending a JSON formatted payload or an XML formatted payload.

// Define everything initially as sandbox environment
bool isProduction = false;
var url = "https://api.sandbox.server.com";
var developer_id = "12345678";

// If it is production, update the variables
if (isProduction)
{
	url = "https://api.server.com";
	developer_id = "87654321";
}

// Setup url scheme
var router_transaction_endpoint = url + "/v2/routertransactions";
var transaction_endpoint = url + "/v2/transactions";
var contact_endpoint = url + '/v2/contacts';

NameValueCollection params = new NameValueCollection()
{
	{ "user_id", "xxxx" }, // This is the user-id (stored in the database) for this location
	{ "user_api_key", "yyyy" }, // This is the user-api-key (stored in the database) for this location
	{ "location_id", "zzzz" } // This is the location_id (stored in the database) for this location
};

var client = new RestClient(router_transaction_endpoint);
var request = new RestRequest(Method.POST);
request.AddHeader("Cache-Control", "no-cache");
request.AddHeader("developer-id", developer_id);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("user-api-key", params["user_api_key"]);
request.AddHeader("user-id", params["user_id"]);
request.AddParameter("undefined", "{\n  \"routertransaction\": {\n    \"action\":\"sale"\",\n    \"payment_method\":\"cc"\",\n    \"location_id\":\"" + params["location_id"] + ""\",\n    \"terminal_id\":\"1111\",\n    \"transaction_amount\":\"1.00\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
var request = require("request");

// Define everything initially as sandbox environment
var isProduction = false;
var url = "https://api.sandbox.server.com"; // This will be the sandbox server url
var developer_id = "12345678"; // This will be your given sandbox developer id

// If it is production, update the variables
if (isProduction) {
	url = "https://api.server.com"; // This will be the production server url
	developer_id = "87654321"; // This will be your given production developer id
}

// Setup url scheme
var router_transaction_endpoint = url + "/v2/routertransactions";
var transaction_endpoint = url + "/v2/transactions";
var contact_endpoint = url + '/v2/contacts';

// Stored variable for this merchant/location_id
var params = {
	user_id = 'xxxx', // This is the user-id (stored in the database) for this location
	user_api_key: 'yyyy', // This is the user-api-key (stored in the database) for this location
	location_id: 'zzzz' // This is the location_id (stored in the database) for this location
	terminal_id: '1111' // This is the terminal_id (stored in the database) for this location
};

// Setup options for request
var options = {
	method: 'POST',
	url: router_transaction_endpoint,
	headers: {
		'Cache-Control': 'no-cache',
		'developer-id': developer_id,
		'Content-Type': 'application/json',
		'user-id': params.user_id,
		'user-api-key': params.user_api_key
	},
	body: {
		routertransaction: {
			action: 'sale',
			payment_method: 'cc',
			location_id: params.location_id,
			terminal_id: params.terminal_id,
			transaction_amount: 1.00
		}
	},
	json: true
};

// Send request to API
request(options, function (error, response, body) {
	if (error) throw new Error(error);

	console.log(body);
});
curl -X POST \
https://terminalrouter.zeamster.com/v2/routertransactions \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-H 'developer-id: dev-id' \
-H 'user-api-key: 111111111111' \
-H 'user-id: 222222222222' \
-d '{
	"routertransaction": {
		"action": "sale",
		"payment_method": "cc",
		"location_id": "location_id",
		"terminal_id":"terminal_id",
		"transaction_amount":"1.00"
	}
}'
require 'url'
require 'net/http'

# Define everything initially as sandbox environment
isProduction = false;
url = "https://api.sandbox.server.com";
developer_id = "12345678";

# If it is production, update the variables
if isProduction
	url = "https://api.server.com"
	developer_id = "87654321"
end

# Setup url scheme
router_transaction_endpoint = url + "/v2/routertransactions";
transaction_endpoint = url + "/v2/transactions";
contact_endpoint = url + '/v2/contacts';

params = {
	"user_id" => "xxxx",
	"user_api_key" => "yyyy",
	"location_id" => "zzzz",
	"terminal_id" => "1111"
}

http = Net::HTTP.new(router_transaction_endpoint, 443)

request = Net::HTTP::Post.new(params.url)
request["user-id"] = params.user_id
request["user-api-key"] = params.user_api_key
request["Content-Type"] = 'application/json'
request["developer-id"] = developer_id
request["Cache-Control"] = 'no-cache'
request.body = "{\n  \"routertransaction\": {\n    \"action\": \"sale\",\n    \"payment_method\": \"cc\",\n    \"location_id\": \"" + params.location_id + ""\",\n    \"terminal_id\":\"" + params.terminal_id + "\",\n    \"transaction_amount\":\"1.00\"\n  }\n}"

response = http.request(request)
puts response.read_body

Useful Tip:

Notice that the above request has a field called contact_api_id. Most endpoints have an api_id field. These fields can be used for you to store your own id's so that you don't have to use ours. For example, If you would like to create a contact and not worry about storing the returned id, you can send your own contact_api_id. Then when you need to reference this contact in the future, your can use contact_api_id in requests instead of contact_id.

Now that you are familiar with how to send requests, you can visit the API Reference to see all of the different endpoints and required parameters. The API Reference can be found here.

Single Sign On Method

The single sign on method is another way to help keep your software out of scope. This method is very quick and easy to code, and only requires a minimal amount of API knowledge. This method should be used in a closed environment and controlled with user access within your software. There are two parts to this method, one is running the transaction, and the other, which is optional, is to download the transaction.

The concept of this method is as follows:

  • Place a clickable button inside your software or on your internal web app.
  • When a user clicks this button, it will open a GET request to our API in the user's browser.
  • The web request then hits our API server, authenticates the user, and redirects them to our user interface to a secure page.
  • Our user interface is then used to run a transaction by either keying in the card data, or capturing the card data with an EMV terminal.

Once the transaction has been run and the user returns to your software, you will need to download the transaction(s) to make sure it is logged properly in your system. Alternatively, if you have an online app, we can perform a postback to your system so you can log the transaction properly. Our API performs real time postbacks once a transaction has been created or updated.

The specifics on how to perform the above sequence of events can be found here.

Ticketing Method

Notice: This integration method has been deprecated.  We encourage developers to look at our other integration methods.

Warning!
This method is no longer considered Out Of Scope for the new PCI 3.2 standard.
See https://www.pcisecuritystandards.org/pdfs/best_practices_securing_ecommerce.pdf for more info.

The ticketing integration method is a way of submitting transactions from your web site to our servers while keeping you Out Of Scope (OOS) for PCI. In a nutshell, the process works by using JavaScript code to send the card data directly to our API servers. We respond back with a token. This token is stored and submitted by your servers to our API servers to complete a credit card transaction. By using this method, your servers will never touch or pass credit card data.

Below is a diagram of the request flow. The only step that involves credit card data is step #2, which bypasses your API servers.

Here are all the steps of how the flow of data works.

  • Merchant Web Server (MWS) generates checkout page.
  • Customer clicks submit on form, browser sends PCI Sensitive Cardholder Data to our API server via ajax request.
  • API responds to browser with a ticket.
  • Browser submits form with ticket (Card data removed) to MWS.
  • MWS sends transaction request to our API.
  • Our API responds back with transaction status (Approved/Declined).
  • MWS responds to Customer browser with “Thank You” page.

There is sample code written in PHP5 that is available. The sample code, and more details on how to implement this method are available here.

Hosted Payment Page Method

This section describes how to accept payments using hosted payment pages in order to keep your servers out of scope for PCI. The payment form will be created in an iFrame or new page that will be loaded from our API. The page will then be submitted and the user can be redirected back to your hosted page.

Basic Setup

Here is an overview of the steps involved in getting the hosted payment page setup. These are basic steps to create a static payment page for accepting payments.

  • Log in to the User Interface.
  • Navigate to the Location settings and then click on Hosted Payment Page.
  • This section will allow you to add a Hosted Payment Page setup for your desired payment service.
  • Select the template you wish to use and then click “Create Hosted Payment Page”
  • Customize the new Hosted Payment Page to meet your needs.
  • Click the “save” button in the upper right hand corner.
  • Click the “use” button in the upper right hand corner.
  • Copy the button text to your web site and place it where you would like someone to be able to make a payment.

The base setup will work for situations that require static content. It the transaction amount is fixed, the above for will work nicely. The above setup will also work well for a donation type Hosted Payment Page where the end customer will be able to supply there own dollar amount.

If your Hosted Payment Page requires a different transaction amount for each payment, and the end customer is not allowed to alter the transaction amount, then you will need to use a slightly more advanced setup. This method is detailed further under the Hosted Payment Page documentation.

AccountForm / PayForm Method

This section explains how to create Account Vaults and Transactions using a widget with no previous data stored needed. All the data will come from the widget and shall keep you out of scope for PCI as the form code is being generated by us, another key part of staying out of scope for PCI Compliance.

This PayForm endpoint should be used for running new Transactions for sale, authonly, or refund transactions. Any void, authincrement, or authcomplete transactions are done as PUT transactions using the id returned from this endpoint. PUT transactions do not require account_number and exp_date fields to be submitted, therefore need to be done on the transactions endpoint.

For more information on how to use AccountForm or PayForm, visit the AccountForm / PayForm Integrations page.