Authentication Code Sample
The Nayax Spark API uses a secure authentication mechanism to protect communication between your integration and the Spark DMZ. This guide shows you the essential code for implementing authentication using cipher and signature generation.
Prerequisites
Before you begin, ensure you have the following from Nayax:
| Requirement | Description |
|---|---|
| Integrator ID | Your unique integrator identifier |
| Token ID | The token identifier for your integration |
| Secret token | A secure token string (minimum 32 characters) provided by Nayax |
| Header hash key | A key used to generate the signature for HTTP headers |
| Base URL | The Spark API endpoint URL |
Generate random number
private string CreateRandom()
{
Faker faker = new();
return faker.Random.String2(17, 17, chars: "0123456789");
}Note: This uses the Bogus library. Install via NuGet: Install-Package Bogus
Create cipher
private string CreateCipher(string sparkTransactionId, string tokenSecretString, string random, bool needTimeStampInRandom = true)
{
string timeStamp = needTimeStampInRandom ? $"{DateTime.UtcNow:yyMMddHHmm}" : string.Empty;
string unencryptedCipher = $"{sparkTransactionId}={random}{timeStamp}";
return CryptoUtils.EncryptAesECB256(unencryptedCipher, tokenSecretString);
}Format: {sparkTransactionId}={random}{timestamp}
Where:
sparkTransactionId: Transaction identifier (use "1234" for testing)random: 17-digit number fromCreateRandom()timestamp: UTC time in formatyyMMddHHmm(optional)
Create signature
public Dictionary<string, string> CreateSignature<T>(T request, int integratorId) where T : class
{
string serializedRequest = _jsonClient.Serialize(request, indent: false);
_integratorDictionary.TryGetValue(integratorId, out Dictionary<string, string> headerHashKeyDictionary);
if (headerHashKeyDictionary.IsNullOrEmpty() || serializedRequest.IsNullOrEmpty())
{
return null;
}
headerHashKeyDictionary.TryGetValue("HeaderHashKey", out string integratorValue);
if (integratorValue.IsNullOrEmpty())
{
return null;
}
return new Dictionary<string, string>
{
{ "IntegratorId", integratorId.ToString() },
{ "Signature", $"{ByteUtils.CalculateSHA256($"{serializedRequest};{integratorValue}")}" }
};
}Format: SHA-256 hash of {serializedRequest};{headerHashKey}
Request model
public class StartAuthenticationRequest3rdParty
{
public int TokenId { get; set; }
public string TerminalId { get; set; }
public int TerminalIdType { get; set; }
public string Cipher { get; set; }
public string Random { get; set; }
}Response model
public class StartAuthenticationResponse3rdParty
{
[JsonProperty("HashedSparkTransactionId", Required = Required.Always)]
public string HashedSparkTransactionId { get; set; }
[JsonProperty("Status", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
public Status Status { get; set; }
}
public class Status
{
public string Verdict { get; set; }
public int ErrorCode { get; set; }
public string StatusMessage { get; set; }
}Trigger transaction
public async Task<TriggerTransactionResponse> TriggerTransaction([FromHeader] int integratorId, [FromBody] TriggerTransactionRequest body)
{
Dictionary<string, string> signature = _SparkUtils.CreateSignature(body, integratorId);
RestOptions options = new RestOptions();
foreach (var headerEntry in signature)
{
options.AddHeader(headerEntry.Key, headerEntry.Value);
}
ApiResponse<TriggerTransactionResponse> response = await _restClient.SendAsync<TriggerTransactionResponse>(HttpMethod.Post, $"{_baseUrl}/TriggerTransaction", body, options);
return response?.Data ?? new TriggerTransactionResponse
{
Status = new Status
{
Verdict = "Declined",
ErrorCode = 29,
StatusMessage = "Failed to trigger transaction - Not Allowed to Wake Device"
}
};
}Settlement
public async Task<SparkSettlementResponse> Settlement([FromHeader] int integratorId, [FromBody] SparkSettlementRequest body)
{
Dictionary<string, string> signature = _SparkUtils.CreateSignature(body, integratorId);
RestOptions options = new RestOptions();
foreach (var headerEntry in signature)
{
options.AddHeader(headerEntry.Key, headerEntry.Value);
}
ApiResponse<SparkSettlementResponse> response = await _restClient.SendAsync<SparkSettlementResponse>(HttpMethod.Post, $"{_baseUrl}/Settlement", body, options);
return response?.Data ?? new SparkSettlementResponse
{
Status = new Status
{
Verdict = "Declined",
ErrorCode = 35,
StatusMessage = "Settlement Failed"
}
};
}Cancel transaction
[HttpPost("CancelTransaction", Name = "CancelTransaction")]
public async Task<SparkCancelTransactionResponse> CancelTransaction([FromHeader] int integratorId, [FromBody] SparkCancelTransactionRequest body)
{
Dictionary<string, string> signature = _SparkUtils.CreateSignature(body, integratorId);
RestOptions options = new RestOptions();
foreach (var headerEntry in signature)
{
options.AddHeader(headerEntry.Key, headerEntry.Value);
}
ApiResponse<SparkCancelTransactionResponse> response = await _restClient.SendAsync<SparkCancelTransactionResponse>(HttpMethod.Post, $"{_baseUrl}/CancelTransaction", body, options);
return response?.Data ?? new SparkCancelTransactionResponse
{
Status = new Status
{
Verdict = "Declined",
ErrorCode = 31,
StatusMessage = "Cancellation Failed"
}
};
}Error codes
| Error code | Description |
|---|---|
| 26 | Cipher is null or integrator ID is invalid |
| 29 | Not allowed to wake device |
| 31 | Cancellation failed |
| 35 | Settlement failed |
| -9 | Cipher validation failed |
Updated 2 minutes ago