Cycle Logo

API Authentication

The Cycle payment platform validates API requests with an HMAC signature. Only requests with a correct signature are allowed to access system resources.

API Caller Credentials

You will receive these credentials when your merchant account is created. If you don’t have them, please contact your account manager.

NameDescription
Merchant Account NameAssigned by Cycle. The unique merchant account name.
Caller NameAssigned by Cycle. The API caller ID.
Caller PasswordAssigned by Cycle. The API caller password, used as the encrypted secret for the HMAC signature.

Request Security Headers

These HTTP request header values are required for each API call to the Cycle platform. Without them, the request will be treated as unauthorised.

Header NameDescription
X-MerchantAccountThe Merchant Account name assigned to you.
X-CallerNameThe name of the API Caller making the request.
X-HMAC-TimestampUnix timestamp (seconds from epoch). Signatures with timestamps more than 30 minutes old are rejected.
X-HMAC-SignatureThe generated HMAC signature.

Generate the Signature

The signature can be generated as follows (pseudo-code):


var timestamp = seconds_from_epoch_utc
var message = string_concat(callerName, merchantAccountName, timestamp, request_path, request_body)
var signature = hmac_sha256_as_hexadecimal(api_secret, message)

Example

As a concrete example, suppose we have the following:

  • X-CallerName = "cycle-api-caller"
  • X-MerchantAccount = "CycleDemo"
  • X-HMAC-Timestamp = "1633767872"
  • Caller's password = "YOUR_CALLER_PASSWORD"
  • Request URL = "https://sandbox.cyclepay.net/api/v3/healthcheck"

The message to be signed would be:

"cycle-api-callerCycleDemo1633767872/api/v3/healthcheck"

The final request would look like this:


curl --location --request GET 'https://sandbox.cyclepay.net/api/v3/healthcheck' \
--header 'X-MerchantAccount: CycleDemo' \
--header 'X-CallerName: cycle-api-caller' \
--header 'X-HMAC-Timestamp: 1633767872' \
--header 'X-HMAC-Signature: 2A5B9C3D...' \
--header 'Content-Type: application/json'

You will receive an HTTP 200 status with an empty response if your signature is correct. If the signature is wrong, you will see this error message:


{"requestId":"44d123b4-5cdc-4522-908b-3d5765addbf3","errorCode":"authentication_error","message":"HMAC Authentication failed. Invalid name or password"}

HMAC Signature Code Examples

Java


import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;

/**
* @param requestPath, the request context path, e.g "/api/v3/charges"
* @param requestBody: the request body content
* @return The HMAC signature
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public String calculateHMAC(String requestPath, String requestBody) throws NoSuchAlgorithmException, InvalidKeyException {
   // Use your configured values
   String apiSecret = "YOUR_CALLER_PASSWORD";
   String merchantAccount = "CycleDemo";
   String callerName = "cycle-api-caller";

   long timestamp = System.currentTimeMillis() / 1000;

   Mac mac = Mac.getInstance("HmacSHA256");
   SecretKeySpec keySpec = new SecretKeySpec(apiSecret.getBytes(), "HmacSHA256");
   mac.init(keySpec);

   StringBuilder message = new StringBuilder().append(callerName).append(merchantAccount);
   message.append(timestamp);

   if (requestPath != null && requestPath.trim().length() > 0) {
       message.append(requestPath);
   }
   if (requestBody != null && requestBody.trim().length() > 0) {
       message.append(requestBody);
   }
   byte[] signatureBytes = mac.doFinal(message.toString().getBytes());
   return org.apache.commons.codec.binary.Hex.encodeHexString(signatureBytes).toUpperCase(Locale.ROOT);
}

JavaScript


// message = X-CallerName + X-MerchantAccount + X-HMAC-Timestamp + path + body
var timestamp = Math.floor(new Date().getTime() / 1000);
var path = pm.request.url.getPath();
var message = environment['caller'] + environment['merchant'] + timestamp + path + (pm.request.body.raw || "");
var secret = environment['password'];
var signature = CryptoJS.HmacSHA256(message, secret).toString().toUpperCase();