> ## Documentation Index
> Fetch the complete documentation index at: https://docs.commercecash.my/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication & Signature

> How to authenticate requests and generate the cc-signature header.

## Overview

The CommerceCash Payment API uses **HMAC-SHA256 request signing** to authenticate and verify the integrity of payment initiation requests. Each merchant is issued a unique **Secret Key** which is used to generate the `cc-signature` header.

<Warning>
  The `cc-signature` header is currently required for the **Initiate Payment** endpoint. Requests without a valid signature will be rejected with `INVALID_SIGNATURE`.
</Warning>

***

## Request Header

Include the following header on every [Initiate Payment](/api-reference/payments/initiate) call:

| Header         | Type   | Required         | Description                                              |
| -------------- | ------ | ---------------- | -------------------------------------------------------- |
| `cc-signature` | string | ✅ Yes (Initiate) | HMAC-SHA256 of the raw JSON request body, Base64-encoded |

***

## Signature Generation

### Algorithm

**Pseudocode:**

```crypto theme={null}
cc-signature = Base64( HMAC-SHA256( secretKey, rawRequestBody ) )
```

### Rules

<Steps>
  <Step title="Serialise the request body to camelCase JSON">
    The signing input is the **complete raw JSON request body** sent in the HTTP request, serialised using **camelCase** property names.

    ```json theme={null}
    {
      "merchantId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "referenceNo": "MER-20240616-001",
      "amount": 15000,
      "currencyCode": "MYR",
      "productCode": "TNB",
      "storeId": "STORE-001",
      "extraProperties": "{\"accountNumber\":\"123456\"}"
    }
    ```

    <Warning>
      The JSON string sent in the HTTP body **must be byte-for-byte identical** to the string used for signing. Do not re-serialise or reformat the body after computing the signature.
    </Warning>
  </Step>

  <Step title="Compute HMAC-SHA256">
    Compute HMAC-SHA256 using:

    * **Key**: your merchant Secret Key (UTF-8 encoded bytes)
    * **Data**: the raw JSON string from Step 1 (UTF-8 encoded bytes)
  </Step>

  <Step title="Encode the result as Base64">
    Convert the raw HMAC-SHA256 byte array to a **Base64 string**. This is the value you place in the `cc-signature` header.
  </Step>
</Steps>

***

## Code Examples

<CodeGroup>
  ```csharp C# theme={null}
  using System.Security.Cryptography; 
  using System.Text; 
  using System.Text.Json;

  var requestBody = new { merchantId = "3fa85f64-5717-4562-b3fc-2c963f66afa6", referenceNo = "MER-20240616-001", amount = 15000, currencyCode = "MYR", productCode = "TNB", storeId = "STORE-001" };

  var jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };

  var rawBody = JsonSerializer.Serialize(requestBody, jsonOptions); 
  var secretKey = "your-merchant-secret-key";

  using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey)); 
  var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody)); 
  var signature = Convert.ToBase64String(hash);

  // Add to HttpClient request: 
  // request.Headers.Add("cc-signature", signature);
  ```

  ```javascript Python theme={null}
  import hmac 
  import hashlib 
  import base64 
  import json

  payload = { "merchantId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "referenceNo": "MER-20240616-001", "amount": 15000, "currencyCode": "MYR", "productCode": "TNB", "storeId": "STORE-001" }

  secret_key = "your-merchant-secret-key" 
  raw_body = json.dumps(payload, separators=(',', ':'))  

  # compact camelCase
  signature = base64.b64encode( hmac.new( secret_key.encode('utf-8'), raw_body.encode('utf-8'), hashlib.sha256 ).digest() ).decode('utf-8')

  # Add to request headers:
  # headers = { "cc-signature": signature }
  ```

  ```javascript JavaScript / Node.js theme={null}
  const crypto = require('crypto');
  const payload = { merchantId: "3fa85f64-5717-4562-b3fc-2c963f66afa6", referenceNo: "MER-20240616-001", amount: 15000, currencyCode: "MYR", productCode: "TNB", storeId: "STORE-001" };

  const secretKey = "your-merchant-secret-key"; 
  const rawBody = JSON.stringify(payload); // already camelCase in JS
  const signature = crypto .createHmac('sha256', secretKey) .update(rawBody, 'utf8') .digest('base64');

  // Add to fetch/axios headers: 
  // headers: { "cc-signature": signature }
  ```

  ```php PHP theme={null}
  <?php
  $payload = [ 'merchantId'  => '3fa85f64-5717-4562-b3fc-2c963f66afa6', 
                'referenceNo' => 'MER-20240616-001', 
                'amount'      => 15000, 
                'currencyCode'=> 'MYR', 
                'productCode' => 'TNB', 
                'storeId'     => 'STORE-001', ];

  $secretKey = 'your-merchant-secret-key'; 
  $rawBody   = json_encode($payload, JSON_UNESCAPED_SLASHES);

  $signature = base64_encode( hash_hmac('sha256', $rawBody, $secretKey, true) );

  // Add to cURL/Guzzle headers: 
  // 'cc-signature: ' . $signature
  ?>
  ```
</CodeGroup>

***

## Error Responses

| Scenario                           | HTTP Status | `errorCode`         | `message`                                                          |
| ---------------------------------- | ----------- | ------------------- | ------------------------------------------------------------------ |
| `cc-signature` header is invalid   | `400`       | `INVALID_SIGNATURE` | `"Invalid request signature"`                                      |
| Merchant secret key not configured | `400`       | `INVALID_SIGNATURE` | `"Signature secret key not configured for merchant: {merchantId}"` |

### Example 400 Response

```json theme={null}
{
  "isSuccess": false,
  "errorCode": "INVALID_SIGNATURE",
  "message": "Invalid request signature",
  "data": null
}
```

***

## Security Notes

<AccordionGroup>
  <Accordion title="Constant-time comparison">
    The server compares signatures using `CryptographicOperations.FixedTimeEquals`, which prevents **timing-based side-channel attacks**. Do not attempt to probe the signature by varying bytes.
  </Accordion>

  <Accordion title="Secret key storage">
    Store your Secret Key in a secrets manager or environment variable. Never hard-code it in client-side code or commit it to source control.
  </Accordion>

  <Accordion title="Body integrity">
    Because the signature is computed over the full request body, any modification to the payload in transit will cause signature validation to fail, protecting against tampering.
  </Accordion>
</AccordionGroup>
