Overview
API Base URL
https://pos.snapscan.io/merchant/api/v1
The SnapScan API is REST-like and relies on HTTP status codes to indicate API errors. We support CORS so that you can interact with our API through a client-side web application. We return JSON in all responses from the API.
- API Overview: authentication, error codes, pagination and webhooks
- QR Codes: how to generate QR codes and their URL structure
- Payments: query the status of payments that were made against your merchant account
- Cash ups: mark the end of your transaction period and query all payments completed within the associated period
WooCommerce, Magento, ShopStar, OpenCart and PayGate integrations available. We also offer a partial integration with Shopify. Please contact help@snapscan.co.za for more information.
API Overview
Authentication
# Authentication through HTTP Basic Auth
curl -u your-api-key: "api_endpoint_here"
The following response can be expected for unauthorised requests:
HTTP/1.1 401 UNAUTHORIZED
Content-Type: application/json
{
"message": "Unauthorised"
}
Access to the API is protected through an API key. Authentication occurs via HTTP Basic Auth. The basic auth username must be set to your API key and the password should be left blank.
All requests must be made over HTTPS and authenticated. Any request over HTTP will fail.
Error Codes
Example of an error response when an invalid parameter is provided:
HTTP/1.1 400 BAD REQUEST
Content-Type: application/json
{
"message": "startDate is not valid"
}
HTTP status codes are use to indicate the success or failure of an API request. In general 2xx indicates success, 4xx indicates failure due to information provided by you (eg. a required parameter is missing) while 5xx indicates that the failure was caused by the SnapScan servers (or we were unable to service the request at this time). A valid error response from us will always be a JSON object containing a message key.
Pagination
All API resources that can return multiple records (eg. the payments API) are paginated. These endpoints provide the same set of parameters that allow you to specify which page and how many records per page to return.
Query Parameters
Parameter | Default | Description |
---|---|---|
page | 1 | The current page. |
perPage | 50 | The number of records to return per page (maximum: 100). |
offset | 0 | The offset to start from. |
Response Headers
The following pagination headers are included in the response. All pagination headers are integers.
Header | Description |
---|---|
X-Total | The total number of records in the query. |
X-Total-Pages | The number of pages that the query is made up of. |
X-Page | The current page. |
X-Per-Page | The number of records per page. |
X-Next-Page | The next page. |
X-Prev-Page | The prev page |
X-Offset | The offset being used. |
Rate Limiting
All calls to our API are rate limited. In the case of your calls being throttled the API will return status code 429
.
It is best to avoid being throttled but in the case that it happens you should use exponential backoff when making requests to prevent constant throttling.
Webhook
We recommend that you make use of a webhook to be notified of payment completion due to the real-time nature of the system. During your account set up we can configure a URL to which we will POST
payment objects as we determine their status. We will only notify you of completed and errored payments through the webhook.
Note that webhooks should be treated as an unauthenticated event stream. If payment spoofing is a concern and you require higher levels of certainty about a payment, make an authenticated API call to our get payment (or get all payments) endpoint.
We POST
the data as an application/x-www-form-urlencoded
JSON string under the payload
key.
Verifying payload authenticity
An example of verifying the hash signature in Ruby:
params do
requires :payload, String
end
post '/snapscan_webhook' do
# the raw POST body that hasn't been parsed or decoded
request_body = request.body.read
verify_signature!(request_body, ENV['WEBHOOK_AUTH_KEY'])
payload = JSON.parse(params[:payload])
puts ">>> Received payload: #{payload.inspect}"
end
def verify_signature!(request_body, webhook_auth_key)
signature = OpenSSL::HMAC.hexdigest('sha256', webhook_auth_key, request_body)
auth_signature = "SnapScan signature=#{signature}"
unless Rack::Utils.secure_compare(auth_signature, headers["Authorization"])
raise "Unauthorized webhook received"
end
end
Once you have been provided with a Webhook Authentication Key it will be used to create a hash signature which will be sent along with all webhook payloads. You can use this signature to verify the authenticity of the payload received from us.
The hash is sent in the HTTP Authorization
header and is computed by creating a HMAC hexdigest of the raw request body (ie. the original URL encoded body including the payload key) using SHA256.
The Authorization
header will have the following form:
SnapScan signature=<hash>
where <hash>
will be replaced with the computed hash.
When comparing signatures we recommend you use constant time string comparison to avoid certain timing attacks, in Ruby this can be achieved using secure_compare
.
QR Codes
Creating the URL
All SnapScan QR codes contain a SnapCode to identify the merchant. Additional parameters amount
and id
can be included to pre-populate the amount owed and a unique order number on the customers phone. These values will be associated with the payment as requiredAmount
and merchantReference
in the API.
Standard SnapCode
Your unique SnapCode will be displayed on your QR code stand or may be supplied by customer support. Let's say yours is "shopalot", your URL would look as follows:
Payment Detail | URL |
---|---|
Merchant: Shopalot SnapCode: shopalot |
https://pos.snapscan.io/qr/shopalot |
Adding payment parameters
Used for online checkout, point-of-sale, vending machines and more.
In some cases, it is useful for the merchant to include the amount owed and a unique reference number to reconcile payments.
If a merchant wishes to embed the amount and reference of a payment so that this information is pulled through into the users app at payment - they simply add query parameters to the payment URL. For example:
Payment Detail | URL |
---|---|
Merchant: Shopalot SnapCode: shopalot ID: Ord123 Amount: R10.00 |
https://pos.snapscan.io/qr/shopalot?id=Ord123&amount=1000 |
Limiting successful payments to unique Id
To prevent the customer from successfully paying the same reference twice, and from paying less than the specified &amount=
parameter, simply add &strict=true
to the end of a payment URL.
Payment Detail | URL |
---|---|
Merchant: Shopalot SnapCode: shopalot ID: Ord123 Amount: R10.00 Strict: true |
https://pos.snapscan.io/qr/shopalot?id=Ord123&amount=1000&strict=true |
Adding extra parameters
You can add extra parameters to the payment URL. These details will be passed back under the extra
attribute in payments.
https://pos.snapscan.io/qr/shopalot?customValue=123
Generating a QR code
Use the QR code generator API to display a code on your site. This allows for the specification of the image type as well as the size. See the code snippet to the right as an example.
An example QR code in HTML:
<a href="https://pos.snapscan.io/qr/shopalot?id=Ord123&amount=1000">
<img src="https://pos.snapscan.io/qr/shopalot.svg?id=Ord123&amount=1000&snap_code_size=125">
</a>
- The
snap_code_size
can be between 50 and 500 (and defaults to 125) - The image type can be either
.svg
or.png
- The image type and size parameters are stripped from the generated QR code
Payments
The API's main purpose is to provide you with various ways to query the status of payments that were made against your merchant account.
Credit card processing can take a few seconds and because the API caters for real-time queries it affects how the API works. If a query contains any pending payments it is necessary to poll the API until the result is available, for real-time applications we recommend that you make use of our webhook service. Seeing as webhooks can fail due to various reasons (eg. timeouts) an integration should allow the client to manually request the status of payments.
The payment object
An example of a payment object:
{
"id": 1,
"status": "completed",
"date": "1999-12-31T23:00:00Z",
"totalAmount": 1000,
"tipAmount": 0,
"requiredAmount": 1000,
"snapCode": "STB115",
"snapCodeReference": "Till Point #1",
"userReference": "John Doe",
"merchantReference": "INV001",
"statementReference": "SNAPSCAN 20150109",
"authCode": "123456",
"isVoucher": false,
"isVoucherRedemption": false,
"extra": {
"customValue": "123"
},
"deviceSerialNumber": "R28K10HBQTX",
"transactionType": "payment"
}
Payment objects always have all possible attributes but if an attribute is not defined it will be null
.
The following attributes are provided by all QR codes:
Attribute | Type | Description |
---|---|---|
id | Integer | Our unique ID for the payment. The ID is sequential and starts from 1 . |
status | String | Indicates whether the payment was successful or failed. Possible values are: completed , error , pending |
date | String | The UTC date of when the payment was started. The date is ISO8601 formatted, eg: 1999-12-31T23:00:00Z |
totalAmount | Integer | The total amount in cents paid by the user. |
tipAmount | Integer | If this feature is enabled for your account it will include the tip amount in cents paid by the user (totalAmount includes the tipAmount). |
snapCode | String | The unique reference encoded in the QR code which is linked to your account. The code is always the same for payments made through the QR. |
snapCodeReference | String | A reference that you can choose to be defined on the QR code which will always be the same for payments made through it. |
userReference | String | If this feature is enabled for your account it will include the reference defined by the user when he made the payment. |
statementReference | String | If the payment has been settled into your bank account it will include the statement reference for the associated settlement. This is the reference that will appear on your bank statement. |
isVoucher | Boolean | Indicates whether or not a voucher was purchased for this payment. |
isVoucherRedemption | Boolean | Indicates whether or not a voucher was used to perform this payment. |
deviceSerialNumber | String | The unique serial number of the card terminal used to make the payment. |
transactionType | String | Indicates the transaction type of the payment. Can either be 'payment' or 'refund'. |
extra | Object | Any extra parameters that were sent as part of the payment URL. |
The following attributes will be returned on the payment object if you generate QR codes using the relevant payment parameters specified here:
Attribute | Type | Description |
---|---|---|
requiredAmount | Integer | The amount in cents that was required to be paid by the user. |
merchantReference | String | A unique reference defined by you and encoded in the QR code. |
authCode | String | A random number that is generated by us for payments that have a required amount and merchant reference defined. The number is 6 digits long and will be displayed to the user if the payment was successful. |
Get all payments
curl -u your-api-key: "https://pos.snapscan.io/merchant/api/v1/payments"
The above command returns JSON structured like this:
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": 2,
"status": "error",
"date": "2000-01-01T01:00:00Z",
"totalAmount": 2050,
"tipAmount": 550,
"requiredAmount": 1500,
"snapCode": "STB115",
"snapCodeReference": "Till Point #1",
"userReference": "Jane Doe",
"merchantReference": "INV002",
"statementReference": "SNAPSCAN 20150109",
"authCode": "654321",
"extra": null,
"deviceSerialNumber": "R28K10HBQTX",
"transactionType": "payment",
},
{
"id": 1,
"status": "completed",
"date": "1999-12-31T23:00:00Z",
"totalAmount": 1000,
"tipAmount": 0,
"requiredAmount": 1000,
"snapCode": "STB115",
"snapCodeReference": "Till Point #1",
"userReference": "John Doe",
"merchantReference": "INV001",
"statementReference": "SNAPSCAN 20150109",
"authCode": "123456",
"extra": null,
"deviceSerialNumber": "R28K10HBQTX",
"transactionType": "payment",
}
]
Returns all payments that have been made against your QR codes (SnapCodes). If no parameters are provided all payments made to you will be returned, subject to pagination. By default if the status parameter isn't provided completed, errored and pending payments will be returned.
A successful response is always an array and is paginated. If no payments match your query parameters the array will be empty. Payments are ordered by descending IDs, ie. the first payment will always be the latest in the returned result.
HTTP Request
GET https://pos.snapscan.io/merchant/api/v1/payments
Query Parameters
All parameters are optional.
Parameter | Description |
---|---|
startDate | Payments that were started at or after this time, eg: 2000-01-01T01:00:00Z |
endDate | Payments that were started before this time, eg: 2000-01-01T01:00:00Z |
status | A comma separated string of the following values: completed , error or pending , eg. completed,pending |
snapCode | Payments with the SnapCode. |
snapCodeReference | Payments with the SnapCode reference. |
userReference | Payments with the user reference. |
merchantReference | Payments with your reference. |
statementReference | Payments included in the settlement with the provided reference. |
Response Codes
Description | HTTP Status | JSON Response |
---|---|---|
OK | 200 | Array of payment objects |
Invalid parameter | 400 | Object containing a message key |
Get a payment
curl -u your-api-key: "https://pos.snapscan.io/merchant/api/v1/payments/1"
The above command returns JSON structured like this:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 1,
"status": "completed",
"date": "1999-12-31T23:00:00Z",
"totalAmount": 1000,
"tipAmount": 0,
"requiredAmount": 1000,
"snapCode": "STB115",
"snapCodeReference": "Till Point #1",
"userReference": "John Doe",
"merchantReference": "INV001",
"statementReference": "SNAPSCAN 20150109",
"authCode": "123456",
"extra": null,
"deviceSerialNumber": "R28K10HBQTX",
"transactionType": "payment",
}
Returns a single payment.
HTTP Request
GET https://pos.snapscan.io/merchant/api/v1/payments/{id}
Route Parameters
The id
route parameter is required.
Parameter | Description |
---|---|
id | The sequential ID of the payment. |
Response Codes
Description | HTTP Status | JSON Response |
---|---|---|
OK | 200 | A payment object |
ID doesn't exist | 404 | Object containing a message key |
Get all cash up payments
curl -u your-api-key: "https://pos.snapscan.io/merchant/api/v1/payments/cash_ups/1e07ac748d2627ba"
The above command returns JSON structured like this:
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": 3,
"status": "completed",
"date": "2000-01-01T01:00:00Z",
"totalAmount": 2050,
"tipAmount": 550,
"requiredAmount": 1500,
"snapCode": "STB115",
"snapCodeReference": "Till Point #1",
"userReference": "Jane Doe",
"merchantReference": "INV002",
"statementReference": "SNAPSCAN 20150109",
"authCode": "654321",
"extra": null,
"deviceSerialNumber": "R28K10HBQTX",
"transactionType": "payment",
},
{
"id": 1,
"status": "completed",
"date": "1999-12-31T23:00:00Z",
"totalAmount": 1000,
"tipAmount": 0,
"requiredAmount": 1000,
"snapCode": "STB115",
"snapCodeReference": "Till Point #1",
"userReference": "John Doe",
"merchantReference": "INV001",
"statementReference": "SNAPSCAN 20150109",
"authCode": "123456",
"extra": null,
"deviceSerialNumber": "R28K10HBQTX",
"transactionType": "payment",
}
]
If the period contains any pending payments you can expect the following response:
HTTP/1.1 500 INTERNAL SERVER ERRROR
Content-Type: application/json
{
"message": "The query is not ready yet because the specified cash up period contains pending payments, please try again in a moment."
}
Returns all payments that were completed successfully in the specified cash up period. If a cash up period contains any pending payments we will return a HTTP 500
. A cash up period is considered complete once we know the status of all the payments within the period. When we return a HTTP 500
due to pending payments you should wait a couple of seconds and then retry the request. See Cash Ups for further details.
HTTP Request
GET https://pos.snapscan.io/merchant/api/v1/payments/cash_ups/{reference}
Route Parameters
The reference
route parameter is required.
Parameter | Description |
---|---|
reference | The cash up period's reference. |
Response Codes
Description | HTTP Status | JSON Response |
---|---|---|
OK | 200 | Array of payment objects |
Reference doesn't exist | 404 | Object containing a message key |
Pending payments | 500 | Object containing a message key |
Cash Ups
The cash up API allows you to mark the end of your transaction period on our system. Once a transaction period has been marked you will be able to query all payments that were completed within the associated period. You can use the period's reference with the get cash up payments endpoint to retrieve the payments.
The cash up object
An example of a cash up object:
{
"date": "1999-12-31T23:00:00Z",
"reference": "ab34Def78"
}
Attribute | Type | Description |
---|---|---|
date | String | The UTC date of when the transaction period was ended. The date is ISO8601 formatted, eg: 1999-12-31T23:00:00Z |
reference | String | A hexadecimal string with a maximum length of 32. |
Create a cash up period
curl -u your-api-key: -X POST "https://pos.snapscan.io/merchant/api/v1/cash_ups"
The above command returns JSON structured like this:
HTTP/1.1 200 OK
Content-Type: application/json
{
"date": "1999-12-31T23:00:00Z",
"reference": "1e07ac748d2627ba"
}
Returns a reference that marks the end of the current transaction period and the start of a new one. The reference can be used to retrieve all the payments that were successfully completed in the associated period.
HTTP Request
POST https://pos.snapscan.io/merchant/api/v1/cash_ups
Response Codes
Description | HTTP Status | JSON Response |
---|---|---|
OK | 200 | A cash up object |
Get all cash ups
curl -u your-api-key: "https://pos.snapscan.io/merchant/api/v1/cash_ups"
The above command returns JSON structured like this:
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"date": "2000-01-01T12:00:00Z",
"reference": "2f35cdc8246915c3"
},
{
"date": "1999-12-31T23:00:00Z",
"reference": "1e07ac748d2627ba"
}
]
Returns a paginated list of all cash up references that have been created. The references are ordered by descending date.
HTTP Request
GET https://pos.snapscan.io/merchant/api/v1/cash_ups
Response Codes
Description | HTTP Status | JSON Response |
---|---|---|
OK | 200 | An array of cash up objects |
Code Snippets
Javascript
An example of fetching all payments in Javascript.
let url = "https://pos.snapscan.io/merchant/api/v1/payments";
let apiKey = "your-api-key";
let headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("Authorization", `Basic ${btoa(apiKey + ":")}`);
fetch(url, {method: "GET", headers: headers})
.then(response => response.json())
.then(data => console.log(data));
Ruby
An example of fetching all payments in Ruby.
require "net/http"
require "openssl"
url = "https://pos.snapscan.io/merchant/api/v1/payments"
api_key = "your-api-key"
uri = URI(url)
Net::HTTP.start(uri.host, uri.port,
:use_ssl => uri.scheme == "https",
:verify_mode => OpenSSL::SSL::VERIFY_PEER) do |http|
request = Net::HTTP::Get.new uri.request_uri
request.basic_auth api_key, ""
response = http.request request
puts response
puts response.body
end
end
Python
An example of fetching all payments in Python. This example assumes you have installed the requests
module.
import json
import requests
from requests.auth import HTTPBasicAuth
URL = "https://pos.snapscan.io/merchant/api/v1/payments"
API_KEY = "your-api-key"
response = requests.get(URL, auth = HTTPBasicAuth(API_KEY, ""))
print(response.status_code)
print(json.loads(response.content))
PHP
An example of fetching all payments in PHP. This example does not illustrate transforming the response into JSON since this is dependent on the module that you have installed in your project. You can also use the built-in json_decode
method for this purpose.
<?PHP
$url = "https://pos.snapscan.io/merchant/api/v1/payments";
$api_key = "your-api-key";
$opts = array(
"http" => array(
"method" => "GET",
"header" => "Authorization: Basic " . base64_encode("$api_key:")
)
);
$context = stream_context_create($opts);
$file = file_get_contents($url, false, $context);
print($file);
?>
C#
An example of fetching all payments in C#. This example does not illustrate transforming the response into JSON since this is dependent on the library that you have installed in your project.
using System;
using System.Net;
namespace Snapscan
{
class Example
{
static void Main(string[] args)
{
const String apiKey = "your-api-key";
const String url = "https://pos.snapscan.io/merchant/api/v1/payments";
var client = new WebClient { Credentials = new NetworkCredential(apiKey, "") };
var response = client.DownloadString(url);
System.Console.WriteLine(response);
}
}
}
Java
An example of fetching all payments in Java. This example does not illustrate transforming the response into JSON since this is dependent on the library that you have installed in your project.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
...
String apiKey = "your-api-key";
URL url = new URL ("https://pos.snapscan.io/merchant/api/v1/payments");
String encoding = Base64.getEncoder().encodeToString((apiKey + ":").getBytes("UTF-8"));
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.setRequestProperty("Authorization", "Basic " + encoding);
InputStream content = (InputStream)connection.getInputStream();
StringBuilder sb = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(content));
String line;
while ((line = in.readLine()) != null)
sb.append(line);
System.out.println(sb);