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

# Generate Signature

> How to generate the cap-signature header for POST and GET requests.

To ensure the security and integrity of every transaction, the CommercePay API requires a digital signature in the cap-signature header for all API requests except Authentication endpoint. This signature allows our server to verify that the request body has not been tampered with and originated from an authorized source.

<Tabs>
  <Tab title="POST Signature Generation">
    ## POST Signature Generation

    Follow these 6 steps to generate the cap-signature for your request.

    <Steps>
      <Step title="Construct JSON (camelCase)">
        Prepare your request payload as a JSON object. Ensure all property names use camelCase (e.g., currencyCode, referenceCode).
      </Step>

      <Step title="Recursive Sorting (Ascending)">
        Sort all keys in the JSON object in ascending (A-Z) alphabetical order.

        **Important:** This must be done recursively. If your payload contains nested objects (like the customer object), their internal keys must also be sorted alphabetically.
      </Step>

      <Step title="Form the Signature String">
        Concatenate the Full Endpoint URL and the Sorted JSON string from Step 2 into one continuous string. Do **NOT** add any spaces between the URL and the JSON.

        Format: `[FULL_URL][SORTED_JSON_STRING]`
      </Step>

      <Step title="Normalize to Lowercase">
        Convert the entire combined string from Step 3 to lowercase. This ensures that minor casing differences in URLs or data do not cause a signature mismatch.
      </Step>

      <Step title="Generate HMAC-SHA256 Hash">
        Sign the lowercased string using the HMAC-SHA256 algorithm. Use your Secret Key as the cryptographic key.
      </Step>

      <Step title="Set the Header">
        Convert the resulting hash into a hexadecimal string. Place this value in the cap-signature header of your HTTP request.
      </Step>
    </Steps>

    ### Integration Example

    **Staging URL:** [https://staging-payments.commerce.asia/api/services/app/PaymentGateway/RequestPayment](https://staging-payments.commerce.asia/api/services/app/PaymentGateway/RequestPayment)

    **Secret Key:** bd90787d8e2340b49757438d66eed86d209e6a36a32d865a8287f90b033b815d

    **Raw Request Payload**

    ```json theme={null}
    {
        "currencyCode": "MYR",
        "amount": 100,
        "referenceCode": "TEST-111",
        "customer": {
            "name": "Test User",
            "email": "test.user@example.com"
        }
    }
    ```

    ### Transformation Walkthrough

    | Step  | Output / Action                                                                                                                                                                                                                   |
    | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | 1 & 2 | Sorted JSON: `{"amount":100,"currencyCode":"MYR","customer":{"email":"test.user@example.com","name":"Test User"},"referenceCode":"TEST-111"}`                                                                                     |
    | 3     | Combined: `https://staging-payments.commerce.asia/api/services/app/PaymentGateway/RequestPayment{"amount":100,"currencyCode":"MYR","customer":{"email":"test.user@example.com","name":"Test User"},"referenceCode":"TEST-111"}`   |
    | 4     | Lowercased: `https://staging-payments.commerce.asia/api/services/app/paymentgateway/requestpayment{"amount":100,"currencycode":"myr","customer":{"email":"test.user@example.com","name":"test user"},"referencecode":"test-111"}` |
    | 5 & 6 | Final Hash: `8b20a81c3db0c3d2e6344d4a5f5f8277493c7d20c019d8bd858ca2c41a159cb6`                                                                                                                                                    |

    <Warning>
      #### Troubleshooting "Invalid Signature"

      If you are receiving a **401 Unauthorized** or **Signature Mismatch** error, check the following:

      * **Minification:** Ensure your JSON string is "minified" (no extra spaces, tabs, or newlines). The signature for `{"a":1}` is different from `{"a": 1}`.

      * **Full URL:** Ensure you are using the absolute URL (including https\://) exactly as it is sent in the request.

      * **Nested Keys:** Double-check that nested objects like customer or billingAddress are sorted internally.

      * **Lowercasing:** Ensure the entire concatenated string (URL + JSON) is passed through a lowercase function before hashing.

      * **Secret Key:** Verify that you are using the correct Secret Key for the environment (Staging vs. Production).

      * Investigate payment signatures via the [Signature Validator Tool](https://staging-backoffice-payments.commerce.asia/app/paymentgateway/signaturevalidator).
    </Warning>
  </Tab>

  <Tab title="GET Signature Generation">
    ## GET Signature Generation

    For GET requests, the cap-signature is generated by converting your URL query parameters into a canonical JSON format before hashing. This ensures that the parameters cannot be tampered with in the URL.

    Follow these steps precisely to generate the signature for any GET request.

    <Steps>
      <Step title="Convert Query Parameters to JSON (camelCase)">
        Take all parameters from your query string and represent them as a JSON object. Ensure all keys are in camelCase.

        **Original URL:** ...?merchantId=MICH-01\&amount=100

        **Result:** `{"merchantId":"MICH-01","amount":100}`
      </Step>

      <Step title="Recursive Sorting (Ascending)">
        Sort all keys in the JSON object in ascending (A-Z) alphabetical order. If any parameters contain nested objects or arrays, these must also be sorted internally.
      </Step>

      <Step title="Form the Signature String">
        Concatenate the Full Endpoint URL (excluding the query string itself) and the Sorted JSON string from Step 2.

        Format: `[FULL_URL_WITHOUT_QUERY_STRING][SORTED_JSON_STRING]`
      </Step>

      <Step title="Normalize to Lowercase">
        Convert the entire combined string from Step 3 to lowercase.
      </Step>

      <Step title="Generate HMAC-SHA256 Hash">
        Sign the lowercased string using the HMAC-SHA256 algorithm with your Secret Key.
      </Step>

      <Step title="Set the Header">
        Convert the resulting hash into a hexadecimal string and place it in the cap-signature header.
      </Step>
    </Steps>

    ### GET Integration Example

    **Base URL:** `https://staging-payments.commerce.asia/api/services/app/PaymentGateway/Query`

    **Secret Key:** `bd90787d8e2340b49757438d66eed86d209e6a36a32d865a8287f90b033b815d`

    **Query Params:** `?TransactionNumber=20003D2625A16279BAB260115&Timestamp=1621851652617`

    ### Transformation Walkthrough

    | Step  | Output / Action                                                                                                                                                       |
    | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | 1 & 2 | Convert Query Params and Sorted JSON: `{"timestamp":1621851652617,"transactionNumber":"20003D2625A16279BAB260115"} `                                                  |
    | 3     | Combined: `https://staging-payments.commerce.asia/api/services/app/PaymentGateway/Query{"timestamp":1621851652617,"transactionNumber":"20003D2625A16279BAB260115"}`   |
    | 4     | Lowercased: `https://staging-payments.commerce.asia/api/services/app/paymentgateway/query{"timestamp":1621851652617,"transactionnumber":"20003d2625a16279bab260115"}` |
    | 5 & 6 | Final `cap-signature: a4f50b21c6e29ea7d93357906637a90a5ccf01016a61c52c218686b7ab0d2798`                                                                               |

    <Warning>
      #### Troubleshooting GET Requests "Invalid Signature"

      * **The "Base URL" Rule:** When forming the string in Step 3, do **NOT** include the ? or the parameters in the URL portion. The parameters are only represented within the JSON string.

      * **Minification:** Ensure your JSON string contains **NO extra whitespace** (spaces, tabs, or newlines). `{"a":1}` is valid; `{"a": 1}` will fail.

      * **Data Types:** Numeric values (like timestamp or amount) should be represented as numbers in the JSON, not strings (no quotes), unless specified otherwise.

      * **Case Sensitivity:** Step 4 (Lowercasing) is mandatory. Even if your URL is already lowercase, the entire concatenated string must be processed through a lowercase function.

      * Investigate payment signatures via the [Signature Validator Tool](https://staging-backoffice-payments.commerce.asia/app/paymentgateway/signaturevalidator).
    </Warning>
  </Tab>
</Tabs>
