Introduction
Zota MG Deposit API enables Zota merchants to:
- Accept online payments from their customers.
- Query for a transaction status on-demand.
- Get real-time updates for events that occur within customer's payment process.
Before You Begin
In order to use this API, a merchant must first receive from Zota the following:
Name | Description |
---|---|
MerchantID |
A merchant unique identifier, used for identification. |
MerchantSecretKey |
A secret key to keep privately and securely, used for authentication. |
EndpointID |
One or more unique endpoint identifiers to use in API requests. |
API URL
URL | Description |
---|---|
https://api.zotapay-sandbox.com |
Sandbox environment, used for integration and testing purposes. |
https://api.zotapay.com or https://mg-api.zotapay.com |
Live environment. |
Deposit Flow
Payment Form Integration
- End-user requests a payment from merchant server.
- Merchant server sends a
Deposit Request
to Zota server. - Zota server responds with an
orderID
, a unique order identifier, and with adepositUrl
, url of the payment page. - Merchant server redirects the waiting end-user to
depositUrl
. - Merchant server starts sending an
Order Status Request
to Zota servers every 10-15 seconds, until end-user returns or a callback arrives. - End-user submits his payment, and is being redirected back to merchant
redirectUrl
. - Finally, Zota server sends merchant server
callbackUrl
a callback notification of the payment result (*).
Card Payment Integration
- End-user submits his card details to merchant server.
- Merchant server sends a
Deposit Request
to Zota server and receives in the responsedepositUrl
which is the URL to which the request containing the card data should be posted. - Merchant server sends request with card data to the provided deposit URL in the previous step.
- Zota server responds with an
orderID
, a unique order identifier, and with astatus
, the status of the requested order (e.gPROCESSING
). - In the meanwhile, merchant displays a 'waiting page' to the end-user.
- Merchant server starts sending an
Order Status Request
to Zota servers every 10-15 seconds, until order gets the final status or gets the status PENDING withurl3dSecure
. - If callbackUrl is included in the
Deposit Request
, the final status or PENDING status withurl3dSecure
will be sent to thecallbackUrl
. - If the order status is PENDING and
pending3dSecure
is TRUE, merchant server must redirect the end-user to the providedurl3dSecure
- End-user fills extra info (e.g 3D Secure), and is being redirected back to merchant
redirectUrl
. - Finally, Zota server sends merchant server
callbackUrl
a callback notification of the payment result (*).
Please refer to the sections below for detailed information regarding of the steps above.
Authentication
In order for Zota server to trust a merchant server, every request should be authenticated.
The authentication process is done using a SHA-256
checksum on critical request parameters, aka signature.
Under each of the sections below you'll find the appropriate Signature section that explains how to generate (or verify) a signature for a specific case.
Example of a SHA-256
signature: b549bdaa4b7ae8d779bdd796190e93f487b2690e7609c5869c4793900cf24a3c
Merchants may find this tool very helpful for signature
testing and debugging, with a dedicated tool for each of the signing use-cases.
Merchant Endpoints
The EndpointID
represents the API destination that each deposit request must be submitted to for any particular payment solution. By design, each endpoint supports only one specific currency.
Once your merchant application is approved by Zota, you will be provided with a full list of endpoints for your account, specific to the solution(s) requested.
Merchant Endpoint Groups
The EndpointGroupID
is a means to consolidate all or part of a merchant’s endpoints into a single, multi-currency API destination. Requests made to an endpoint group will accept the orderCurrency
value for any EndpointID
that is included in said group and route the request accordingly.
Endpoint groups are not issued to merchants by default but are available upon request.
Deposit Request
Context
The whole deposit flow starts when the end-user attempts to make a payment and sends a request to merchants' server.
Merchant server then should send a signed Deposit Request
to Zota server, while holding the user with some kind of a 'loading' sign.
Issue a Deposit Request
Initiates the payment process.
HTTP Request
POST /api/v1/deposit/request/EndpointID/
or
POST /api/v1/deposit/request/group/EndpointGroupID/
Content-Type: application/json
Merchant
-> Zota
Request Parameters
Deposit Request example
curl -X POST \
"https://api.zotapay.com/api/v1/deposit/request/1050/" \
-H "Content-Type: application/json" \
-d '{
"merchantOrderID": "QvE8dZshpKhaOmHY",
"merchantOrderDesc": "Test order",
"orderAmount": "500.00",
"orderCurrency": "THB",
"customerEmail": "[email protected]",
"customerFirstName": "John",
"customerLastName": "Doe",
"customerAddress": "5/5 Moo 5 Thong Nai Pan Noi Beach, Baan Tai, Koh Phangan",
"customerCountryCode": "TH",
"customerCity": "Surat Thani",
"customerZipCode": "84280",
"customerPhone": "+66-77999110",
"customerIP": "103.106.8.104",
"redirectUrl": "https://www.example-merchant.com/payment-return/",
"callbackUrl": "https://www.example-merchant.com/payment-callback/",
"customParam": "{\"UserId\": \"e139b447\"}",
"checkoutUrl": "https://www.example-merchant.com/account/deposit/?uid=e139b447",
"signature": "47d7ed292cf10e689b311ef5573eddbcc8505fe51e20d3f74e6b33756d96800b"
}'
import requests # pip install requests
url = 'https://api.zotapay.com/api/v1/deposit/request/1050/'
payload = {
"merchantOrderID": "QvE8dZshpKhaOmHY",
"merchantOrderDesc": "Test order",
"orderAmount": "500.00",
"orderCurrency": "THB",
"customerEmail": "[email protected]",
"customerFirstName": "John",
"customerLastName": "Doe",
"customerAddress": "5/5 Moo 5 Thong Nai Pan Noi Beach, Baan Tai, Koh Phangan",
"customerCountryCode": "TH",
"customerCity": "Surat Thani",
"customerZipCode": "84280",
"customerPhone": "+66-77999110",
"customerIP": "103.106.8.104",
"redirectUrl": "https://www.example-merchant.com/payment-return/",
"callbackUrl": "https://www.example-merchant.com/payment-callback/",
"customParam": "{\"UserId\": \"e139b447\"}",
"checkoutUrl": "https://www.example-merchant.com/account/deposit/?uid=e139b447",
"signature": "47d7ed292cf10e689b311ef5573eddbcc8505fe51e20d3f74e6b33756d96800b"
}
headers = {'content-type': 'application/json'}
try:
response = requests.post(url, json=payload, headers=headers)
except Exception as e:
raise Exception(f"deposit request failed, error: {e}")
print( response.json() ) # {'code': '200', 'data': { ... }}
const request = require('request'); // npm install request
const opts = {
uri: 'https://api.zotapay.com/api/v1/deposit/request/1050/',
method: 'POST',
json: {
"merchantOrderID": "QvE8dZshpKhaOmHY",
"merchantOrderDesc": "Test order",
"orderAmount": "500.00",
"orderCurrency": "THB",
"customerEmail": "[email protected]",
"customerFirstName": "John",
"customerLastName": "Doe",
"customerAddress": "5/5 Moo 5 Thong Nai Pan Noi Beach, Baan Tai, Koh Phangan",
"customerCountryCode": "TH",
"customerCity": "Surat Thani",
"customerZipCode": "84280",
"customerPhone": "+66-77999110",
"customerIP": "103.106.8.104",
"redirectUrl": "https://www.example-merchant.com/payment-return/",
"callbackUrl": "https://www.example-merchant.com/payment-callback/",
"customParam": "{\"UserId\": \"e139b447\"}",
"checkoutUrl": "https://www.example-merchant.com/account/deposit/?uid=e139b447",
"signature": "47d7ed292cf10e689b311ef5573eddbcc8505fe51e20d3f74e6b33756d96800b"
}
};
request(opts, function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('request got an error:');
console.log(body);
return
}
console.log('request got OK');
console.log(body);
// {code: '200', data: { ... }}
});
Name | Max. Length | Description | Required |
---|---|---|---|
merchantOrderID |
128 | Merchant-defined unique order identifier | Yes |
merchantOrderDesc |
128 | Brief order description | Yes |
orderAmount |
24 | Amount to be charged, must be specified with delimiter, e.g. 1.50 for USD is 1 dollar and 50 cents | Yes |
orderCurrency |
3 | Currency to be charged in, three-letter ISO 4217 currency code. See Currency Codes for a full list of currency codes. | Yes |
customerEmail |
50 | End user email address | Yes |
customerFirstName |
128 | End user first name | Yes |
customerLastName |
128 | End user last name | Yes |
customerAddress |
128 | End user address | Yes |
customerCountryCode |
2 | End user country, two-letter ISO 3166-1 Alpha-2 country code. see Country Codes for a full list of country codes. | Yes |
customerCity |
128 | End user city | Yes |
customerState |
3 | Required for US , CA and AU countries. End user state/province, two-letter state code. See State Codes for a full list of state codes. |
Conditional |
customerZipCode |
15 | End user postal code | Yes |
customerPhone |
15 | End user full international telephone number, including country code | Yes |
customerIP |
64 | End user IPv4/IPv6 address | Yes |
customerPersonalID |
20 | End user personal ID number | No |
customerBankCode |
16 | End user bank code | No |
customerBankAccountNumber |
64 | End user bank account number | No |
redirectUrl |
255 | URL for end user redirection upon transaction completion, regardless of order status, see Final Redirection section below for more details | Yes |
callbackUrl |
255 | URL the order status will be sent to, see Callback section below for more details | No |
checkoutUrl |
256 | The original URL from where the end-user started the deposit request (a URL in Merchants' website) | Yes |
customParam |
128 | Merchant-defined optional custom parameter | No |
language |
2 | Preferred payment form language | No |
signature |
64 | Request checksum encrypted with SHA-256 , see Signature section below for more details |
Yes |
A successful response from Zota server to the example request above:
{
"code": "200",
"data": {
"depositUrl": "https://api.zotapay.com/api/v1/deposit/init/8b3a6b89697e8ac8f45d964bcc90c7ba41764acd/",
"merchantOrderID": "QvE8dZshpKhaOmHY",
"orderID": "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd"
}
}
A non-successful response from Zota server to the example request above:
{
"code": "401",
"message": "unauthorized"
}
Or alternatively
{
"code": "400",
"message": "endpoint currency mismatch"
}
See Error Codes for a full list of codes.
HTTP Response
Content-Type: application/json
Zota
-> Merchant
Name | Description |
---|---|
code |
A status code representing the acceptance of the request by Zota server. |
message |
When code is other than 200 , this parameter may hold information about the reason / error. |
data |
When code is 200 , this parameter will include the following fields: depositUrl , merchantOrderID and orderID . Please see the table below for detailed information regarding these fields. |
The data
Response Parameter
Field | Description |
---|---|
depositUrl |
The URL of the payment form, merchant should redirect the (waiting) end-user to this URL (HTTP 302 ). |
merchantOrderID |
Merchants' order unique identifier, provided in the original Deposit Request . |
orderID |
Order unique identifier generated by Zota, should be then used for Order Status Requests . |
Signature
Every request must be signed by the merchant in order to be successfully authenticated by Zota servers.
EndpointID
+ merchantOrderID
+ orderAmount
+ customerEmail
+ MerchantSecretKey
Signing a Deposit Request
# prepare vars
endpointID=1050
merchantOrderID=QvE8dZshpKhaOmHY
orderAmount=500.00
customerEmail=[email protected]
secretKey=EXAMPLE-SECRET-KEY
# concatenate values into a single string
str="${endpointID}${merchantOrderID}${orderAmount}${customerEmail}${secretKey}"
echo $str
# 1050QvE8dZshpKhaOmHY500.00customer@email-address.comEXAMPLE-SECRET-KEY
# create a sha256 hash of the string
sig=$(echo -n "${str}" | openssl dgst -sha256)
echo $sig
# 47d7ed292cf10e689b311ef5573eddbcc8505fe51e20d3f74e6b33756d96800b
from hashlib import sha256
# prepare vars
endpoint = "1050"
moid = "QvE8dZshpKhaOmHY"
amt = "500.00"
email = "[email protected]"
secret = "EXAMPLE-SECRET-KEY"
# concatenate values into a single string
s = f"{endpoint}{moid}{amt}{email}{secret}"
# create a sha256 hash of the string
sig = sha256(s.encode('utf-8')).hexdigest()
print( sig ) # 47d7ed292cf10e689b311ef5573eddbcc8505fe51e20d3f74e6b33756d96800b
const crypto = require('crypto'); // npm install crypto
const endpoint = "1050";
const moid = "QvE8dZshpKhaOmHY";
const amt = "500.00";
const email = "[email protected]";
const secret = "EXAMPLE-SECRET-KEY";
const s = endpoint + moid + amt + email + secret;
const sig = crypto.createHash('sha256').update(s).digest('hex');
console.log(sig); // 47d7ed292cf10e689b311ef5573eddbcc8505fe51e20d3f74e6b33756d96800b
Card Payment Integration
In case of merchant collects card information on their side and wish to include it in the request, the deposit request (following the documentation) should be posted including the direct path parameter:
POST /api/v1/deposit/request/direct/EndpointID/
or
POST /api/v1/deposit/request/direct/group/EndpointGroupID/
HTTP Response (Card Integration)
A successful response from Zota server to the deposit request on direct endpoints:
{
"code": "200",
"data": {
"merchantOrderID": "1721809513",
"orderID": "32530298",
"depositUrl": "https://cde-secure.zota.com/api/v1/deposit/process/direct/1/32530298"
}
}
Content-Type: application/json
Zota
-> Merchant
Name | Description |
---|---|
code |
A status code representing the acceptance of the request by Zota server. |
message |
When code is other than 200 , this parameter may hold information about the reason / error. |
data |
When code is 200 , this parameter will include the following fields: depositUrl , merchantOrderID and orderID . Please see the table below for detailed information regarding these fields. |
The data
Response Parameter
Field | Description |
---|---|
depositUrl |
The URL to which the request containing the card data should be posted. |
merchantOrderID |
Merchants' order unique identifier, provided in the original Deposit Request . |
orderID |
Order unique identifier generated by Zota, should be then used for Order Status Requests . |
HTTP Request (Card Data)
Content-Type: application/json
Merchant
-> Zotapay
Name | Max. Length | Description | Required |
---|---|---|---|
data |
n/a | Contains the card data object, see details below | Yes |
signature |
64 | Request checksum encrypted with SHA-256 , see Signature section below for more details |
Yes |
Signing a Card Data Request
#!/bin/bash
echo "$@"
# prepare vars
cardNum=4111111111111111
holderName="John Doe"
expYear=2025
expMonth=12
cvv=123
secretKey=EXAMPLE-SECRET-KEY
# concatenate values into a single string
str="${cardNum}${holderName}${expYear}${expMonth}${cvv}${secretKey}"
echo $str
# 4111111111111111John Doe202512123EXAMPLE-SECRET-KEY
# create a sha256 hash of the string
sig=$(echo -n ${str} | openssl dgst -sha256 -hex)
echo $sig
# c118fd59adf8a8c335df4122a2a15cadd60023c290faf1b078252feeaf9b479a
from hashlib import sha256
# prepare vars
card_num = "4111111111111111"
holder_name = "John Doe"
exp_year = "2025"
exp_month = "12"
cvv = "123"
secret_key = "EXAMPLE-SECRET-KEY"
# concatenate values into a single string
s = f"{card_num}{holder_name}{exp_year}{exp_month}{cvv}{secret_key}"
print(s)
# create a sha256 hash of the string
sig = sha256(s.encode('utf-8')).hexdigest()
print(sig) # c118fd59adf8a8c335df4122a2a15cadd60023c290faf1b078252feeaf9b479a
const crypto = require('crypto'); // npm install crypto
const cardNum = "4111111111111111";
const holderName = "John Doe";
const expYear = "2025";
const expMonth = "12";
const cvv = "123";
const secretKey = "EXAMPLE-SECRET-KEY";
const s = cardNum + holderName + expYear + expMonth + cvv + secretKey;
const sig = crypto.createHash('sha256').update(s).digest('hex');
console.log(sig); // c118fd59adf8a8c335df4122a2a15cadd60023c290faf1b078252feeaf9b479a
The signature
is formed from all card parameters along with the API KEY and SHA256 hashed:cardNum
+ holderName
+ expYear
+ expMonth
+ cvv
+ MerchantSecretKey
The data
object structure:
Deposit Card Data Request example
curl -X POST \
"https://cde-secure.zota.com/api/v1/deposit/process/direct/1/32530298" \
-H "Content-Type: application/json" \
-d '{
"data": {
"cardNum": "4917610000000000",
"cvv": "0001",
"expMonth": 10,
"expYear": 2031,
"holderName": "John Doe"
},
"signature": "3559f675ac345102f7fbf80dcd229637467899203665bf6cf7dfed9a4b34a504"
}'
import requests # pip install requests
url = 'https://cde-secure.zota.com/api/v1/deposit/process/direct/1/32530298'
payload = {
"data": {
"cardNum": "4917610000000000",
"cvv": "0001",
"expMonth": 10,
"expYear": 2031,
"holderName": "John Doe"
},
"signature": "3559f675ac345102f7fbf80dcd229637467899203665bf6cf7dfed9a4b34a504"
}
headers = {'content-type': 'application/json'}
try:
response = requests.post(url, json=payload, headers=headers)
except Exception as e:
raise Exception(f"deposit request failed, error: {e}")
print( response.json() ) # {'code': '200', 'data': { ... }}
const request = require('request'); // npm install request
const opts = {
uri: 'https://cde-secure.zota.com/api/v1/deposit/process/direct/1/32530298',
method: 'POST',
json: {
"data": {
"cardNum": "4917610000000000",
"cvv": "0001",
"expMonth": 10,
"expYear": 2031,
"holderName": "John Doe"
},
"signature": "3559f675ac345102f7fbf80dcd229637467899203665bf6cf7dfed9a4b34a504"
}
};
request(opts, function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('request got an error:');
console.log(body);
return
}
console.log('request got OK');
console.log(body);
// {code: '200', data: { ... }}
});
Name | Max. Length | Description | Required |
---|---|---|---|
cardNum |
16 | Card number | Yes |
holderName |
64 | Card holder name as appears on card | Yes |
expMonth |
2 | Expiration month (e.g "02") | Yes |
expYear |
4 | Expiration Year (e.g "2020" or just "20") | Yes |
cvv |
4 | CVV / Security code | Yes |
HTTP Response
Content-Type: application/json
Zotapay
-> Merchant
Name | Description |
---|---|
code |
A status code representing the acceptance of the request by Zota server. |
data |
If code is 200 the data will include status and orderID . |
The data
object structure:
Name | Description |
---|---|
status |
Transaction status, see Order Statuses for a full list of statuses. |
orderID |
Order unique identifier generated by Zota, should be then used for Order Status Requests . |
A successful response from Zota server to the deposit direct request:
{
"code": "200",
"data": {
"status": "PENDING",
"orderID": "32530310"
}
}
Order Status Request
Context
Merchant should issue an Order Status Request
to get the most up-to-date status of customer’s order transaction.
After a Deposit Request
is sent to Zota server and orderID
is returned, merchant server should start polling for transaction status (10-15 seconds interval).
This polling interval should continue until one of the following occurs:
Zota server responds to an
Order Status Request
with a final status (e.g.APPROVED
,DECLINED
)Merchant server receives a
callback
notification from Zota with a final status (e.g.APPROVED
,DECLINED
)In case of Card Payment Integration, when the status is
PENDING
(see more in Card 3D Secure).
Issue an Order Status Request
HTTP Request
GET /api/v1/query/order-status/
Merchant
-> Zota
Query Parameters
Order Status Request example
# prepare vars
mid=EXAMPLE-MERCHANT-ID
moid=QvE8dZshpKhaOmHY
oid=8b3a6b89697e8ac8f45d964bcc90c7ba41764acd
ts=1564617600
sig=4a01f6cc95e6a7a771afe0e49f59fb572dbadec449f175d978e722b97ee9785d
# prepare query string
qs="merchantID=${mid}&merchantOrderID=${moid}&orderID=${oid}×tamp=${ts}&signature=${sig}"
# send request
curl -X GET "https://api.zotapay.com/api/v1/query/order-status/?${qs}"
import requests # pip install requests
# prepare vars
mid = "EXAMPLE-MERCHANT-ID"
moid = "QvE8dZshpKhaOmHY"
oid = "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd"
ts = "1564617600" # str(int(time.time()))
sig = "4a01f6cc95e6a7a771afe0e49f59fb572dbadec449f175d978e722b97ee9785d"
# prepare query string
qs=f"merchantID={mid}&merchantOrderID={moid}&orderID={oid}×tamp={ts}&signature={sig}"
url = f"https://api.zotapay.com/api/v1/query/order-status/?{qs}"
# send request
try:
response = requests.get(url)
except Exception as e:
raise Exception(f"order status request failed, error: {e}")
print( response.json() ) # {'code': '200', 'data': { ... }}
const request = require('request'); // npm install request
const mid = "EXAMPLE-MERCHANT-ID";
const moid = "QvE8dZshpKhaOmHY";
const oid = "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd";
const ts = "1564617600"; // parseInt((new Date().getTime()) / 1000).toString()
const sig = "4a01f6cc95e6a7a771afe0e49f59fb572dbadec449f175d978e722b97ee9785d";
const qs = `merchantID=${mid}&merchantOrderID=${moid}&orderID=${oid}×tamp=${ts}&signature=${sig}`;
const url = `https://api.zotapay.com/api/v1/query/order-status/?${qs}`;
request(url, function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('request got an error:');
console.log(body);
return
}
console.log('request got OK');
console.log(body);
// {code: '200', data: { ... }}
});
Name | Max. Length | Description | Required |
---|---|---|---|
merchantID |
32 | Unique merchant identifier provided by Zota (see Before You Begin section) | Yes |
orderID |
128 | Order unique identifier generated by Zota | Yes |
merchantOrderID |
128 | Merchant-defined unique order identifier | Yes |
timestamp |
15 | Unix timestamp of the request time | Yes |
signature |
64 | Request checksum encrypted with SHA-256 , see Signature section below for more details |
Yes |
HTTP Response
Content-Type: application/json
Zota
-> Merchant
Name | Description |
---|---|
code |
A status code representing the acceptance of the request by Zota server. |
message |
When code is other than 200 , this parameter may hold information about the reason / error. |
data |
When code is 200 , this parameter will include the following fields: depositUrl , merchantOrderID and orderID . Please see the table below for detailed information regarding these fields. |
The data
Response Parameter
A successful response from Zota server to the example request above:
{
"code": "200",
"data": {
"type": "SALE",
"status": "PROCESSING",
"errorMessage": "",
"endpointID": "1050",
"processorTransactionID": "",
"orderID": "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd",
"merchantOrderID": "QvE8dZshpKhaOmHY",
"amount": "500.00",
"currency": "THB",
"customerEmail": "[email protected]",
"customParam": "{\"UserId\": \"e139b447\"}",
"extraData": {
"amountChanged": true,
"amountRounded": true,
"amountManipulated": false,
"dcc": false,
"originalAmount": "499.98",
"paymentMethod": "INSTANT-BANK-WIRE",
"selectedBankCode": "SCB",
"selectedBankName": ""
},
"request": {
"merchantID": "EXAMPLE-MERCHANT-ID",
"orderID": "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd",
"merchantOrderID": "QvE8dZshpKhaOmHY",
"timestamp": "1564617600"
}
}
}
A non-successful response from Zota server to the example request above:
{
"code": "401",
"message": "unauthorized"
}
Or alternatively
{
"code": "400",
"message": "timestamp too old"
}
See Error Codes for a full list of codes.
Name | Description |
---|---|
type |
Transaction type: SALE . |
status |
Transaction status, see Order Statuses for a full list of statuses. |
errorMessage |
Contains the error description if the transaction is declined or yields an error. |
processorTransactionID |
The transaction identifier that was generated by the payment processor. |
orderID |
Order unique identifier generated by Zota. |
merchantOrderID |
Merchant-defined unique order identifier. |
amount |
The amount of the transaction. |
currency |
The currency of the transaction. |
customerEmail |
End-user email address. |
customParam |
Merchant-defined optional custom parameter. |
extraData |
A Json object with additional information that was collected during the payment process on Zota environment, such as amountChanged (boolean), originalAmount (int), paymentMethod (string), etc. |
request |
A Json object with a copy of the original Order Status Request sent by merchant server to Zota. |
Signature
Every request must be signed by the merchant in order to be successfully authenticated by Zota servers. The signature
parameter of an Order Status Request
must be generated by hashing a string of concatenated parameters using SHA-256
in the exact following order:
MerchantID
+ merchantOrderID
+ orderID
+ timestamp
+ MerchantSecretKey
Signing an Order Status Request
# prepare vars
merchantID=EXAMPLE-MERCHANT-ID
merchantOrderID=QvE8dZshpKhaOmHY
orderID=8b3a6b89697e8ac8f45d964bcc90c7ba41764acd
timestamp=1564617600
secretKey=EXAMPLE-SECRET-KEY
# concatenate values into a single string
str="${merchantID}${merchantOrderID}${orderID}${timestamp}${secretKey}"
echo $str
# EXAMPLE-MERCHANT-IDQvE8dZshpKhaOmHY8b3a6b89697e8ac8f45d964bcc90c7ba41764acd1564617600EXAMPLE-SECRET-KEY
# create a sha256 hash of the string
sig=$(echo -n "${str}" | openssl dgst -sha256)
echo $sig
# 4a01f6cc95e6a7a771afe0e49f59fb572dbadec449f175d978e722b97ee9785d
from hashlib import sha256
# prepare vars
mid = "EXAMPLE-MERCHANT-ID"
moid = "QvE8dZshpKhaOmHY"
oid = "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd"
ts = "1564617600" # str(int(time.time()))
secret = "EXAMPLE-SECRET-KEY"
# concatenate values into a single string
s = f"{mid}{moid}{oid}{ts}{secret}"
# create a sha256 hash of the string
sig = sha256(s.encode('utf-8')).hexdigest()
print( sig ) # 4a01f6cc95e6a7a771afe0e49f59fb572dbadec449f175d978e722b97ee9785d
const crypto = require('crypto'); // npm install crypto
const mid = "EXAMPLE-MERCHANT-ID";
const moid = "QvE8dZshpKhaOmHY";
const oid = "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd";
const ts = "1564617600"; // parseInt((new Date().getTime()) / 1000).toString()
const secret = "EXAMPLE-SECRET-KEY";
const s = mid + moid + oid + ts + secret;
const sig = crypto.createHash('sha256').update(s).digest('hex');
console.log(sig); // 4a01f6cc95e6a7a771afe0e49f59fb572dbadec449f175d978e722b97ee9785d
Card 3D Secure
Context
When integrating using the Card Payment Integration, after the Deposit Request
was sent, order might enter a PENDING
status, with 3D Secure input requirement from the end-user. Merchant will be notified about such event by callback (when callbackUrl
is provided) and/or by performing an Order Status Request
.
Example of a
PENDING
callback with 3D Secure requirements
{
"type": "SALE",
"status": "PENDING",
"endpointID": "70210",
"processorTransactionID": "04a1cade-1187-44e8-b555-843dc5a7211e",
"orderID": "1502000",
"merchantOrderID": "574c19d02b7f",
"amount": "100.00",
"currency": "USD",
"customerEmail": "[email protected]",
"customParam": "",
"extraData": {
"card": {
"cvv": "***",
"expiration": "03/2022",
"holder": "card tester",
"number": "111100***0000"
},
"cardData": {
"bank": {},
"brand": "",
"country": {}
},
"pending3dSecure": true, // <--------------
"url3dSecure": "< URL >", // <--------------
},
"originalRequest": {
...
},
"signature": "bd2097de35188dee5714b497f7b9aef78712c3207d33fbb94f863f2d9b404fa5"
}
## Handling 3D Secure Events
Merchant should react to such PENDING
event by prompting the (already waiting) end-user with a 3D Secure form, which is provided by Zota as an HTML snippet in the callback / status-check response.
Please review the attached example of a PENDING
callback with 3D Secure requirements. Especially note these 2 parameters inside of extraData
:
pending3dSecure
(Boolean)url3dSecure
(String)
When callback (or status-check response) status is PENDING
, and extraData.pending3dSecure
is true
, merchant should redirect the end-user the url that was received in extraData.url3dSecure
(popup / iframe is also a valid option).
After the user submits the form, the order processing will continue automatically, and merchant should still expect a final status for the order, either by callback, or by continuing the Order Status Request
loop.
Order Completion
Context
After end-user submits the payment form, Zota:
- Redirects the end-user back to
redirectUrl
provided by the merchant with theDeposit Request
. - Sends an http
callback
notification tocallbackUrl
, if provided by merchant with theDeposit Request
.
End User Final Redirect
HTTP GET
Zota
-> DepositRequest.redirectUrl
Query Parameters
Example Redirect URL
https://www.example-merchant.com/payment-return/?
merchantOrderID=QvE8dZshpKhaOmHY
&orderID=8b3a6b89697e8ac8f45d964bcc90c7ba41764acd
&signature=da2d2d269792c78145edc12096732d2aba411c9eefd3c717d87993c7e4a4c523
&status=APPROVED
Field | Description |
---|---|
status |
Transaction status, see Order Statuses for a full list of statuses. |
errorMessage |
Contains the error description if the transaction is declined or yields an error. |
orderID |
Order unique identifier generated by Zota. |
merchantOrderID |
Merchant-defined unique order identifier. |
signature |
Request checksum encrypted with SHA-256 , see Signature section below for more details |
Signature
When Zota redirects the end-user back to merchant's redirectUrl
, this redirect request includes a signature
query parameter. The signature
parameter must be verified by the merchant. A verification is done by hashing a string of concatenated parameters using SHA-256
in the exact following order:
status
+ orderID
+ merchantOrderID
+ MerchantSecretKey
And then by ensuring that the result is equal to the signature
parameter of the request.
If both signatures are equal, you should consider the request as authentic.
Verifying Redirect Signature
# prepare vars
status=APPROVED
orderID=8b3a6b89697e8ac8f45d964bcc90c7ba41764acd
merchantOrderID=QvE8dZshpKhaOmHY
secretKey=EXAMPLE-SECRET-KEY
# concatenate values into a single string
str="${status}${orderID}${merchantOrderID}${secretKey}"
echo $str
# APPROVED8b3a6b89697e8ac8f45d964bcc90c7ba41764acdQvE8dZshpKhaOmHYEXAMPLE-SECRET-KEY
# create a sha256 hash of the string
sig=$(echo -n "${str}" | openssl dgst -sha256)
echo $sig
# da2d2d269792c78145edc12096732d2aba411c9eefd3c717d87993c7e4a4c523
# if provided 'signature' equals to 'sig', consider the request as authentic.
from hashlib import sha256
# prepare vars
status = "APPROVED"
oid = "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd"
moid = "QvE8dZshpKhaOmHY"
secret = "EXAMPLE-SECRET-KEY"
# concatenate values into a single string
s = f"{status}{oid}{moid}{secret}"
# create a sha256 hash of the string
sig = sha256(s.encode('utf-8')).hexdigest()
print( sig ) # da2d2d269792c78145edc12096732d2aba411c9eefd3c717d87993c7e4a4c523
const crypto = require('crypto'); // npm install crypto
const status = "APPROVED";
const oid = "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd";
const moid = "QvE8dZshpKhaOmHY";
const secret = "EXAMPLE-SECRET-KEY";
const s = status + oid + moid + secret;
const sig = crypto.createHash('sha256').update(s).digest('hex');
console.log(sig); // da2d2d269792c78145edc12096732d2aba411c9eefd3c717d87993c7e4a4c523
Callback Notification
Callbacks are the most accurate way to record the final status for any given order.
Once a transaction is processed and its status is final (see Order Statuses), Zota server will initiate an HTTP POST
request to merchant callbackUrl
provided in the original Deposit Request
.
The callback
notification request will contain all the relevant parameters identifying a specific order, along with the status
parameter representing the final status of the transaction.
This request also includes more useful information about the order and can be seen as kind of a detailed order summary.
HTTP Request
HTTP POST
Content-Type: application/json
Zota
-> DepositRequest.callbackUrl
Request Parameters
Example Callback Notification
{
"type": "SALE",
"amount": "500.00",
"status": "APPROVED",
"orderID": "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd",
"currency": "THB",
"extraData": {
"amountChanged": true,
"amountRounded": true,
"amountManipulated": false,
"dcc": false,
"originalAmount": "499.98",
"paymentMethod": "INSTANT-BANK-WIRE",
"selectedBankCode": "SCB",
"selectedBankName": ""
},
"signature": "062c0480aafd1faf735b987f5a2f878634d7931ffb3df256cdbfa77c31a2a4cc",
"endpointID": "1050",
"customParam": "{\"UserId\": \"e139b447\"}",
"errorMessage": "",
"customerEmail": "[email protected]",
"merchantOrderID": "QvE8dZshpKhaOmHY",
"originalRequest": {
"merchantOrderID": "QvE8dZshpKhaOmHY",
"merchantOrderDesc": "Test order",
"orderAmount": "500.00",
"orderCurrency": "THB",
"customerEmail": "[email protected]",
"customerFirstName": "John",
"customerLastName": "Doe",
"customerAddress": "5/5 Moo 5 Thong Nai Pan Noi Beach, Baan Tai, Koh Phangan",
"customerCountryCode": "TH",
"customerCity": "Surat Thani",
"customerZipCode": "84280",
"customerPhone": "+66-77999110",
"customerIP": "103.106.8.104",
"redirectUrl": "https://www.example-merchant.com/payment-return/",
"callbackUrl": "https://www.example-merchant.com/payment-callback/",
"customParam": "{\"UserId\": \"e139b447\"}",
"checkoutUrl": "https://www.example-merchant.com/account/deposit/?uid=e139b447",
"signature": "47d7ed292cf10e689b311ef5573eddbcc8505fe51e20d3f74e6b33756d96800b",
"customerState": "",
},
"processorTransactionID": "000139821"
}
Name | Description |
---|---|
type |
Transaction type, either SALE or PAYOUT . |
status |
Transaction status, see Order Statuses for a full list of statuses. |
errorMessage |
Contains the error description if the transaction is declined or yields an error. |
endpointID |
The merchant EndpointID that the order was sent through. |
processorTransactionID |
The transaction identifier that was generated by the payment processor. |
orderID |
Order unique identifier generated by Zota. |
merchantOrderID |
Merchant-defined unique order identifier. |
amount |
The amount of the transaction. |
currency |
The currency of the transaction. |
customerEmail |
End-user email address. |
customParam |
Merchant-defined optional custom parameter. |
extraData |
A Json object with additional information that was collected during the payment process on Zota's environemnt, such as amountChanged (boolean), originalAmount (int), paymentMethod (string), etc. |
originalRequest |
A Json object with a copy of the original Deposit Request sent by merchant server to Zota. |
signature |
Request checksum encrypted with SHA-256 , see Request Signature section below for more details |
Signature
When Zota sends an http callback
notification to merchant's callbackUrl
, this request request includes a signature
parameter. The signature
parameter must be verified by the merchant. A verification is done by hashing a string of concatenated parameters using SHA-256
in the exact following order:
EndpointID
+ orderID
+ merchantOrderID
+ status
+ amount
+ customerEmail
+ MerchantSecretKey
And then by ensuring that the result is equal to the signature
parameter of the request.
If both signatures are equal, you should consider the request as authentic.
Verifying Callback Signature
# prepare vars
endpointID=1050
orderID=8b3a6b89697e8ac8f45d964bcc90c7ba41764acd
merchantOrderID=QvE8dZshpKhaOmHY
status=APPROVED
amount=500.00
email=[email protected]
secretKey=EXAMPLE-SECRET-KEY
# concatenate values into a single string
str="${endpointID}${orderID}${merchantOrderID}${status}${amount}${email}${secretKey}"
echo $str
# 10508b3a6b89697e8ac8f45d964bcc90c7ba41764acdQvE8dZshpKhaOmHYAPPROVED500.00customer@email-address.comEXAMPLE-SECRET-KEY
# create a sha256 hash of the string
sig=$(echo -n "${str}" | openssl dgst -sha256)
echo $sig
# 062c0480aafd1faf735b987f5a2f878634d7931ffb3df256cdbfa77c31a2a4cc
# if provided 'signature' equals to 'sig', consider the request as authentic.
from hashlib import sha256
# prepare vars
endpoint = "1050"
oid = "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd"
moid = "QvE8dZshpKhaOmHY"
status = "APPROVED"
amt = "500.00"
email = "[email protected]"
secret = "EXAMPLE-SECRET-KEY"
# concatenate values into a single string
s = f"{endpoint}{oid}{moid}{status}{amt}{email}{secret}"
# create a sha256 hash of the string
sig = sha256(s.encode('utf-8')).hexdigest()
print( sig ) # 062c0480aafd1faf735b987f5a2f878634d7931ffb3df256cdbfa77c31a2a4cc
const crypto = require('crypto'); // npm install crypto
const endpoint = "1050";
const oid = "8b3a6b89697e8ac8f45d964bcc90c7ba41764acd";
const moid = "QvE8dZshpKhaOmHY";
const status = "APPROVED";
const amt = "500.00";
const email = "[email protected]";
const secret = "EXAMPLE-SECRET-KEY";
const s = endpoint + oid + moid + status + amt + email + secret;
const sig = crypto.createHash('sha256').update(s).digest('hex');
console.log(sig); // 062c0480aafd1faf735b987f5a2f878634d7931ffb3df256cdbfa77c31a2a4cc
Orders Report
Context
At any single point in time merchants are able to issue an Orders Report Request
in order to receive a detailed transaction log of their account by a given date range.
Issue an Orders Report Request
HTTP Request
GET /api/v1/query/orders-report/csv/
Merchant
-> Zota
Query Parameters
Orders Report Request example
# prepare vars
mid='EXAMPLE-MERCHANT-ID'
date_type='created'
endpoint_ids='1001,1002'
from_date='2019-11-01'
request_id='d6da50a9-aca4-4d6f-a022-f487a127b54d'
statuses='APPROVED,DECLINED'
ts='1573533000'
to_date='2019-11-01'
types='SALE,PAYOUT'
sig='677ff8f149c7cbe54937312ac5d6f5fc838417ba9a4a04779be2c75edde1d714'
# prepare query string
qs="merchantID=${mid}&dateType=${date_type}&endpointIds=${endpoint_ids}&fromDate=${from_date}&requestID=${request_id}&statuses=${statuses}×tamp=${ts}&toDate=${to_date}&types=${types}&signature=${sig}"
# send request
curl -X GET "https://api.zotapay.com/api/v1/query/orders-report/csv/?${qs}"
import requests # pip install requests
# prepare vars
mid = 'EXAMPLE-MERCHANT-ID'
date_type = 'created'
endpoint_ids = '1001,1002'
from_date = '2019-11-01'
request_id = 'd6da50a9-aca4-4d6f-a022-f487a127b54d'
statuses = 'APPROVED,DECLINED'
ts = '1573533000' # str(int(time.time()))
to_date = '2019-11-01'
types = 'SALE,PAYOUT'
sig='677ff8f149c7cbe54937312ac5d6f5fc838417ba9a4a04779be2c75edde1d714'
# prepare query string
qs=f"merchantID={mid}&dateType={date_type}&endpointIds={endpoint_ids}&fromDate={from_date}&requestID={request_id}&statuses={statuses}×tamp={ts}&toDate={to_date}&types={types}&signature={sig}"
url = f"https://api.zotapay.com/api/v1/query/orders-report/csv/?{qs}"
# send request
try:
response = requests.get(url)
except Exception as e:
raise Exception(f"order status request failed, error: {e}")
print( response.json() ) # {'code': '200', 'data': { ... }}
const request = require('request'); // npm install request
const mid = 'EXAMPLE-MERCHANT-ID'
const date_type = 'created'
const endpoint_ids = '1001,1002'
const from_date = '2019-11-01'
const request_id = 'd6da50a9-aca4-4d6f-a022-f487a127b54d'
const statuses = 'APPROVED,DECLINED'
const ts = '1573533000' // parseInt((new Date().getTime()) / 1000).toString()
const to_date = '2019-11-01'
const types = 'SALE,PAYOUT'
const sig='677ff8f149c7cbe54937312ac5d6f5fc838417ba9a4a04779be2c75edde1d714'
const qs = `merchantID=${mid}&dateType=${date_type}&endpointIds=${endpoint_ids}&fromDate=${from_date}&requestID=${request_id}&statuses=${statuses}×tamp=${ts}&toDate=${to_date}&types=${types}&signature=${sig}`;
const url = `https://api.zotapay.com/api/v1/query/orders-report/csv/?${qs}`;
request(url, function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('request got an error:');
console.log(body);
return
}
console.log('request got OK');
console.log(body);
// {code: '200', data: { ... }}
});
Name | Max. Length | Description | Required |
---|---|---|---|
merchantID |
32 | Unique merchant identifier provided by Zota (see Before You Begin section) | Yes |
dateType |
8 | Set date filter type either as created or ended , related to fromDate and toDate |
Yes |
endpointIds |
128 | Commas separated list of Endpoint Ids to filter from, e.g 1001,1002,1004 |
No |
fromDate |
12 | Report starting date, format YYYY-MM-DD , e.g 2019-11-01 |
Yes |
requestID |
36 | A unique identifier for the request, must be of type uuid4 |
Yes |
statuses |
128 | Commas separated list of Order Statuses to filter by, e.g APPROVED,DECLINED . see Order Statuses for a full list of statuses. |
No |
timestamp |
15 | Unix timestamp of the request time | Yes |
toDate |
12 | Report ending date, format YYYY-MM-DD , e.g 2019-11-01 |
Yes |
types |
128 | Commas separated list of Order Types to filter by, supported types are SALE and PAYOUT |
No |
signature |
64 | Request checksum encrypted with SHA-256 , see Signature section below for more details. |
Yes |
HTTP Response
Content-Type: text/csv
Zota
-> Merchant
Response payload will be returned as a CSV stream of the results.
Signature
Every request must be signed by the merchant in order to be successfully authenticated by Zota servers. The signature
parameter of an Orders Report Request
must be generated by hashing a string of concatenated parameters using SHA-256
in the exact following order:
MerchantID
+ dateType
+ endpointIds
+ fromDate
+ requestID
+ statuses
+ timestamp
+ toDate
+ types
+ MerchantSecretKey
Signing an Orders Report Request
# prepare vars
mid='EXAMPLE-MERCHANT-ID'
date_type='created'
endpoint_ids='1001,1002'
from_date='2019-11-01'
request_id='d6da50a9-aca4-4d6f-a022-f487a127b54d'
statuses='APPROVED,DECLINED'
ts='1573533000'
to_date='2019-11-01'
types='SALE,PAYOUT'
secret='EXAMPLE-SECRET-KEY'
# concatenate values into a single string
str="${mid}${date_type}${endpoint_ids}${from_date}${request_id}${statuses}${ts}${to_date}${types}${secret}"
echo $str
# EXAMPLE-MERCHANT-IDcreated1001,10022019-11-01d6da50a9-aca4-4d6f-a022-f487a127b54dAPPROVED,DECLINED15735330002019-11-01SALE,PAYOUTEXAMPLE-SECRET-KEY
# create a sha256 hash of the string
sig=$(echo -n "${str}" | openssl dgst -sha256)
echo $sig
# 677ff8f149c7cbe54937312ac5d6f5fc838417ba9a4a04779be2c75edde1d714
from hashlib import sha256
# prepare vars
mid = 'EXAMPLE-MERCHANT-ID'
date_type = 'created'
endpoint_ids = '1001,1002'
from_date = '2019-11-01'
request_id = 'd6da50a9-aca4-4d6f-a022-f487a127b54d'
statuses = 'APPROVED,DECLINED'
ts = '1573533000' # str(int(time.time()))
to_date = '2019-11-01'
types = 'SALE,PAYOUT'
secret = "EXAMPLE-SECRET-KEY"
# concatenate values into a single string
s = f"{mid}{date_type}{endpoint_ids}{from_date}{request_id}{statuses}{ts}{to_date}{types}{secret}"
# create a sha256 hash of the string
sig = sha256(s.encode('utf-8')).hexdigest()
print( sig ) # 677ff8f149c7cbe54937312ac5d6f5fc838417ba9a4a04779be2c75edde1d714
const crypto = require('crypto'); // npm install crypto
const mid = 'EXAMPLE-MERCHANT-ID'
const date_type = 'created'
const endpoint_ids = '1001,1002'
const from_date = '2019-11-01'
const request_id = 'd6da50a9-aca4-4d6f-a022-f487a127b54d'
const statuses = 'APPROVED,DECLINED'
const ts = '1573533000' // parseInt((new Date().getTime()) / 1000).toString()
const to_date = '2019-11-01'
const types = 'SALE,PAYOUT'
const secret = "EXAMPLE-SECRET-KEY";
const s = mid + date_type + endpoint_ids + from_date + request_id + statuses + ts + to_date + types + secret;
const sig = crypto.createHash('sha256').update(s).digest('hex');
console.log(sig); // 677ff8f149c7cbe54937312ac5d6f5fc838417ba9a4a04779be2c75edde1d714
Exchange Rates
Context
This API endpoint provides merchants with access to the effective rates for currency conversions on a specific date and for a particular order. It is essential for merchants who want to calculate the cost of their transactions accurately.
Issue an Exchange Rates Request
HTTP Request
GET /api/v1/query/exchange-rates/
Query Parameters
Exchange Rates Request example
# prepare vars
mid='EXAMPLE-MERCHANT-ID'
request_id='d6da50a9-aca4-4d6f-a022-f487a127b54d'
ts='1695200538'
order_type='SALE'
order_id='32452684'
date=''
sig='63adcf671d9f23ab5184a05234d1ddb8a6be0b1f2570be739bb96ccedcf725e7'
# prepare query string
qs="merchantID=${mid}&requestID=${request_id}&date=${date}×tamp=${ts}&orderType=${order_type}&orderID=${order_id}&signature=${sig}"
# send request
curl -X GET "https://api.zotapay.com/api/v1/query/exchange-rates/?${qs}"
import requests # pip install requests
# prepare vars
mid='EXAMPLE-MERCHANT-ID'
request_id='d6da50a9-aca4-4d6f-a022-f487a127b54d'
ts='1695200538' # str(int(time.time()))
order_type='SALE'
order_id='32452684'
date=''
sig='63adcf671d9f23ab5184a05234d1ddb8a6be0b1f2570be739bb96ccedcf725e7'
# prepare query string
qs=f"merchantID={mid}&requestID={request_id}&date={date}×tamp={ts}&orderType={order_type}&orderID={order_id}&signature={sig}"
url = f"https://api.zotapay.com/api/v1/query/exchange-rates/?{qs}"
# send request
try:
response = requests.get(url)
except Exception as e:
raise Exception(f"order status request failed, error: {e}")
print( response.json() ) # {'code': '200', 'data': { ... }}
const request = require('request'); // npm install request
const mid='EXAMPLE-MERCHANT-ID'
const request_id='d6da50a9-aca4-4d6f-a022-f487a127b54d'
const ts='1695200538' // parseInt((new Date().getTime()) / 1000).toString()
const order_type='SALE'
const order_id='32452684'
const date=''
const sig='63adcf671d9f23ab5184a05234d1ddb8a6be0b1f2570be739bb96ccedcf725e7'
const qs = `merchantID=${mid}&requestID=${request_id}&date=${date}×tamp=${ts}&orderType=${order_type}&orderID=${order_id}&signature=${sig}`;
const url = `https://api.zotapay.com/api/v1/query/exchange-rates/?${qs}`;
request(url, function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('request got an error:');
console.log(body);
return
}
console.log('request got OK');
console.log(body);
// {code: '200', data: { ... }}
});
Name | Max. Length | Description | Required |
---|---|---|---|
merchantID |
32 | Unique merchant identifier provided by Zota (see Before You Begin section) | Yes |
orderID |
128 | Order unique identifier generated by Zota | No |
date |
10 | Get rates for specific date, format YYYY-MM-DD , e.g 2019-11-01 . If orderID is not defined, then date is required. |
No |
requestID |
36 | A unique identifier for the request, must be of type uuid4 |
Yes |
timestamp |
15 | Unix timestamp of the request time | Yes |
orderType |
128 | Order Type, either SALE , PAYOUT or REVERSAL . |
Yes |
signature |
64 | Request checksum encrypted with SHA-256 , see Signature section below for more details. |
Yes |
HTTP Response
Content-Type: application/json
Response payload will be returned as a JSON object.
- orderID is Specified
If an orderID is specified, the API will return the effective exchange rate used for the given order and other relevant accounting parameters.
A successful response with orderID specified in the request.
{
"code": "200",
"data": {
"exchangeRates": {
"balanceCurrency": "USD",
"balanceAccount": "EXAMPLE-MERCHANT-USD",
"isConverted": true,
"conversionRate": "0.01300121",
"effectiveAmount": "0.1300121",
"toMerchantBalance": "-0.22323821",
"totalFees": "0.3532503",
"rollingReserve": "0"
}
}
}
- orderID is Not Specified
The API will return the effective rate for all currency pairs, from "any supported currency" to the "merchant base currency".
A successful response with orderID NOT specified in the request.
{
"code": "200",
"data": {
"exchangeRates": {
"AED": {
"CNY": "1.8645665591266114"
},
"AFN": {
"CNY": "0.0799337999497419"
},
"AMD": {
"CNY": "0.0175356317659951"
}
}
}
}
Signature
Every request must be signed by the merchant in order to be successfully authenticated by Zota servers. The signature
parameter of an Exchange Rates Request
must be generated by hashing a string of concatenated parameters using SHA-256
in the exact following order:
MerchantID
+ MerchantSecretKey
+ requestID
+ date
+ timestamp
+ OrderID
Signing an Orders Report Request
# prepare vars
mid='EXAMPLE-MERCHANT-ID'
secret='EXAMPLE-SECRET-KEY'
request_id='d6da50a9-aca4-4d6f-a022-f487a127b54d'
date=''
ts='1695200538'
order_type='SALE'
order_id='32452684'
# concatenate values into a single string
str="${mid}${secret}${request_id}${date}${ts}${order_id}"
echo $str
# EXAMPLE-MERCHANT-IDEXAMPLE-SECRET-KEYd6da50a9-aca4-4d6f-a022-f487a127b54d169520053832452684
# create a sha256 hash of the string
sig=$(echo -n "${str}" | openssl dgst -sha256)
echo $sig
# de2c787eb8ed6ba83812c0bf5ec9aa3c24ca20b3a4e906c85e1b036eaf558686
from hashlib import sha256
# prepare vars
mid='EXAMPLE-MERCHANT-ID'
secret='EXAMPLE-SECRET-KEY'
request_id='d6da50a9-aca4-4d6f-a022-f487a127b54d'
date=''
ts='1695200538' # str(int(time.time()))
order_type='SALE'
order_id='32452684'
# concatenate values into a single string
s = f"{mid}{secret}{request_id}{date}{ts}{order_id}"
# create a sha256 hash of the string
sig = sha256(s.encode('utf-8')).hexdigest()
print( sig ) # de2c787eb8ed6ba83812c0bf5ec9aa3c24ca20b3a4e906c85e1b036eaf558686
const crypto = require('crypto'); // npm install crypto
mid='EXAMPLE-MERCHANT-ID'
secret='EXAMPLE-SECRET-KEY'
request_id='d6da50a9-aca4-4d6f-a022-f487a127b54d'
date=''
ts='1695200538' // parseInt((new Date().getTime()) / 1000).toString()
order_type='SALE'
order_id='32452684'
const s = mid + secret + request_id + date + ts + order_id;
const sig = crypto.createHash('sha256').update(s).digest('hex');
console.log(sig); // de2c787eb8ed6ba83812c0bf5ec9aa3c24ca20b3a4e906c85e1b036eaf558686
Error Codes
Context
Every request to Zota's API is answered with a response. Every response from Zota will be structured as following:
HTTP Response
Content-Type: application/json
Zota
-> Merchant
Name | Description |
---|---|
code |
A status code representing the acceptance of the request by Zota server. |
message |
When code is other than 200 , this parameter may hold information about the reason / error. |
data |
When code is 200 , this parameter will hold a JSON object with different fields, depending on the requested resource and context. |
Response Codes
Code | Meaning | Common message values |
---|---|---|
200 |
OK | - |
400 |
Invalid or malformed request | bad request , endpoint currency mismatch , missing arguments , expired ... |
401 |
Invalid signature | unauthorized |
404 |
Not found | not found |
409 |
Conflict | order already created , already requested ... |
410 , 500 |
Error | unresolved , internal error |
Signature Helper
Merchants may find this tool very helpful for signature
testing and debugging, with a dedicated section for each of the signing use-cases.
Common Resources
Order Statuses
Status | Description |
---|---|
CREATED |
Order was created. |
PROCESSING |
Order is being processed, continue polling until final status. |
APPROVED |
Order is approved, final status. |
DECLINED |
Order is declined, final status. |
FILTERED |
Order is declined by fraud-prevention system. |
PENDING |
Order is still in processing state, awaiting next step. |
UNKNOWN |
Order status is unknown, please inform our support team. not a final status. |
ERROR |
Order is declined due to a technical error, please inform our support team. final status. |
Test Cards
Use the following test cards in our Sandbox environment in order to simulate different statuses and cases for Card Payment Integration.
Card Number | Card Type | Status | 3D Secure |
---|---|---|---|
4222222222222222 | Visa | Approved | No |
4222222222347466 | Visa | Pending | Yes |
5555555555555557 | MasterCard | Approved | No |
5595883393160089 | MasterCard | Pending | Yes |
3555555555555552 | JCB | Approved | No |
3530185156387088 | JCB | Pending | Yes |
0000000000000000 | - | Error | No |
0000000000001111 | - | Filtered | No |
3D Secure Sandbox Form
Value | Status |
---|---|
12345 | Approved |
Any other card number / 3D Secure value will result with DECLINED
status.
Test Cards V2
In order to test your integration with our payment gateway, you can use our sandbox environment and test credit cards. The sandbox environment simulates the behavior of our production environment, but without processing actual payments. This allows you to test your integration thoroughly before going live.
Please note that you should never use real credit card information in the sandbox environment or any testing environment. Instead, you should use the following test credit card information:
- Card Number: You can use any valid credit card number to test your integration or use our Card Number generator below
- Expiration Date: You can use any expiration date in the future to test your integration.
Card Number | Card Type | |
---|---|---|
4222222222222222 |
Visa | |
5555555555555557 |
MasterCard | |
3530185156387088 |
JCB | |
6011000000000000 |
Discover | |
6759111111111111 |
Maestro | |
3411111111111111 |
Amex |
Non-3D Orders:
The following CVV2 codes can be used in the Sandbox environment to get different statuses for test orders which are not 3D secured.
Value | Status |
---|---|
000 | APPROVED |
001 | DECLINED |
010 | UNKNOWN |
1234 / any other 4 digits CVV2 code | PENDING + 3D Secure |
Please note that any other 3-digit CVV2 values will result in an ERROR status.
3D Orders
In order to test the 3D Secure flow, use any 4 digit CVV2 code.
This will invoke the 3D Secure authentication process, and you will need to use one of the below OTP codes to complete the transaction with the desired status.
For 3D-secured orders, you can use the following OTP codes to simulate different test cases:
Value | Status |
---|---|
123456 | APPROVED |
654321 | DECLINED |
000000 | UNKNOWN |
Please note that any other 3D Secure OTP value will result in an ERROR status.
Currency Codes
Code | Name |
---|---|
EUR |
Euro € |
USD |
US Dollar $ |
BTC |
Bitcoin ₿ |
CNY |
Yuan Renminbi ¥ |
AFN |
Afghani ؋ |
DZD |
Algerian Dinar دج |
ARS |
Argentine Peso $ |
AMD |
Armenian Dram ֏ |
AWG |
Aruban Florin ƒ |
AUD |
Australian Dollar $ |
AZN |
Azerbaijanian Manat ₼ |
BSD |
Bahamian Dollar $ |
BHD |
Bahraini Dinar BD |
THB |
Baht ฿ |
PAB |
Panamanian Balboa B/ |
BBD |
Barbados Dollar Bds$ |
BYR |
Belarussian Ruble Br |
BZD |
Belize Dollar $ |
BMD |
Bermudian Dollar $ |
VEF |
Venezuelan Bolivar Bs |
BOB |
Bolivian Boliviano Bs |
BRL |
Brazilian Real R$ |
BND |
Brunei Dollar $ |
BGN |
Bulgarian Lev Лв |
BIF |
Burundi Franc FBu |
CVE |
Cabo Verde Escudo Esc |
CAD |
Canadian Dollar $ |
KYD |
Cayman Islands Dollar $ |
XOF |
CFA Franc BCEAO CFA |
XAF |
CFA Franc BEAC FCFA |
XPF |
CFP Franc ₣ |
CLP |
Chilean Peso $ |
COP |
Colombian Peso $ |
KMF |
Comoro Franc CF |
CDF |
Congolese Franc FC |
BAM |
Convertible Mark KM |
NIO |
Cordoba Oro C$ |
CRC |
Costa Rican Colon ₡ |
CUP |
Cuban Peso ₱ |
CZK |
Czech Koruna Kč |
GMD |
Gambian Dalasi D |
DKK |
Danish Krone Kr |
MKD |
Macedonian Denar Ден |
DJF |
Djibouti Franc Fdj |
DOP |
Dominican Peso RD$ |
VND |
Vietnamese Dong ₫ |
XCD |
East Caribbean Dollar $ |
EGP |
Egyptian Pound ج.م |
SVC |
El Salvador Colon $ |
ETB |
Ethiopian Birr ብር |
FKP |
Falkland Islands Pound £ |
FJD |
Fiji Dollar $ |
HUF |
Hungaria Forint Ft |
GHS |
Ghana Cedi GH₵ |
GIP |
Gibraltar Pound £ |
HTG |
Haitian Gourde G |
PYG |
ParaguayanGuarani ₲ |
GNF |
Guinea Franc FG |
GYD |
Guyana Dollar G$ |
HKD |
Hong Kong Dollar HK$ |
UAH |
Ukranian Hryvnia ₴ |
ISK |
Iceland Krona Íkr |
INR |
Indian Rupee ₹ |
IRR |
Iranian Rial ﷼ |
IQD |
Iraqi Dinar ع.د |
JMD |
Jamaican Dollar $ |
JOD |
Jordanian Dinar د.ا |
KES |
Kenyan Shilling Ksh |
PGK |
Papua New Ginea Kina K |
LAK |
Lao Kip ₭ |
HRK |
Croatian Kuna kn |
KWD |
Kuwaiti Dinar د.ك |
MWK |
MalawaianKwacha MK |
AOA |
Angolan Kwanza Kz |
MMK |
Burmese Kyat K |
GEL |
Georgian Lari ლ |
LBP |
Lebanese Pound ل.ل |
ALL |
Albanian Lek L |
HNL |
Honduran Lempira L |
SLL |
Sierra Leonean Leone Le |
LRD |
Liberian Dollar L$ |
LYD |
Libyan Dinar ل.د |
SZL |
Swazi Lilangeni L |
LSL |
Loti L |
MGA |
Malagasy Ariary Ar |
MYR |
Malaysian Ringgit RM |
MUR |
Mauritius Rupee ₨ |
MXN |
Mexican Peso $ |
MDL |
Moldovan Leu L |
MAD |
Moroccan Dirham MAD |
MZN |
Mozambique Metical MT |
NGN |
Nigerian Naira ₦ |
ERN |
Eritrean Nakfa نافكا |
NAD |
Namibia Dollar N$ |
NPR |
Nepalese Rupee रू |
ANG |
Netherlands Antillean Guilder ƒ |
ILS |
New Israeli Sheqel ₪ |
TWD |
New Taiwan Dollar NT$ |
NZD |
New Zealand Dollar $ |
BTN |
Ngultrum Nu. |
KPW |
North Korean Won ₩ |
NOK |
Norwegian Krone kr |
PEN |
Nuevo Sol S/ |
TOP |
Tongan Pa’anga T$ |
PKR |
Pakistan Rupee ₨ |
MOP |
Pataca MOP$ |
CUC |
Peso Convertible CUC$ |
UYU |
Peso Uruguayo $U |
PHP |
Philippine Peso ₱ |
GBP |
Pound Sterling £ |
BWP |
Botswanan Pula P |
QAR |
Qatari Rial ر.ق |
GTQ |
Guatemalan Quetzal Q |
ZAR |
South African Rand R |
OMR |
Rial Omani ر.ع. |
KHR |
Cambodian Riel ៛ |
RON |
Romanian Leu lei |
MVR |
Maldivian Rufiyaa Rf |
IDR |
Indonesian Rupiah Rp |
RUB |
Russian Ruble ₽ |
RWF |
Rwanda Franc R₣ |
SHP |
Saint Helena Pound £ |
SAR |
Saudi Riyal ﷼ |
RSD |
Serbian Dinar din |
SCR |
Seychelles Rupee SRe |
SGD |
Singapore Dollar $ |
SBD |
Solomon Islands Dollar Si$ |
KGS |
Som Лв |
SOS |
Somali Shilling Sh |
TJS |
Tajikistani Somoni ЅM |
LKR |
Sri Lanka Rupee ரூ |
SDG |
Sudanese Pound ج.س. |
SRD |
Surinam Dollar $ |
SEK |
Swedish Krona kr |
CHF |
Swiss Franc SFr |
SYP |
Syrian Pound £S |
BDT |
Bangeladash Taka ৳ |
WST |
Tala WS$ |
TZS |
Tanzanian Shilling TSh |
KZT |
Kazahstani Tenge ₸ |
TTD |
Trinidad and Tobago Dollar TT$ |
MNT |
Tugrik ₮ |
TND |
Tunisian Dinar د.ت |
TRY |
Turkish Lira ₺ |
TMT |
Turkmenistan New Manat T |
AED |
UAE Dirham د.إ |
UGX |
Uganda Shilling USh |
UZS |
Uzbekistan Sum so'm |
VUV |
Vanuatu Vatu VT |
KRW |
Won ₩ |
YER |
Yemeni Rial ﷼ |
JPY |
Yen ¥ |
ZMW |
Zambian Kwacha ZK |
ZWL |
Zimbabwe Dollar Z$ |
PLN |
Polish Zloty zł |
Country Codes
Country Code | Country Name |
---|---|
AF |
Afghanistan |
AL |
Albania |
DZ |
Algeria |
AS |
American Samoa |
AD |
Andorra |
AO |
Angola |
AI |
Anguilla |
AQ |
Antarctica |
AG |
Antigua and Barbuda |
AR |
Argentina |
AM |
Armenia |
AW |
Aruba |
AU |
Australia |
AT |
Austria |
AZ |
Azerbaijan |
BS |
Bahamas |
BH |
Bahrain |
BD |
Bangladesh |
BB |
Barbados |
BY |
Belarus |
BE |
Belgium |
BZ |
Belize |
BJ |
Benin |
BM |
Bermuda |
BT |
Bhutan |
BO |
Bolivia |
BQ |
Bonaire, Sint Eustatius and Saba |
BA |
Bosnia and Herzegovina |
BW |
Botswana |
BV |
Bouvet Island |
BR |
Brazil |
IO |
British Indian Ocean Territory |
BN |
Brunei Darussalam |
BG |
Bulgaria |
BF |
Burkina Faso |
BI |
Burundi |
CV |
Cabo Verde |
KH |
Cambodia |
CM |
Cameroon |
CA |
Canada |
KY |
Cayman Islands |
CF |
Central African Republic |
TD |
Chad |
CL |
Chile |
CN |
China |
CX |
Christmas Island |
CC |
Cocos (Keeling) Islands |
CO |
Colombia |
KM |
Comoros |
CD |
Congo Democratic Republic |
CG |
Congo |
CK |
Cook Islands |
CR |
Costa Rica |
HR |
Croatia |
CU |
Cuba |
CW |
Curaçao |
CY |
Cyprus |
CZ |
Czechia |
CI |
Cote d Ivoire |
DK |
Denmark |
DJ |
Djibouti |
DM |
Dominica |
DO |
Dominican Republic |
EC |
Ecuador |
EG |
Egypt |
SV |
El Salvador |
GQ |
Equatorial Guinea |
ER |
Eritrea |
EE |
Estonia |
SZ |
Swaziland |
ET |
Ethiopia |
FK |
Falkland Islands (Malvinas) |
FO |
Faroe Islands |
FJ |
Fiji |
FI |
Finland |
FR |
France |
GF |
French Guiana |
PF |
French Polynesia |
TF |
French Southern Territories |
GA |
Gabon |
GM |
Gambia |
GE |
Georgia |
DE |
Germany |
GH |
Ghana |
GI |
Gibraltar |
GR |
Greece |
GL |
Greenland |
GD |
Grenada |
GP |
Guadeloupe |
GU |
Guam |
GT |
Guatemala |
GG |
Guernsey |
GN |
Guinea |
GW |
Guinea-Bissau |
GY |
Guyana |
HT |
Haiti |
HM |
Heard & McDonald Islands |
VA |
Holy See |
HN |
Honduras |
HK |
Hong Kong |
HU |
Hungary |
IS |
Iceland |
IN |
India |
ID |
Indonesia |
IR |
Iran |
IQ |
Iraq |
IE |
Ireland |
IM |
Isle of Man |
IL |
Israel |
IT |
Italy |
JM |
Jamaica |
JP |
Japan |
JE |
Jersey |
JO |
Jordan |
KZ |
Kazakhstan |
KE |
Kenya |
KI |
Kiribati |
KP |
Korea (Democratic Republic) |
KR |
Korea (Republic) |
KW |
Kuwait |
KG |
Kyrgyzstan |
LA |
Lao Democratic Republic |
LV |
Latvia |
LB |
Lebanon |
LS |
Lesotho |
LR |
Liberia |
LY |
Libya |
LI |
Liechtenstein |
LT |
Lithuania |
LU |
Luxembourg |
MO |
Macao |
MK |
Macedonia |
MG |
Madagascar |
MW |
Malawi |
MY |
Malaysia |
MV |
Maldives |
ML |
Mali |
MT |
Malta |
MH |
Marshall Islands |
MQ |
Martinique |
MR |
Mauritania |
MU |
Mauritius |
YT |
Mayotte |
MX |
Mexico |
FM |
Micronesia |
MD |
Moldova |
MC |
Monaco |
MN |
Mongolia |
ME |
Montenegro |
MS |
Montserrat |
MA |
Morocco |
MZ |
Mozambique |
MM |
Myanmar |
NA |
Namibia |
NR |
Nauru |
NP |
Nepal |
NL |
Netherlands |
NC |
New Caledonia |
NZ |
New Zealand |
NI |
Nicaragua |
NE |
Niger |
NG |
Nigeria |
NU |
Niue |
NF |
Norfolk Island |
MP |
Northern Mariana Islands |
NO |
Norway |
OM |
Oman |
PK |
Pakistan |
PW |
Palau |
PS |
Palestine, State of |
PA |
Panama |
PG |
Papua New Guinea |
PY |
Paraguay |
PE |
Peru |
PH |
Philippines |
PN |
Pitcairn |
PL |
Poland |
PT |
Portugal |
PR |
Puerto Rico |
QA |
Qatar |
RO |
Romania |
RU |
Russian Federation |
RW |
Rwanda |
RE |
Réunion |
BL |
Saint Barthélemy |
SH |
Saint Helena |
KN |
Saint Kitts and Nevis |
LC |
Saint Lucia |
MF |
Saint Martin (French part) |
PM |
Saint Pierre and Miquelon |
VC |
Saint Vincent and the Grenadines |
WS |
Samoa |
SM |
San Marino |
ST |
Sao Tome and Principe |
SA |
Saudi Arabia |
SN |
Senegal |
RS |
Serbia |
SC |
Seychelles |
SL |
Sierra Leone |
SG |
Singapore |
SX |
Sint Maarten (Dutch part) |
SK |
Slovakia |
SI |
Slovenia |
SB |
Solomon Islands |
SO |
Somalia |
ZA |
South Africa |
GS |
South Georgia |
SS |
South Sudan |
ES |
Spain |
LK |
Sri Lanka |
SD |
Sudan |
SR |
Suriname |
SJ |
Svalbard and Jan Mayen |
SE |
Sweden |
CH |
Switzerland |
SY |
Syrian Arab Republic |
TW |
Taiwan |
TJ |
Tajikistan |
TZ |
Tanzania, United Republic of |
TH |
Thailand |
TL |
Timor-Leste |
TG |
Togo |
TK |
Tokelau |
TO |
Tonga |
TT |
Trinidad and Tobago |
TN |
Tunisia |
TR |
Turkey |
TM |
Turkmenistan |
TC |
Turks and Caicos Islands |
TV |
Tuvalu |
UG |
Uganda |
UA |
Ukraine |
AE |
United Arab Emirates |
GB |
United Kingdom |
US |
United States of America |
UY |
Uruguay |
UZ |
Uzbekistan |
VU |
Vanuatu |
VE |
Venezuela |
VN |
Vietnam |
VG |
Virgin Islands (British) |
VI |
Virgin Islands (U.S.) |
WF |
Wallis and Futuna |
EH |
Western Sahara |
YE |
Yemen |
ZM |
Zambia |
ZW |
Zimbabwe |
AX |
Aland Islands |
QZ |
Kosovo |
AN |
Netherlands Antilles |
UM |
United States Minor Outlying Islands |
State Codes
Country Code | State Code | State Name |
---|---|---|
AU |
AC |
Australian Capital Territory |
AU |
NS |
New South Wales |
AU |
NT |
Northern Territory |
AU |
QL |
Queensland |
AU |
SA |
South Australia |
AU |
TA |
Tasmania |
AU |
VI |
Victoria |
AU |
WA |
Western Australia |
CA |
AB |
Alberta |
CA |
BC |
British Columbia |
CA |
MB |
Manitoba |
CA |
NB |
New Brunswick |
CA |
NL |
Newfoundland and Labrador |
CA |
NT |
Northwest Territories |
CA |
NS |
Nova Scotia |
CA |
NU |
Nunavut |
CA |
ON |
Ontario |
CA |
PE |
Prince Edward Island |
CA |
QC |
Quebec |
CA |
SK |
Saskatchewan |
CA |
YT |
Yukon |
US |
AL |
Alabama |
US |
AK |
Alaska |
US |
AS |
American Samoa |
US |
AZ |
Arizona |
US |
AR |
Arkansas |
US |
CA |
California |
US |
CO |
Colorado |
US |
CT |
Connecticut |
US |
DE |
Delaware |
US |
DC |
District of Columbia |
US |
FL |
Florida |
US |
GA |
Georgia |
US |
GU |
Guam |
US |
HI |
Hawaii |
US |
ID |
Idaho |
US |
IL |
Illinois |
US |
IN |
Indiana |
US |
IA |
Iowa |
US |
KS |
Kansas |
US |
KY |
Kentucky |
US |
LA |
Louisiana |
US |
ME |
Maine |
US |
MD |
Maryland |
US |
MA |
Massachusetts |
US |
MI |
Michigan |
US |
MN |
Minnesota |
US |
MS |
Mississippi |
US |
MO |
Missouri |
US |
MT |
Montana |
US |
NE |
Nebraska |
US |
NV |
Nevada |
US |
NH |
New Hampshire |
US |
NJ |
New Jersey |
US |
NM |
New Mexico |
US |
NY |
New York |
US |
NC |
North Carolina |
US |
ND |
North Dakota |
US |
OH |
Ohio |
US |
OK |
Oklahoma |
US |
OR |
Oregon |
US |
PA |
Pennsylvania |
US |
PR |
Puerto Rico |
US |
RI |
Rhode Island |
US |
SC |
South Carolina |
US |
SD |
South Dakota |
US |
TN |
Tennessee |
US |
TX |
Texas |
US |
UT |
Utah |
US |
VT |
Vermont |
US |
VI |
Virgin Islands |
US |
VA |
Virginia |
US |
WA |
Washington |
US |
WV |
West Virginia |
US |
WI |
Wisconsin |
US |
WY |
Wyoming |
Supported Language Codes
Language | Code |
---|---|
Japanese |
ja |
Chinese |
zh |
Korean |
ko |
Malay |
ms |
Thai |
th |
Vietnamese |
vi |
Indonesian |
id |
Spanish |
es |
Portuguese |
pt |
French |
fr |
Italian |
it |
Russian |
ru |
Callback IPs
To ensure seamless integration and secure communication between Metagate and your systems, it's crucial to recognize and allowlist the specific IP addresses we use for sending callbacks. This section provides an overview of our production and sandbox environment's IP addresses. Configuring your systems to accept callbacks from these addresses is essential for receiving transaction updates.
Production server IPs:
18.158.191.139
, 18.198.240.187
, 3.122.154.23
, 13.36.22.5
, 15.236.215.248
, 35.180.81.96
Sandbox server IPs:
18.198.219.119
, 3.124.25.16
, 3.125.82.248