Getting Started
Welcome to the Gateway documentation!
A detailed technical description of the interface can be found in our Reference documentation - but you should read the Transaction Flow chapter first to understand what the different requests are used for.
In case you want to accept card payments, you may want to integrate our payment.js Library for seamless user experience.
Transaction Process
Depending on the payment method and connector configuration there are 4 different types of process flows:
Full-Page Redirect
This flow requires you to redirect the end-user to the payment page as advised in the redirectUrl
response.
- Initiate the payment with the appropriate API call
- Upon success the Gateway answers with a result containing
returnType = REDIRECT
and the url inredirectUrl
- You redirect the user to the given URL (usually via a
Location
header) - The user completes the payment process on the payment page
- The Gateway sends an asynchronous status notification to the URL provided in the initial API call
- The user will be back redirected to the
successUrl
orerrorUrl
(depending on the result) you provided in the initial API call.
Note: If the payment is in status PENDING
the user will still be redirected to the success page.
Hosted Payment Form
This flow requires you to embed an iFrame containing the payment form.
- Initiate the payment with the appropriate API call
- Upon success the Gateway answers with a result containing
returnType = REDIRECT
and the url inredirectUrl
(depending on the payment form setup it may also containredirectType = iframe
) - You either redirect the user to the given URL (usually via a Location header) or embed and load the iFrame with the given URL
- The user completes the payment process on the payment page (or within the iFrame)
- The Gateway sends you an asynchronous Postback Notifications to the URL you provided in the initial API call
- The iFrame's content will load the
successUrl
orerrorUrl
(depending on the result) you provided in the initial API call. Or in the case of an embedded iFrame, the iFrame will load thesuccessUrl
orerrorUrl
(depending on the result). On that page you usually want to notify your top browser frame via JavaScript about the transaction result. Another approach is to let the success page break out of the iFrame.
Note: The top frame as well as the success/error page are served from the same domain. Some payment types may return a »PENDING« response, in such cases the user will be redirected to the successUrl. If 3-D Secure authentication is used, the customer will be redirected to their bank's authentication page.
Javascript Integration with payment.js
For most card payments (and payment methods with proprietary widgets, e.g. Amazon Payments) you're encouraged to integrate our payment.js Javascript Library. Using this approach most of the payment form will be served from your checkout page - which gives you full flexibility for styling and design. Only the sensitive fields for card number and CVV code are rendered within small iFrames served by our PCI-compliant infrastructure.
- Embed the payment.js library and initiate it as described in payment.js Javascript Integration
- Once you retrieved the payment token from the Javascript
tokenize
callback you submit your part of the payment form to your backend - Initiate the payment with the appropriate API call, passing the token in the field
transactionToken
- The transaction gets processed immediately and the Gateway sends you an asynchronous Postback Notification to the URL you provided in the initial API call
- The Gateway answers your request containing the result. Depending on the transaction status this result will usually be
FINISHED
,PENDING
orERROR
.
Server-to-Server only
Some transactions just require some Server-to-Server communication, without involving the end-user directly into the payment process. Typical examples are: Recurring payments, Refunds, De-Registering of a payment method, Capture or Void of a previous preauthorization. Furthermore SEPA DirectDebit transactions can be performed using this way, as there is currently no requirement to collect IBAN and BIC in a PCI-compliant manner.
Note: This is subject to change depending on legal jurisdiction in various countries.
- Initiate the payment the appropriate API call, passing all relevant payment information.
- The transaction gets processed immediately and the Gateway sends you an asynchronous Postback Notification to the URL you provided in the initial API call
- The Gateway answers your request containing the result. Depending on the transaction status this result will usually be
FINISHED
,PENDING
orERROR
.
Transaction Types
The Gateway knows the following transactions types. Note: Depending on the connector some transaction types may not be available/supported.
- Debit: a "normal" payment. Debits the end customer with the given amount.
- Preauthorize: Reserves the payment amount on the customer's payment instrument.
- Capture: Completes a payment which was previously authorized with the Preauthorize method.
- Void: Cancels a previously performed Preauthorize
- Refund: Reverses a payment which was previously performed with Debit or Capture
- Payout: Credits the customer's account with the given amount
- Chargeback: a negative booking on the merchant's account which is triggered by the end customer
- Chargeback Reversal: reversal of a previous chargeback (usually when you won a chargeback dispute)
- Register: Registers a customer's payment instrument for future charges (see Recurring Transactions below)
- Deregister: Deletes a previously registered payment instrument.
Each transaction returns a unique ID ("UUID") which you should store within your database, as you may need it for further transactions.
Recurring Transactions
For recurring transaction you have to set the withRegister
flag on the first transaction. By doing this the gateway and the payment provider will accept further payments with reference to the first one.
For the recurring Debit or Preauthorize call you must set the referenceTransactionId
(XML) / referenceUuid
(JSON) with the UUID of the first transaction.
Alternatively to the withRegister
flag, you can use the Register transaction type to just register a customer's payment instrument without charging it immediately.
Additionally you can set the transactionIndicator
in the transaction's data to one of the following values:
transactionIndicator | Description |
---|---|
SINGLE | This marks a one-off transaction (without any recurring options). This is the default for Debits and Preauthorizes WITHOUT withRegister flag |
INITIAL | This marks the first transaction of a recurring series. This is the default for Debits and Preauthorizes with enabled withRegister flag |
RECURRING | This is for subsequent transactions of a recurring series (e.g. subscriptions), which are usually initiated automatically (e.g. once a month). |
CARDONFILE | This marks transactions as Card-On-File transaction, which are initiated by the end-customer (e.g. making a purchase with a stored creditcard) |
CARDONFILE-MERCHANT-INITIATED | This marks transactions as an unscheduled Card-On-File transaction initiated by the merchant |
MOTO | Mail and telephone order |
API Call
Now that you know which kind of transaction there are, you are encouraged to continue reading the API Reference Documentation
Asynchronous status notification
For every payment reaching a final status (successful or erroneous), the Gateway sends a notification XML to the callback URL defined in the transaction request.
Depending on the payment method this can either happen immediately or can take up to several days.
Your system must respond to this request with a HTTP status code "200" and the content "OK". If your response differs, the Gateway will continue to send the notification in increasing intervals.
Additionally the Gateway will also send a notification in case of any new follow up transactions or transaction status changes.
See the API Reference Documentation for detailed information about the notification XML.
Error Codes
Transactions which fail will always return an error code and an error message. If the error is produced by any sub-sequent system (bank, PSP, PayPal etc.) the error element will also contain the adapterCode
and adapterMessage
, reflecting directly the response of those systems.
The error codes are structured in various groups, reflecting the phase when an error occured.
General Errors & Validation Errors
Code | Message | Description |
---|---|---|
1000 | Request failed | Some fundamental error in your request |
1001 | Invalid response | The upstream system responded with an unknown response |
1002 | Invalid request data | Request data are malformed or missing |
1003 | Processing error | Transaction could not be processed |
1004 | Invalid signature | The request signature you provided was wrong |
1005 | Invalid XML | The XML you provided was malformed or invalid |
1006 | Logical error | Preconditions failed, e.g. capture on a failed authorize. |
1007 | Invalid configuration | Something is wrong with your configuration, please contact your integration engineer |
1008 | Unexpected system error | As said |
1009 | Too many requests | Rate limit has been exceeded |
9999 | Unknown error | We received an error which is not (yet) mapped to a better error code |
Payment Errors
Code | Message | Description |
---|---|---|
2001 | Account closed externally | The customer cancelled permission for his payment instrument externally |
2002 | User cancelled | Transaction was cancelled by customer |
2003 | Transaction declined | Transaction declined by upstream system/bank |
2004 | Quota regulation | Some limit reached |
2005 | Transaction expired | Customer took to long to submit his payment info |
2006 | Insufficient funds | Card limit reached |
2007 | Incorrect payment info | . |
2008 | Invalid card | Card is invalid |
2009 | Expired card | |
2010 | Fraudulent card | |
2011 | Unsupported card | |
2012 | Transaction cancelled | |
2013 | Risk check block | |
2014 | Pickup card | |
2015 | Lost card | Card is claimed as lost |
2016 | Stolen card | |
2017 | IBAN invalid | |
2018 | BIC invalid | |
2019 | Customer data invalid | |
2020 | CVV required | |
2021 | 3D-Secure Verification failed | |
2022 | 3D-Secure Soft declined |
Status API Errors
Code | Message | Description |
---|---|---|
8001 | Transaction not found | No transaction was found for this specific connector determined by the apiKey |
Schedule API Errors
Code | Message | Description |
---|---|---|
7001 | schedule request is invalid | |
7002 | schedule request failed | |
7005 | scheduleAction is not valid | |
7010 | registrationId is required | |
7020 | registrationId is not valid | |
7030 | reference transaction not a register | The registrationId must point to a register or a debit-with-register or a preauthorize-with-register |
7035 | initial transaction is not a register | The transaction for starting a schedule must be a register, a debit-with-register or a preauthorize-with-register |
7036 | invalid initial period | The period between the initial and second transaction must be greater than 24 hours |
7040 | The scheduleId is not valid or does not match to the connector | |
7050 | The startDateTime is invalid or older than 24 hours | |
7060 | The continueDateTime is invalid or older than 24 hours | |
7070 | The status of the schedule is not valid for the requested operation |
Network Errors
Code | Message | Description |
---|---|---|
3001 | Timeout | |
3002 | Not Allowed | |
3003 | Temporary unavailable | |
3004 | Duplicate transaction ID | |
3005 | Communication error |
Post-Processing Errors
Code | Message | Description |
---|---|---|
4001 | Chargeback reverted | Chargeback was reverted |
4002 | Payment dispute | A dispute was filed, see the upstream system (e.g. PayPal) for details |
Scheduler
The Gateway's scheduler enables you to perform recurring debits without any further intervention. Based on the defined schedule it automatically triggers the recurring transactions and notifies you about the result.
Creating a schedule
A schedule can be created along with an initial Register or Debit with Register transaction, or can be attached to these kinds of transactions afterwards.
Note that if you send a Debit with a schedule attached to it, the initial Debit will be performed immediately, and the first scheduled transaction will be performed on the defined startDateTime
of the schedule.
Schedule intervals
In the schedule data you have to define the interval for the recurring transactions. It consists of a periodUnit
and a periodLength
.
Example:
periodUnit = DAY
and periodLength = 14
means the customer will be charged every 14 days with the defined amount.
The following interval units are allowed:
DAY
WEEK
MONTH
YEAR
Schedule states
ACTIVE
: this schedule is active and will perform recurring charges in the defined intervalPAUSED
: this schedule is paused, and must be activated with thecontinueSchedule
call to continue recurring transactionsCANCELLED
: this schedule is cancelled. You cannot reactivate a cancelled schedule, you have to send a newstartSchedule
instead.ERROR
: if a recurring charge fails, the schedules changes to this status. You can retry an erroneous schedule by sending acontinueSchedule
call.
payment.js Javascript Integration
With the payment.js integration you can securely accept card payments and integrate card number and CVV collection directly into your shop website without the need for redirecting to a separate payment form. The payment.js library renders 2 separate iFrames for card number and CVV/CVC in your checkout page. This reduces your PCI-DSS scope to as low as it can get (PCI-DSS SAQ-A).
As a result of the JavaScript flow, you will receive a transactionToken
, identifying the customer's card, which you pass to the Transaction API request.
Step-By-Step Guide
1. On top of your HTML page (either in HEAD or directly after BODY), include the payment.1.3.min.js as shown here:
<script data-main="payment-js" src="https://gateway.tillpayments.com/js/integrated/payment.1.3.min.js"></script>
2. Build the payment form using inputs for all data, except card number and CVV/CVC code. For those two just build a DIV element with an ID, and provide these IDs to the payment.js init call. Furthermore you will want to add a hidden field for the transaction token you'll get from payment.js.
For successful payment processing, at least the following fields are required:
- Card Holder (or separate fields for First name & Last name)
- Expiration Month
- Expiration Year
<form id="payment_form" method="POST" action="someScript.php" onsubmit="interceptSubmit(); return false;">
<input type="hidden" name="transaction_token" id="transaction_token" />
<div>
<label for="first_name">First name</label>
<input type="text" id="first_name" name="first_name" />
</div>
<div>
<label for="last_name">Last name</label>
<input type="text" id="last_name" name="last_name" />
</div>
<!-- OR -->
<div>
<label for="card_holder">Card holder</label>
<input type="text" id="card_holder" name="card_holder" />
</div>
<div>
<label for="number_div">Card number</label>
<div id="number_div" style="height: 35px; width: 200px;"></div>
</div>
<div>
<label for="cvv_div">CVV</label>
<div id="cvv_div" style="height: 35px; width: 200px;"></div>
</div>
<div>
<label for="exp_month">Month</label>
<input type="text" id="exp_month" name="exp_month" />
</div>
<div>
<label for="exp_year">Year</label>
<input type="text" id="exp_year" name="exp_year" />
</div>
<div>
<label for="email">Email</label>
<input type="text" id="email" name="email" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
3. Initialize the Payment.js library by instantiating a PaymentJs object, and call init() on it. The init method expects the connector's public integration key, id of div containing the credit card number, id of the CVV div, and a callback function as parameters. The callback function will receive the PaymentJs object, and you should call any further methods (formatting, event handlers) directly there (see Method reference).
<script type="text/javascript">
var payment = new PaymentJs();
payment.init('public-integration-key', 'number_div', 'cvv_div', function(payment) {
payment.setNumberStyle({
'border': '1px solid red',
'width': '150px'
});
payment.setCvvStyle({ ... });
payment.numberOn('input', function(data) {
alert('A number was entered');
})
});
</script>
4. Once the user submits the form, you must intercept the submit event and call Payment.js' tokenize
method, passing the additional data, a success callback and an error callback function.
The success callback receives the transaction token as string, you should store it and transmit it to your server together with the rest of the form. Additional data about the card itself will also be passed in the success callback.
The error callback function will receive an array with error objects, containing field name and error message.
/*
This example assumes you have jQuery loaded for accessing DOM elements
*/
function interceptSubmit() {
var data = {
first_name: $('#first_name').val(),
last_name: $('#last_name').val(),
// OR card_holder: $('#card_holder').val(),
month: $('#exp_month').val(),
year: $('#exp_year').val(),
email: $('#email').val()
};
payment.tokenize(
data, //additional data, MUST include card_holder (or first_name & last_name), month and year
function(token, cardData) { //success callback function
$('#transaction_token').val(token); //store the transaction token
$('#payment_form').get(0).submit(); //submit the form
},
function(errors) { //error callback function
alert('Errors occurred');
//render error information here
}
);
}
//error example
[
{
"attribute":"first_name",
"key":"errors.blank",
"message":"First name can't be blank"
}
]
5. You can now call the Transaction API with the transaction token you acquired.
CVV Refreshing
If you already have tokenized a card and stored it via a Register or Debit/Preauthorize with Register transaction, all subsequent transaction will not pass CVC/CVV code to the acquiring bank, because the verification code must not be stored due to PCI regulations.
If you still want to perform card-on-file transactions with CVC/CVV code, you have to present the CVC/CVV input field to the customer again.
For refreshing the CVV you must have the referenceId
of the initial transaction stored in your customer details.
Furthermore we recommend to store the last 4 digits of the card to let your customer know, which card will be used for the payment.
Note: CVV Refresh call is not enabled for the dummy adapter
Use the CVV Refresh call as following:
1. On top of your HTML page (either in HEAD or directly after BODY), include the payment.1.3.min.js as shown here:
<script data-main="payment-js" src="https://gateway.tillpayments.com/js/integrated/payment.1.3.min.js"></script>
2. Build the payment form providing a DIV element with an ID for the CVV/CVC code input field.
<form id="payment_form" method="POST" action="someScript.php" onsubmit="interceptSubmit(); return false;">
<div>
<h1>Payment</h1>
<p>
Your purchase will be made with your stored credit card <b>**** **** **** 1111</b>
</p>
</div>
<div>
<label for="cvv_div">CVV</label>
<div id="cvv_div" style="height: 35px; width: 200px;"></div>
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
3. Initialize the Payment.js library by instantiating an PaymentJs object, and call initCvvRefresh() on it. The method expects the connector's public integration key, referenceId of the initial transaction which was used to store the card, id of the CVV div and a callback function as parameters. The callback function will receive the PaymentJs object, and you should call any further methods (formatting, event handlers) directly there (see Method reference).
<script type="text/javascript">
var payment = new PaymentJs();
payment.initCvvRefresh('public-integration-key', 'referenceTransactionId', 'cvv_div', function(payment) {
payment.setCvvStyle({
'border': '1px solid red',
'width': '75px'
});
payment.cvvOn('input', function(data) {
alert('A number was entered');
});
});
</script>
4. Once the user submits the form, you must intercept the submit event and call Payment.js' refreshCvv
method, passing a success callback and an error callback function.
The success callback will be called once the CVV was successfully updated for the card.
The error callback function will receive an array with error objects, containing field name and error message.
/*
This example assumes you have jQuery loaded for accessing DOM elements
*/
function interceptSubmit() {
payment.refreshCvv(
function() { //success callback function
$('#payment_form').get(0).submit(); //submit the form
},
function(errors) { //error callback function
alert('Errors occured');
//render error information here (see list of error codes below)
}
);
}
//error example
[
{
"attribute": "cvv",
"key": "errors.blank",
"message": "CVV code must not be empty"
}
]
5. You can now call the Transaction API to perform the Debit/Preauthorize with CVV present.
payment.js Error codes
- Invalid Integration Key
{
'attribute': 'integration_key',
'key': 'errors.configuration',
'message': 'Invalid Integration Key'
}
- System Error
{
'attribute': 'integration_key',
'key': 'errors.system',
'message': 'System error occurred, please retry'
}
- Card number empty
{
'attribute': 'number',
'key': 'errors.blank',
'message': 'Card number must not be empty'
}
- CVV/CVC Code empty
{
'attribute': 'cvv',
'key': 'errors.blank',
'message': 'CVV code must not be empty'
}
- Invalid card number
{
'attribute': 'number',
'key': 'errors.invalid',
'message': 'Invalid card number'
}
- Invalid CVV/CVC
{
'attribute': 'cvv',
'key': 'errors.invalid',
'message': 'Invalid CVV code'
}
- Expiration month empty
{
attribute: "month",
key: "errors.blank",
message: "Expiration month must not be empty"
}
- Expiration month invalid
{
attribute: "month",
key: "errors.invalid",
message: "Invalid expiration month"
}
- Expiration year empty
{
attribute: "year",
key: "errors.blank",
message: "Expiration year must not be empty"
}
- Expiration year invalid
{
attribute: "year",
key: "errors.invalid",
message: "Invalid expiration year"
}
- Card expired
{
'attribute': 'year',
'key': 'errors.expired',
'message': 'Card expired'
}
- Card holder empty
{
attribute: "card_holder",
key: "errors.blank",
message: "Card holder must not be empty"
}
- First name empty
{
attribute: "first_name",
key: "errors.blank",
message: "First name must not be empty"
}
- Last name empty
{
attribute: "last_name",
key: "errors.blank",
message: "Last name must not be empty"
}
Method reference
PaymentJs.init(publicIntegrationKey, numberDivId, cvvDivId, completeCallback)
Initializes the PaymentJs object, pass the connector's public integration key (provided to you together with your credentials), the ID of the DIVs which will contain the number and CVV code, and a completeCallback function. This function will receive the PaymentJs object as first argument.
PaymentJs.initCvvRefresh(publicIntegrationKey, referenceTransactionId, cvvDivId, completeCallback)
Initializes the PaymentJs object, pass the connector's public integration key (provided to you together with your credentials), the referenced transaction ID, the ID of the DIV which will contain the CVV code, and a completeCallback function. This function will receive the PaymentJs object as first argument.
PaymentJs.tokenize(additionalData, successCallback, errorCallback)
This submits the sensitive card information to the vaulting server.
additionalData is an object, may containing the following keys: first_name, last_name, month, year, email, phone_number, company, address1, zip, city, state, country
successCallback(token, cardData) will be called upon completion, receiving the token (as string) as first argument, and additional card data as second argument
Example card data
{
"card_type": "visa",
"full_name": "John Smith",
"first_six_digits": 123456,
"last_four_digits": "1234",
"month": 1,
"year": 2020,
fingerprint: "46f7adfeb0a123fb8fcbfasdf6171asd6b3dfas44834c"
}
Possible card_type values are: amex
, diners
, discover
, jcb
, maestro
, mastercard
, uatp
, unionpay
, visa
, visa_electron
errorCallback is called if an error occurs, containing error information (see above).
PaymentJs.refreshCvv(successCallback, errorCallback)
This submits the CVC/CVV code to the vaulting server.
successCallback will be called upon successful completion
errorCallback is called if an error occurs, containing error information (see above).
PaymentJs.initRiskScript(options, completeCallback)
Initialize Risk Scripts required for certain types of risk checks like the Kount risk check.
The first parameter should be an object including the type, e.g. "{type:'kount'}"
,
completeCallback will be called upon successful completion
PaymentJs.setNumberStyle(styleObject)
Sets the style of the number field. The styleObject parameters should be an object with css properties, e.g.
{
"border-color": "red",
"font-size": "10px"
}
PaymentJs.setCvvStyle(styleObject)
Sets the style of the CVV field, for formatting see method above.
PaymentJs.setNumberPlaceholder(placeholderText)
Sets the placeholder text for the number input field
PaymentJs.setCvvPlaceholder(placeholderText)
Sets the placeholder text for the CVV input field
PaymentJs.numberOn(event, callbackFunction)
PaymentJs.cvvOn(event, callbackFunction)
Attach an event listener for the number or CVV input field, respectively. The callbackFunction will receive data about the event, e.g.
Data object
{
"cardType": "visa",
"cvvLength": 3,
"numberLength": 12,
"validCvv": true,
"validNumber": true
}
Possible cardType values are: amex
, diners
, discover
, jcb
, maestro
, mastercard
, uatp
, unionpay
, visa
, visa_electron
For details see event listening
PaymentJs.setRequireCardHolder(boolean)
With this you can disable the requirement of passing a card holder name within the additionalData
object
PaymentJs.enableAutofill()
Enables the autofill handling, see description in chapter Auto-fill card data
PaymentJs.onAutofill(data)
Registers an event handler to receive auto filled data, see description in chapter Auto-fill card data
Deprecated methods
PaymentJs.onNumberInput(callbackFunction)
PaymentJs.onCvvInput(callbackFunction)
Both functions are deprecated in favor of PaymentJs.numberOn('input', eventHandler)
and PaymentJs.cvvOn('input', eventHandler)
, respectively.
Event listening
Both, number and CVV support listening for the following events:
input
focus
andblur
mouseover
andmouseout
enter
andesc
tab
andshift-tab
For all events, the callback function will receive a data object.
var payment = new PaymentJs();
payment.init('public-integration-key', 'number_div', 'cvv_div', function(payment) {
payment.numberOn('input', function(data) {
// Handle input event
console.log(data);
});
payment.numberOn('focus', function(data) {
// Handle focus event
});
});
Example output:
{
"cardType": "visa",
"cvvLength": 3,
"numberLength": 12,
"validCvv": true,
"validNumber": true
}
Possible cardType values are: amex
, diners
, discover
, jcb
, maestro
, mastercard
, uatp
, unionpay
, visa
, visa_electron
Auto-fill card data
Most browsers and password managers support auto-fill of card data in payment forms. Since payment.js renders two iframes for the sensitive card data inputs (card number and CVV/CVC code) it requires special preparation to support the auto-fill feature.
Known restrictions
Because the browser are auto filling all data only in one domain scope, the card number fields should be first in order on your payment form. This is the only way that payment.js can capture all data and passes them to you through the mechanism described below
How to use
Once the complete handler of the init
method is invoked, call the enableAutofill
method to turn it on.
Additionally register a onAutofill
handler to receive updates on the auto-fill event.
var payment = new PaymentJs();
payment.init('public-integration-key', 'number_div', 'cvv_div', function(payment) {
payment.enableAutofill();
payment.onAutofill(function(data) {
/* data contains an object like this:
data = {
card_holder: "card holder name",
month: 12,
year: 2099
}
you should apply them to the input fields on your payment form.
Example:
*/
$('#field_id_of_cardholder').val(data.card_holder);
$('#field_id_of_month').val(data.month);
$('#field_id_of_year').val(data.year);
}
});
Advanced form styling
Using PaymentJs.setNumberStyle()
/PaymentJs.setCvvStyle()
in
combination with event listening allows styling the input fields similar to the
rest of your forms.
While the PaymentJs provides the most seamless integration for a shop, it has one limitation due to PCI requirements: external resources most not be loaded. Due to this constraint, it is not possible to load external fonts or background images.
var payment = new PaymentJs();
payment.init('public-integration-key', 'number_div', 'cvv_div', function(payment) {
var numberFocused = false;
var cvvFocused = false;
var style = {
'border': '3px solid gray',
};
var hoverStyle = {
'border': '3px solid blue',
};
var focusStyle = {
'border': '3px solid green',
};
// Set the initial style
payment.setNumberStyle(style);
payment.setCvvStyle(style);
// Focus events
payment.numberOn('focus', function() {
numberFocused = true;
payment.setNumberStyle(focusStyle);
});
payment.cvvOn('focus', function() {
cvvFocused = true;
payment.setCvvStyle(focusStyle);
});
// Blur events
payment.numberOn('blur', function() {
numberFocused = false;
payment.setNumberStyle(style);
});
payment.cvvOn('blur', function() {
cvvFocused = false;
payment.setCvvStyle(style);
});
// Hover events
payment.numberOn('mouseover', function() {
// Don't override style if element is already focused
if(! numberFocused) {
payment.setNumberStyle(hoverStyle);
}
});
payment.numberOn('mouseout', function() {
// Don't override style if element is already focused
if(! numberFocused) {
payment.setNumberStyle(style);
}
});
payment.cvvOn('mouseover', function() {
// Don't override style if element is already focused
if(! cvvFocused) {
payment.setCvvStyle(hoverStyle);
}
});
payment.cvvOn('mouseout', function() {
// Don't override style if element is already focused
if(! cvvFocused) {
payment.setCvvStyle(style);
}
});
});
3D-Secure Authentication
Introduction
Depending on your configuration you may be eligible to use 3D Secure authentication for card transactions. Your integration engineer will tell you, which version is applicable for you.
3D Secure 2.x
The 3D Secure 2.x protocol facilitates a lot more options to identify your customer.
Generally there are 2 possible authentication flows available:
- Frictionless flow
- Challenge flow
Depending on the data provided, the card issuing bank determines which flow to apply. In the frictionless flow no further customer interaction is required, in the challenge flow the customer will be redirected to its bank's authentication page.
The Gateway automatically handles any necessary data exchanges and redirects. The transaction response will only ask your system once to redirect the customer.
To improve your chances to apply for the frictionless flow, you should transmit as many 3D Secure related data as you have.
Refer to 3D-Secure 2.x Fields for detailed field documentation.
Transaction-based activation
Some connectors support 3D-Secure Payer Authentication (i.e. "Verified by Visa", "MasterCard SecureCode").
If your setup allows you to decide whether to activate 3D-Secure or not, you can control it via an extraData key/value pair in the transaction request. Alternatively it might also be enabled based on your risk profile. The following three values are possible:
Value | Description |
---|---|
OFF | 3D-Secure Verification is turned off. It can still occur that the Verification gets enabled by certain risk parameters. |
OPTIONAL | If the payer is enrolled into 3D-Secure program, the verification will be performed. If not, the transaction will still be processed. |
MANDATORY | If 3D-Secure verification is not possible (e.g. Payer not enrolled, Server not reachable), the transaction will be declined. |
The extraData element must have the key "3dsecure".
<extraData key="3dsecure">MANDATORY</extraData>
<?php
$debit->addExtraData('3dsecure', 'MANDATORY');
External Risk Scripts
Kount - Init risk script
Certain types of risk checks require to initialize risk scripts, e.g. the Kount risk check.
When using PaymentJS initialize the Kount Risk Script as follows:
<script type="text/javascript">
/**
* With using PaymentJS when the connector related to your "Public Integration Key" is
* not configured to init Kount automatically.
* If the "Automatic" is enabled no javascript steps are required at all for embedding Kount.
**/
var payment = new PaymentJs("1.2");
payment.init('public-integration-key', 'number_div', 'cvv_div', function(payment) {
// ...
payment.initRiskScript({type:'kount'});
});
</script>
Without using PaymentJs initialize the Kount Risk Script as follows:
<script type="text/javascript" src="https://gateway.tillpayments.com/js/risk-scripts/kount-dc.min.js"></script>
<script type="text/javascript">
/**
* Initializing the Kount Risk Script without PaymentJS
*
**/
var kountMerchantId = 'kount-merchant-id';
var kountTestMode = false; // using Kount sandbox or not
var kountHandler = new KountDcHandler(kountMerchantId, kountTestMode);
kountHandler.initKountDc(function(kountSessionId) {
$('#hidden-kount-session-id').val(kountSessionId); // example with using jQuery
});
</script>
Forter - Init risk script
Certain types of risk checks require to initialize risk scripts, e.g. the Forter risk check.
When using PaymentJS initialize the Forter Risk Script as follows:
<script type="text/javascript">
/**
* With using PaymentJS when the connector/risk profile related to your "Public Integration Key" is
* not configured to init Forter automatically.
* If "Init Scripts Automatically" is enabled no further javascript steps are required at all for embedding Forter.
**/
var payment = new PaymentJs("1.2");
payment.init('public-integration-key', 'number_div', 'cvv_div', function(payment) {
// ...
//required if automatic script initialization for Forter is NOT enabled
try{
payment.initRiskScript({type:'forter'});
} catch (exception) {
//this might happen on an invalid configuration at risk profile or connector level
}
});
</script>
Without using PaymentJs initialize the Forter Risk Script as follows:
<script type="text/javascript" src="https://gateway.tillpayments.com/js/risk-scripts/forter-dc.min.js"></script>
<script type="text/javascript">
/**
* Initializing the Forter Risk Script without PaymentJS
*
**/
var forterSiteId = 'forter-site-id'; //retrieve from your Forter dashbaord differs from PROD / DEV environment
var forterHandler = new ForterDcHandler(forterSiteId);
forterHandler.initForterDc(function(forterToken) {
$('#hidden-forter-token-id').val(forterToken); // provide as transaction extra data FORTER_TOKEN or forter_token
);
</script>
FraudNet - Init risk script
Certain types of risk checks require to initialize risk scripts, e.g. the FraudNet risk check.
When using PaymentJS initialize the FraudNet Risk Script as follows:
<script type="text/javascript">
/**
* With using PaymentJS when the connector/risk profile related to your "Public Integration Key" is
* not configured to init FraudNet automatically.
* If "Init Scripts Automatically" is enabled no further javascript steps are required at all for embedding FraudNet.
**/
var payment = new PaymentJs("1.2");
payment.init('public-integration-key', 'number_div', 'cvv_div', function(payment) {
// ...
//required if automatic script initialization for FraudNet is NOT enabled
try{
payment.initRiskScript({type:'fraudNet'});
} catch (exception) {
//this might happen on an invalid configuration at risk profile or connector level
}
});
</script>
Without using PaymentJs initialize the FraudNet Risk Script as follows:
<script type="text/javascript" src="https://gateway.tillpayments.com/js/risk-scripts/fraudNet-dc.min.js"></script>
<script type="text/javascript">
/**
* Initializing the FraudNet Risk Script without PaymentJS
*
**/
var fraudNetHandler = new FraudNetDcHandler();
fraudNetHandler.initFraudNetDc(function(fraudNet_fingerprint_id) {
$('#hidden-fraudNet-fingerprint-id').val(fraudNet_fingerprint_id); // provide as transaction extra data FRAUDNET_DEVICE_FINGERPRINT_ID or fraudNet_fingerprint_id
});
</script>
Integration Guides
Seamless Card payments using payment.js
With this integration flow you collect & tokenize card data directly on your checkout page and send the transaction afterwards Server-to-Server.
Advantages:
- Seamless flow, native user experience
- Full control about look & feel
Disadvantages:
- Integration effort slightly higher as with hosted payment page
1. Collect card data
<html>
<head>
<title>Your checkout page</title>
</head>
<body>
<script data-main="payment-js" src="https://gateway.tillpayments.com/js/integrated/payment.1.3.min.js"></script>
<div>
Checkout details:
...
</div>
<div>
Please enter your card details:
</div>
<form id="payment_form" method="POST" action="/path/to/your/yourPaymentHandler" onsubmit="interceptSubmit(); return false;">
<!-- add additional fields for your internal processing if necessary -->
<input type="hidden" name="transaction_token" id="transaction_token" />
<div>
<label for="card_holder">Card holder</label>
<input type="text" id="card_holder" name="card_holder" />
</div>
<div>
<label for="number_div">Card number</label>
<div id="number_div" style="height: 35px; width: 200px;"></div>
</div>
<div>
<label for="cvv_div">CVV</label>
<div id="cvv_div" style="height: 35px; width: 200px;"></div>
</div>
<div>
<label for="exp_month">Expiration Month</label>
<input type="text" id="exp_month" name="exp_month" />
</div>
<div>
<label for="exp_year">Expiration Year</label>
<input type="text" id="exp_year" name="exp_year" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
<script type="text/javascript">
var payment = new PaymentJs();
payment.init('public-integration-key', 'number_div', 'cvv_div', function(payment) {
payment.setNumberStyle({
'border': '1px solid black',
'width': '150px'
});
payment.setCvvStyle({ ... });
payment.numberOn('input', function(data) {
alert('A number was entered');
})
});
function interceptSubmit() {
var data = {
card_holder: $('#card_holder').val(),
month: $('#exp_month').val(),
year: $('#exp_year').val()
};
payment.tokenize(
data, //additional data, MUST include card_holder (or first_name & last_name), month and year
function(token, cardData) { //success callback function
$('#transaction_token').val(token); //store the transaction token
$('#payment_form').get(0).submit(); //submit the form
},
function(errors) { //error callback function
alert('Errors occurred');
//render error information here
}
);
}
</script>
<body>
</html>
Integrate payment.js library in your Checkout page to collect and tokenize customer's card details.
The payment.js library renders 2 separate iFrames for card number and CVV/CVC in your checkout page. This reduces your PCI-DSS scope to as low as it can get (PCI-DSS SAQ-A).
As a result of the JavaScript flow, you will receive a transactionToken
, identifying the customer's card, which you pass to the Transaction API request.
See more details here: payment.js Documentation.
2. Send transaction
POST https://gateway.tillpayments.com/api/v3/transaction/{INSERT_API_KEY_HERE}/debit
Authorization: .... (use your API User & Password)
X-Signature: .... (optional, may be necessary if required by your merchant configuration)
{
"merchantTransactionId": "2019-09-02-0001",
"transactionToken": "TOKEN_FROM_PAYMENTJS",
"merchantMetaData": "merchantRelevantData",
"amount": "9.99",
"currency": "EUR",
"successUrl": "http://example.com/success",
"cancelUrl": "http://example.com/cancel",
"errorUrl": "http://example.com/error",
"callbackUrl": "http://example.com/callback",
"description": "Example Product",
"customer": {
"identification": "c0001",
"firstName": "John",
"lastName": "Doe",
"birthDate": "1990-10-10",
"gender": "M",
"billingAddress1": "Maple Street 1",
"billingAddress2": "Syrup Street 2",
"billingCity": "Victoria",
"billingPostcode": "V8W",
"billingState": "British Columbia",
"billingCountry": "CA",
"billingPhone": "1234567890",
"shippingFirstName": "John",
"shippingLastName": "Doe",
"shippingCompany": "Big Company Inc.",
"shippingAddress1": "Yellow alley 3",
"shippingAddress2": "Yellow alley 4",
"shippingCity": "Victoria",
"shippingPostcode": "V8W",
"shippingState": "British Columbia",
"shippingCountry": "CA",
"shippingPhone": "1234567890",
"company": "John's Maple Syrup",
"email": "[email protected]",
"emailVerified": false,
"ipAddress": "127.0.0.1",
"nationalId": "1234",
"extraData": {
"someCustomerDataKey": "value",
"anotherKey": "anotherValue"
}
},
"extraData": {
"someKey": "someValue",
"otherKey": "otherValue"
},
"threeDSecureData": {
"3dsecure": "MANDATORY"
},
"language": "en"
}
In this step you will now send a transaction request from your backend server to our server.
Depending on your business model, this can either be a Debit
or a Preauthorize
request.
Note that the Preauthorization must be captured afterwards to conclude the transaction.
The mandatory fields for this request, especially the end-customer data, greatly depends on the configuration of your merchant account. As a rule of thumb we would recommend to send as much available data as possible, to receive the most benefits from risk checks, 3D-secure authentication and any other validations.
Details about the format and requirements about the transaction request can be found here:
3a. Conditional: 3D Secure Authentication
{
"success": true,
"uuid": "abcde12345abcde12345",
"purchaseId": "20190927-abcde12345abcde12345",
"returnType": "REDIRECT",
"redirectUrl": "http://redirectUrlHere.com/some/path",
"paymentMethod": "Creditcard"
}
If 3D-Secure authentication is mandatory for your merchant account, or was requested through the 3dsecure
parameter, you will receive a result indicating that you must redirect the customer to the 3D-Secure authentication page of the card issuer.
You should read the redirectUrl
value of the transaction response and redirect the customer's browser to this URL.
After authentication the customer will return back to the successUrl
you have defined in Step 2. (Or to errorUrl
in case of failure).
Note: Before the customer is redirected back to you, the Gateway sends you the asynchronous postback notification as described in Step 4. So you can immediately show the payment outcome to your customer.
Depending on your application, there might be various options to perform the actual redirect.
Two possible generic approaches are either via Location
header, or via JavaScript.
// Location header:
Location: http://redirectUrlHere.com/some/path
// Javascript:
window.location.href = "http://redirectUrlHere.com/some/path";
3b. Without 3D Secure Authentication
Success Result:
{
"success": true,
"uuid": "abcde12345abcde12345",
"purchaseId": "20190927-abcde12345abcde12345",
"returnType": "FINISHED",
"paymentMethod": "Creditcard"
}
Error Result:
{
"success": false,
"uuid": "abcde12345abcde12345"
"purchaseId": "20190927-abcde12345abcde12345",
"returnType": "ERROR",
"paymentMethod": "Creditcard",
"errors": [
{
"errorMessage": "Transaction declined",
"errorCode": 2003,
"adapterMessage": "Do not honour",
"adapterCode": "05"
}
]
}
If no 3D-Secure Authentication is necessary, you will directly receive the transaction result in the response.
Depending on the outcome the customer's purchase is either done, or in case of errors you might want to present your customer the payment page again to choose a different payment method.
4. Handle asynchronous postback notification
POST http://example.com/callback
X-Signature: EXAMPLE_SIGNATURE...
Date: Mon, 01 Jan 2018 11:01:36 GMT
Content-Type: application/json; charset=utf-8
{
"result": "OK",
"uuid": "abcde12345abcde12345",
"merchantTransactionId": "auto-2019-09-02-0012",
"purchaseId": "20191210-abcde12345abcde12345",
"transactionType": "DEBIT",
"paymentMethod": "Creditcard",
"amount": "9.99",
"currency": "EUR",
"returnData": {
"cardData": {
"type": "visa",
"cardHolder": "John Doe",
"expiryMonth": 12,
"expiryYear": 2020,
"binDigits": "12345678",
"firstSixDigits": "123456",
"lastFourDigits": "4321",
"fingerprint": "s0mEF1n6eRpr1n7",
"threeDSecure": "OFF",
"binBrand": "VISA",
"binBank": "SOME BANK",
"binCountry": "US"
}
}
}
In almost any case the Gateway will send a postback notification about the transaction outcome.
Only if your request could not be processed at all, there will be no postback notification, only a response with the error description.
E.g. due to invalid request format, invalid authentication etc.
The Postback Notification contains the result of the transaction and optionally additional data, such as card details or customer information.
Signature Validation:
To validate to signature of the postback notification, you have to rebuild the signature using the shared secret provided to you along with any other credentials.
This calculated signature must be compared to the one provided in the X-Signature
header of the notification request. (Note: using the API Version 2, the relevant header is Authorization
).
The signature is generated via Hash HMAC using SHA2-512 as hashing algorithm. Afterwards the binary (not hexadecimal!) HMAC must be Base64 encoded.
The secret key for the HMAC is the shared secret of the Gateway's connector.
The message is the concatenation of the following data, separated by a single line-break without carriage-return character, i.e. \n
- HTTP Method (i.e.
POST
) - SHA512 hash of the request's body (the JSON string) - Note: In previous versions this is MD5 instead.
- Content-Type header value
- Timestamp as sent in the
Date
header - Request URI (e.g.
/api/v3/transaction/<api-key>/debit
)
Example:
- API Key is:
my-api-key
- Shared Secret is:
my-shared-secret
- JSON Body is:
{"merchantTransactionId":"2019-09-02-0004","amount":"9.99","currency":"EUR"}
- Content-Type is:
application/json; charset=utf-8
- Timestamp is:
Tue, 21 Jul 2020 13:15:03 UTC
- Request URI is:
/api/v3/transaction/my-api-key/debit
- SHA512 Hash of body:
efe0b7cd39d6904dc90924b1a89629b14f11082ed2178cff562364ca0172318e1535bb8766fbe66e8cc44d311eba806349bfe185607eca12d9d0f377a03ee617
- Concatenation result (=message):
POST
efe0b7cd39d6904dc90924b1a89629b14f11082ed2178cff562364ca0172318e1535bb8766fbe66e8cc44d311eba806349bfe185607eca12d9d0f377a03ee617
application/json; charset=utf-8
Tue, 21 Jul 2020 13:15:03 UTC
/api/v3/transaction/my-api-key/debit
3. HMAC(sha512, my-shared-secret, message), Base64 encoded = OWUJdoWyWsYVi4QGPB1H79vfI6XrPyDZ/Y1SLInHDprvmYxs8ECLo4NQINnA3k/QM/8fG5s2aIcToDdnOlswuQ==
4. Compare this value to the one provided in the header. If it matches, you can trust the notification.
5. Optional: Capture/Void the Authorization
CAPTURE:
POST https://gateway.tillpayments.com/api/v3/transaction/{INSERT_API_KEY_HERE}/capture
Authorization: .... (use your API User & Password)
X-Signature: .... (optional, may be necessary if required by your merchant configuration)
{
"merchantTransactionId": "2019-09-02-0003",
"referenceUuid": "bcdef23456bcdef23456",
"amount": "9.99",
"currency": "EUR"
}
VOID:
POST https://gateway.tillpayments.com/api/v3/transaction/{INSERT_API_KEY_HERE}/void
Authorization: .... (use your API User & Password)
X-Signature: .... (optional, may be necessary if required by your merchant configuration)
{
"merchantTransactionId": "2019-09-02-0003",
"referenceUuid": "bcdef23456bcdef23456"
}
If you performed a Preauthorize
transaction, you must send a Capture
transaction request to conclude the payment, or a Void
to cancel it.
Note that the Capture must be performed within a certain time frame. Usually this is 7 days, but can be different based on your business model and payment method.
Card payments using Hosted Payment Pages
With this integration flow all payment relevant data will be collected on the Hosted Payment Page of the Gateway, meaning you have to redirect the customer to the URL you will be given with the initial transaction response.
Advantages:
- Fastest way to integrate
- No need to customize anything on your checkout page
Disadvantages:
- Customer leaves your checkout page
- Less control than with seamless flow
1. Send transaction
POST https://gateway.tillpayments.com/api/v3/transaction/{INSERT_API_KEY_HERE}/debit
Authorization: .... (use your API User & Password)
X-Signature: .... (optional, may be necessary if required by your merchant configuration)
{
"merchantTransactionId": "2019-09-02-0001",
"merchantMetaData": "merchantRelevantData",
"amount": "9.99",
"currency": "EUR",
"successUrl": "http://example.com/success",
"cancelUrl": "http://example.com/cancel",
"errorUrl": "http://example.com/error",
"callbackUrl": "http://example.com/callback",
"description": "Example Product",
"customer": {
"identification": "c0001",
"firstName": "John",
"lastName": "Doe",
"birthDate": "1990-10-10",
"gender": "M",
"billingAddress1": "Maple Street 1",
"billingAddress2": "Syrup Street 2",
"billingCity": "Victoria",
"billingPostcode": "V8W",
"billingState": "British Columbia",
"billingCountry": "CA",
"billingPhone": "1234567890",
"shippingFirstName": "John",
"shippingLastName": "Doe",
"shippingCompany": "Big Company Inc.",
"shippingAddress1": "Yellow alley 3",
"shippingAddress2": "Yellow alley 4",
"shippingCity": "Victoria",
"shippingPostcode": "V8W",
"shippingState": "British Columbia",
"shippingCountry": "CA",
"shippingPhone": "1234567890",
"company": "John's Maple Syrup",
"email": "[email protected]",
"emailVerified": false,
"ipAddress": "127.0.0.1",
"nationalId": "1234",
"extraData": {
"someCustomerDataKey": "value",
"anotherKey": "anotherValue"
}
},
"extraData": {
"someKey": "someValue",
"otherKey": "otherValue"
},
"threeDSecureData": {
"3dsecure": "MANDATORY"
},
"language": "en"
}
First you will now send a transaction request from your backend server to our server.
Depending on your business model and payment method, this can either be a Debit
or a Preauthorize
request.
Note that the Preauthorization must be captured afterwards to conclude the transaction.
The mandatory fields for this request, especially the end-customer data, greatly depends on the configuration of your merchant account. As a rule of thumb we would recommend to send as much available data as possible, to receive the most benefits from risk checks, 3D-secure authentication and any other validations.
Details about the format and requirements about the transaction request can be found here:
2. Redirect to Hosted Payment Page
{
"success": true,
"uuid": "abcde12345abcde12345",
"purchaseId": "20190927-abcde12345abcde12345",
"returnType": "REDIRECT",
"redirectUrl": "http://redirectUrlHere.com/some/path",
"paymentMethod": "Creditcard"
}
You will receive a result indicating that you must redirect the customer to the Hosted Payment Page.
You should read the redirectUrl
value of the transaction response and redirect the customer's browser to this URL.
After authentication the customer will return back to the successUrl
you have defined in Step 1. (Or to errorUrl
in case of failure).
Note: Before the customer is redirected back to you, the Gateway sends you the asynchronous postback notification as described in Step 3. So you can immediately show the payment outcome to your customer.
Depending on your application, there might be various options to perform the actual redirect.
Two possible generic approaches are either via Location
header, or via JavaScript.
// Location header:
Location: http://redirectUrlHere.com/some/path
// Javascript:
window.location.href = "http://redirectUrlHere.com/some/path";
3. Handle asynchronous postback notification
POST http://example.com/callback
X-Signature: EXAMPLE_SIGNATURE...
Date: Mon, 01 Jan 2018 11:01:36 GMT
Content-Type: application/json; charset=utf-8
{
"result": "OK",
"uuid": "abcde12345abcde12345",
"merchantTransactionId": "auto-2019-09-02-0012",
"purchaseId": "20191210-abcde12345abcde12345",
"transactionType": "DEBIT",
"paymentMethod": "Creditcard",
"amount": "9.99",
"currency": "EUR",
"returnData": {
"cardData": {
"type": "visa",
"cardHolder": "John Doe",
"expiryMonth": 12,
"expiryYear": 2020,
"binDigits": "12345678",
"firstSixDigits": "123456",
"lastFourDigits": "4321",
"fingerprint": "s0mEF1n6eRpr1n7",
"threeDSecure": "OFF",
"binBrand": "VISA",
"binBank": "SOME BANK",
"binCountry": "US"
}
}
}
In almost any case the Gateway will send a postback notification about the transaction outcome.
Only if your request could not be processed at all, there will be no postback notification, only a response with the error description.
E.g. due to invalid request format, invalid authentication etc.
The Postback Notification contains the result of the transaction and optionally additional data, such as card details or customer information.
Signature Validation:
To validate to signature of the postback notification, you have to rebuild the signature using the shared secret provided to you along with any other credentials.
This calculated signature must be compared to the one provided in the X-Signature
header of the notification request. (Note: using the API Version 2, the relevant header is Authorization
).
The signature is generated via Hash HMAC using SHA2-512 as hashing algorithm. Afterwards the binary (not hexadecimal!) HMAC must be Base64 encoded.
The secret key for the HMAC is the shared secret of the Gateway's connector.
The message is the concatenation of the following data, separated by a single line-break without carriage-return character, i.e. \n
- HTTP Method (i.e.
POST
) - SHA512 hash of the request's body (the JSON string) - Note: In previous versions this is MD5 instead.
- Content-Type header value
- Timestamp as sent in the
Date
header - Request URI (e.g.
/api/v3/transaction/<api-key>/debit
)
Example:
- API Key is:
my-api-key
- Shared Secret is:
my-shared-secret
- JSON Body is:
{"merchantTransactionId":"2019-09-02-0004","amount":"9.99","currency":"EUR"}
- Content-Type is:
application/json; charset=utf-8
- Timestamp is:
Tue, 21 Jul 2020 13:15:03 UTC
- Request URI is:
/api/v3/transaction/my-api-key/debit
- SHA512 Hash of body:
efe0b7cd39d6904dc90924b1a89629b14f11082ed2178cff562364ca0172318e1535bb8766fbe66e8cc44d311eba806349bfe185607eca12d9d0f377a03ee617
- Concatenation result (=message):
POST
efe0b7cd39d6904dc90924b1a89629b14f11082ed2178cff562364ca0172318e1535bb8766fbe66e8cc44d311eba806349bfe185607eca12d9d0f377a03ee617
application/json; charset=utf-8
Tue, 21 Jul 2020 13:15:03 UTC
/api/v3/transaction/my-api-key/debit
3. HMAC(sha512, my-shared-secret, message), Base64 encoded = OWUJdoWyWsYVi4QGPB1H79vfI6XrPyDZ/Y1SLInHDprvmYxs8ECLo4NQINnA3k/QM/8fG5s2aIcToDdnOlswuQ==
4. Compare this value to the one provided in the header. If it matches, you can trust the notification.
4. Optional: Capture/Void the Authorization
CAPTURE:
POST https://gateway.tillpayments.com/api/v3/transaction/{INSERT_API_KEY_HERE}/capture
Authorization: .... (use your API User & Password)
X-Signature: .... (optional, may be necessary if required by your merchant configuration)
{
"merchantTransactionId": "2019-09-02-0003",
"referenceUuid": "bcdef23456bcdef23456",
"amount": "9.99",
"currency": "EUR"
}
VOID:
POST https://gateway.tillpayments.com/api/v3/transaction/{INSERT_API_KEY_HERE}/void
Authorization: .... (use your API User & Password)
X-Signature: .... (optional, may be necessary if required by your merchant configuration)
{
"merchantTransactionId": "2019-09-02-0003",
"referenceUuid": "bcdef23456bcdef23456"
}
Alternative Payment Methods
Almost all alternative payment methods facilitate the Full-Page Redirect Flow, which means the customer leaves your Checkout page to conclude the payment on the website of the various payment providers.
1. Send transaction
POST https://gateway.tillpayments.com/api/v3/transaction/{INSERT_API_KEY_HERE}/debit
Authorization: .... (use your API User & Password)
X-Signature: .... (optional, may be necessary if required by your merchant configuration)
{
"merchantTransactionId": "2019-09-02-0001",
"additionalId1": "x0001",
"additionalId2": "y0001",
"extraData": {
"someKey": "someValue",
"otherKey": "otherValue"
},
"merchantMetaData": "merchantRelevantData",
"amount": "9.99",
"currency": "EUR",
"successUrl": "http://example.com/success",
"cancelUrl": "http://example.com/cancel",
"errorUrl": "http://example.com/error",
"callbackUrl": "http://example.com/callback",
"description": "Example Product",
"customer": {
"identification": "c0001",
"firstName": "John",
"lastName": "Doe",
"birthDate": "1990-10-10",
"gender": "M",
"billingAddress1": "Maple Street 1",
"billingAddress2": "Syrup Street 2",
"billingCity": "Victoria",
"billingPostcode": "V8W",
"billingState": "British Columbia",
"billingCountry": "CA",
"billingPhone": "1234567890",
"shippingFirstName": "John",
"shippingLastName": "Doe",
"shippingCompany": "Big Company Inc.",
"shippingAddress1": "Yellow alley 3",
"shippingAddress2": "Yellow alley 4",
"shippingCity": "Victoria",
"shippingPostcode": "V8W",
"shippingState": "British Columbia",
"shippingCountry": "CA",
"shippingPhone": "1234567890",
"company": "John's Maple Syrup",
"email": "[email protected]",
"emailVerified": false,
"ipAddress": "127.0.0.1",
"nationalId": "1234",
"extraData": {
"someCustomerDataKey": "value",
"anotherKey": "anotherValue"
}
},
"extraData": {
"someKey": "someValue",
"otherKey": "otherValue"
},
"threeDSecureData": {
"3dsecure": "MANDATORY"
},
"language": "en"
}
First you will now send a transaction request from your backend server to our server.
Depending on your business model and payment method, this can either be a Debit
or a Preauthorize
request.
Note that the Preauthorization must be captured afterwards to conclude the transaction.
The mandatory fields for this request, especially the end-customer data, greatly depends on the configuration of your merchant account and the used payment method. As a rule of thumb we would recommend to send as much available data as possible, to receive the most benefits from risk checks, 3D-secure authentication and any other validations.
Details about the format and requirements about the transaction request can be found here:
2. Redirect to Hosted Payment Page
{
"success": true,
"uuid": "abcde12345abcde12345",
"purchaseId": "20190927-abcde12345abcde12345",
"returnType": "REDIRECT",
"redirectUrl": "http://redirectUrlHere.com/some/path",
"paymentMethod": "Creditcard"
}
You will receive a result indicating that you must redirect the customer to the Hosted Payment Page.
You should read the redirectUrl
value of the transaction response and redirect the customer's browser to this URL.
After authentication the customer will return back to the successUrl
you have defined in Step 1. (Or to errorUrl
in case of failure).
Note: Before the customer is redirected back to you, the Gateway sends you the asynchronous postback notification as described in Step 3. So you can immediately show the payment outcome to your customer.
Depending on your application, there might be various options to perform the actual redirect.
Two possible generic approaches are either via Location
header, or via JavaScript.
// Location header:
Location: http://redirectUrlHere.com/some/path
// Javascript:
window.location.href = "http://redirectUrlHere.com/some/path";
3. Handle asynchronous postback notification
POST http://example.com/callback
X-Signature: EXAMPLE_SIGNATURE...
Date: Mon, 01 Jan 2018 11:01:36 GMT
Content-Type: application/json; charset=utf-8
{
"result": "OK",
"uuid": "abcde12345abcde12345",
"merchantTransactionId": "auto-2019-09-02-0012",
"purchaseId": "20191210-abcde12345abcde12345",
"transactionType": "DEBIT",
"paymentMethod": "PayPal",
"amount": "9.99",
"currency": "EUR"
}
In almost any case the Gateway will send a postback notification about the transaction outcome.
Only if your request could not be processed at all, there will be no postback notification, only a response with the error description.
E.g. due to invalid request format, invalid authentication etc.
The Postback Notification contains the result of the transaction and optionally additional data, such as card details or customer information.
Signature Validation:
To validate to signature of the postback notification, you have to rebuild the signature using the shared secret provided to you along with any other credentials.
This calculated signature must be compared to the one provided in the X-Signature
header of the notification request. (Note: using the API Version 2, the relevant header is Authorization
).
The signature is generated via Hash HMAC using SHA2-512 as hashing algorithm. Afterwards the binary (not hexadecimal!) HMAC must be Base64 encoded.
The secret key for the HMAC is the shared secret of the Gateway's connector.
The message is the concatenation of the following data, separated by a single line-break without carriage-return character, i.e. \n
- HTTP Method (i.e.
POST
) - SHA512 hash of the request's body (the JSON string) - Note: In previous versions this is MD5 instead.
- Content-Type header value
- Timestamp as sent in the
Date
header - Request URI (e.g.
/api/v3/transaction/<api-key>/debit
)
Example:
- API Key is:
my-api-key
- Shared Secret is:
my-shared-secret
- JSON Body is:
{"merchantTransactionId":"2019-09-02-0004","amount":"9.99","currency":"EUR"}
- Content-Type is:
application/json; charset=utf-8
- Timestamp is:
Tue, 21 Jul 2020 13:15:03 UTC
- Request URI is:
/api/v3/transaction/my-api-key/debit
- SHA512 Hash of body:
efe0b7cd39d6904dc90924b1a89629b14f11082ed2178cff562364ca0172318e1535bb8766fbe66e8cc44d311eba806349bfe185607eca12d9d0f377a03ee617
- Concatenation result (=message):
POST
efe0b7cd39d6904dc90924b1a89629b14f11082ed2178cff562364ca0172318e1535bb8766fbe66e8cc44d311eba806349bfe185607eca12d9d0f377a03ee617
application/json; charset=utf-8
Tue, 21 Jul 2020 13:15:03 UTC
/api/v3/transaction/my-api-key/debit
3. HMAC(sha512, my-shared-secret, message), Base64 encoded = OWUJdoWyWsYVi4QGPB1H79vfI6XrPyDZ/Y1SLInHDprvmYxs8ECLo4NQINnA3k/QM/8fG5s2aIcToDdnOlswuQ==
4. Compare this value to the one provided in the header. If it matches, you can trust the notification.
Special Use-Cases
Intermediate Redirects
For transactions which are processed through a so called "Payment Selection Page", it is possible to redirect the customer back to your checkout flow, before the actual chosen payment method is processed.
Sample Flow
- Merchant: Send
Debit
transaction to the Gateway, include theintermediateUrl
extraData parameter (see below) - Merchant: Redirect the customer's browser to the
redirectUrl
of the response - Gateway: Presents payment selection page
- Customer: Chooses payment method on payment selection page
- Gateway: Sends postback notification with status
PENDING
and extraDatacontinueUrl
to the given callbackUrl (alternatively can be queried through the Status API) - Gateway: Redirects customer's browser back to the
intermediateUrl
. The Gateway appends acontinueUrl
parameter to the page's URL. - Merchant: Present checkout finalization to customer
- Customer: Clicks e.g. "Finalize purchase now"
- Merchant: Redirect customer's browser to the
continueUrl
(from the GET parameters or from postback notification resp. Status API) - Gateway: Presents actual payment page to customer
- Customer: Finalizes payment at the PSP
- Gateway: Sends final postback notification to merchant's
postbackUrl
- Gateway: Redirects customer back to the merchant's
successUrl
(resp.errorUrl
in case of payment failure) - Merchant: Presents "Thank you for your purchase" page
Technical details
Beside all the transaction details needed as for a regular transaction flow, the following data must be considered:
- Transmit the
extraData
value for keyintermediateUrl
: This is the URL where we redirect the customer after selecting the payment method.
E.g.
<extraData key="intermediateUrl">https://my.page/checkout/finalize</extraData>
- Read the
extraData
value with keycontinueUrl
from the postback notification: This is the URL where you must redirect the customer to finalize the payment.
In postback notification:
<extraData key="continueUrl">https://gateway.tillpayments.com/redirect/....</extraData>
- Alternatively: Retrieve the
continueUrl
via the Status API - Alternatively: Read the
continueUrl
parameter, which we append to yourintermediateUrl
.
(Example: you provided https://my.page/checkout/finalize
, we redirect to https://my.page/checkout/finalize?continueUrl=https://url.to/gateway/for/continue