Signature generation and verification

All the communication between merchant and the ECommPay payment platform is protected by using the TLS protocol version 1.2 or higher, though on the HTTP protocol level messages between merchant and the payment platform are not encrypted. Therefore, it vital to make sure that the information is not modified or tampered on the way from sender to receiver. To this end, every message is digitally signed by using the secret key issued by ECommPay for the merchant and known only to the ECommPay payment platform and the merchant. The signature also indirectly authenticates the identity of the message author, because the secret key is known only to the merchant and the ECommPay payment platform.

You must add a digital signature to every message you send to the ECommPay payment platform and verify the signature for every message you receive from the payment platform. This section describes how to calculate digital signatures for outgoing messages and verify the integrity of incoming messages when using the ECommPay payment platform.

Example of signature calculation for a Payment Page request

This section describes how to calculate and add a signature in a request before sending it to Payment Page.

Scenario

Suppose that you need to calculate the signature of a Payment Page request in the following scenario:

  • Secret key obtained from the ECommPay support service: secret
  • The parameters you need to pass when opening Payment Page:
    • project_id: 12345
    • payment_id: X03936
    • payment_amount: 3550
    • payment_currency: USD
    • payment_description: Guyliner purchase
    • customer_first_name: Jack
    • customer_last_name: Sparrow
    • customer_phone: 02081234567

If you choose to open the payment page by using the configObj object, it will look similar to the following (the order of the "parameter-value" pairs may be different):

{
"project_id": 12345, 
"payment_id": "X03936", 
"payment_amount": 2035, 
"payment_currency": "USD",
"payment_description": "Guyliner purchase",
"customer_first_name": "Jack",
"customer_last_name": "Sparrow",
"customer_phone": 02081234567,
"close_on_missclick": true,
"signature": "<digital signature to calculate>" 
}

The task is to calculate the signature; in other words, you need to compute the value for the signature parameter and add it in the request. Note that you do not use the signature parameter when calculating the digital signature. The parameter is the last to add in the parameter collection.

Signature calculation

The signature is calculated as follows:

  1. For each of the parameters except for signature, join parameter name and parameter value with colon sign (:); delete any commas between "key-value" pairs and any quotation marks that delimit string values:
    project_id:12345 
    payment_id:X03936
    payment_amount:2035
    payment_currency:USD
    payment_description:Guyliner purchase
    customer_first_name:Jack
    customer_last_name:Sparrow
    customer_phone:02081234567
    close_on_missclick:1
  2. If required, do the following:

    • Convert all the strings to UTF-8.
    • Encode any Boolean values as follows: replace false with 0, replace true with 1.
    • Leave any empty parameter values empty. In other words, do not replace any empty values with anything like blank space or null, for instance "payment_description":"" is replaced with payment_description:.
  3. Sort the strings in alphabetical order:
    close_on_missclick:1
    customer_first_name:Jack
    customer_last_name:Sparrow
    customer_phone:02081234567
    payment_amount:2035
    payment_currency:USD
    payment_description:Guyliner purchase
    payment_id:X03936
    project_id:12345
    
  4. Join all the strings in a single string by using semicolon (;) as a delimiter:
    close_on_missclick:1;customer_first_name:Jack;customer_last_name:Sparrow;customer_phone:02081234567;payment_amount:2035;payment_currency:USD;payment_description:Guyliner purchase;payment_id:X03936;project_id:12345
  5. Calculate the HMAC code for the string by using the SHA-512 hash function and secret key. The HMAC code must be calculated as a raw binary data.
  6. Encode the HMAC code by using the Base64 scheme. The resulting string is the value for the signature parameter.

    Here is the signature generated for the parameters collection from step 3 by using the secret secret key:

    vV1YUoH1XnSowQiJJEHHyBwuKxCy1t+TWwD+E/Q+OpeFagZpDT4TSi98yJGegIYbTTstx16+0IMCOMxizec/vA==

Here is the complete configObj object with the signature parameter:

{
"project_id": 12345, 
"payment_id": "X03936", 
"payment_amount": 2035, 
"payment_currency": "USD",
"payment_description": "Guyliner purchase",
"customer_first_name": "Jack",
"customer_last_name": "Sparrow",
"customer_phone": 02081234567,
"close_on_missclick": true,
"signature": "vV1YUoH1XnSowQiJJEHHyBwuKxCy1t+TWwD+E/Q+OpeFagZpDT4TSi98yJGegIYbTTstx16+0IMCOMxizec/vA==" 
}

Example of callback signature verification

All the communication between merchant and the ECommPay payment platform is protected by using the TLS protocol version 1.2 or higher, though on the HTTP protocol level messages between merchant and the payment platform are not encrypted. Therefore, it vital to make sure that the information is not modified or tampered on the way from sender to receiver—you are required to verify the signature of every callback you receive from the payment platform. You verify the callback signature by calculating the actual signature using all the callback parameters and compare the calculated value with the signature included in the callback. If the signatures are the same, the callback is valid, otherwise the callback is corrupted and cannot be used in the payment processing procedure.

This section describes how to verify the signature of a callback by calculating the signature and comparing it with the signature included in the callback.

Scenario

Suppose that you need to verify the signature a callback you received from Payment Page in the following scenario:

  • Secret key obtained from the ECommPay support service: secret
  • The callback body contains the following information:
    {
      "request_id": "17cd0535c5ffecf5-335e5b5e",
      "transaction": {
        "id": 82452138542211,
        "date": "2019-10-09T07:13:36+0000",
        "type": "purchase"
      },
      "payment": {
        "id": "123456789",
        "method": "webmoney",
        "date": "2019-10-09T07:13:36+0000",
        "result_code": "9999",
        "result_message": "Awaiting processing",
        "status": "awaiting redirect result",
        "is_new_attempts_available": false,
        "attempts_timeout": 0,
        "cascading_with_redirect": false,
        "provider_id": 1234
      },
      "sum_real": {
        "amount": 29100,
        "currency": "USD"
      },
      "account": {
        "number": "79879496816"
      },
      "return_url": {
        "method": "GET",
        "body": {
          "shop": "1234567",
          "transaction": 45712154,
          "iframe": "true",
          "successUrl": "https://mysite.com/process/success",
          "failUrl": "https://mysite.com/process/failure",
          "target": "iframe"
        },
        "encrypted": []
      },
      "rrn": "",
      "general": {
        "project_id": 123,
        "payment_id": "457822332658",
        "signature": "NtDutuRiksyHeBhhUs+nQxQ1FcMSueoACb4vENju0APgHgeZfRfMj46289v1vD4hJ1a8Yhg=="
      },
      "description": "TEST_1543831735980",
      "sum_request": {
        "amount": 29100,
        "currency": "USD"
      },
      "operations": [
        {
          "id": 45712154,
          "type": "sale",
          "status": "awaiting redirect result",
          "date": "2019-10-09T07:13:36+0000",
          "processing_time": null,
          "request_id": "3bd75dc1977cc8c05b50855b-544f7f6af3d989dd42ef8e6ff02df56eef7c5f4e-521457",
          "sum": {
            "amount": 29100,
            "currency": "USD"
          },
          "code": "9999",
          "message": "Awaiting processing"
        }
      ]
    }

Signature verification

The signature is verified as follows:

  1. Remove the signature parameter and its value from the callback:
    {
      "request_id": "17cd0535c5ffecf5-335e5b5e",
      "transaction": {
        "id": 82452138542211,
        "date": "2019-10-09T07:13:36+0000",
        "type": "purchase"
      },
      "payment": {
        "id": "123456789",
        "method": "webmoney",
        "date": "2019-10-09T07:13:36+0000",
        "result_code": "9999",
        "result_message": "Awaiting processing",
        "status": "awaiting redirect result",
        "is_new_attempts_available": false,
        "attempts_timeout": 0,
        "cascading_with_redirect": false,
        "provider_id": 1234
      },
      "sum_real": {
        "amount": 29100,
        "currency": "USD"
      },
      "account": {
        "number": "79879496816"
      },
      "return_url": {
        "method": "GET",
        "body": {
          "shop": "1234567",
          "transaction": 45712154,
          "iframe": "true",
          "successUrl": "https://mysite.com/process/success",
          "failUrl": "https://mysite.com/process/failure",
          "target": "iframe"
        },
        "encrypted": []
      },
      "rrn": "",
      "general": {
        "project_id": 123,
        "payment_id": "457822332658",
        "signature": "NtDutuRiksyHeBhhUs+nQxQ1FcMSueoACb4vENju0APgHgeZfRfMj46289v1vD4hJ1a8Yhg=="
      },
      "description": "TEST_1543831735980",
      "sum_request": {
        "amount": 29100,
        "currency": "USD"
      },
      "operations": [
        {
          "id": 45712154,
          "type": "sale",
          "status": "awaiting redirect result",
          "date": "2019-10-09T07:13:36+0000",
          "processing_time": null,
          "request_id": "3bd75dc1977cc8c05b50855b-544f7f6af3d989dd42ef8e6ff02df56eef7c5f4e-521457",
          "sum": {
            "amount": 29100,
            "currency": "USD"
          },
          "code": "9999",
          "message": "Awaiting processing"
        }
      ]
    }
  2. Convert each parameter into a string that contains the full path to the parameter (with all its parents), parameter name, and parameter value:

    <parent_1>:...:<parent_n>:<parameter_name>:<parameter_value>

    Parents are the object(s) and/or arrays in which the parameter is contained. Parents are ordered by embedding level starting with the topmost one. The parent names, parameter name, and parameter value are delimited by colon (:); delete any commas between "key-value" pairs and any quotation marks that delimit string values.

    If required, do the following:

    • Convert all the strings to UTF-8.
    • Encode any Boolean values as follows: replace false with 0, replace true with 1. For instance, the false values of the "is_new_attempts_available" parameter is replaced with 0.

      This rule applies only to Boolean values! If any string parameter contains "false" or "true" value, the value is not replaced with 0 or 1 but is treated as any other string value.

    • Leave any empty parameter values empty. In other words, do not replace any empty values with anything like blank space or null, for instance "payment_description":"" is replaced with payment_description:.
    • Empty arrays are totally ignored and not included in the string set used to generate the signature. In our example the "encrypted": [] array is left out when collecting the string set that is used to generate the signature.
    • Add index numbers to array elements starting with zero, for instance ["alpha", "beta", "gamma"] is replaced with three strings: 0:alpha, 1:beta, and 2:gamma.

    Thus, the request turns into the following collection of strings:

    request_id:17cd0535c5ffecf5-335e5b5e
    transaction:id:82452138542211
    transaction:date:2019-10-09T07:13:36+0000
    transaction:type:purchase
    payment:id:123456789
    payment:method:webmoney
    payment:date:2019-10-09T07:13:36+0000
    payment:result_code:9999
    payment:result_message:Awaiting processing
    payment:status:awaiting redirect result
    payment:is_new_attempts_available:0
    payment:attempts_timeout:0
    payment:cascading_with_redirect:0
    payment:provider_id:1234
    sum_real:amount:29100
    sum_real:currency:USD
    account:number:79879496816
    return_url:method:GET
    return_url:body:shop:1234567
    return_url:body:transaction:45712154
    return_url:body:iframe:true
    return_url:body:successUrl:https://mysite.com/process/success
    return_url:body:failUrl:https://mysite.com/process/failure
    return_url:body:target:iframe
    rrn:
    general:project_id:123
    general:payment_id:457822332658
    description:TEST_1543831735980
    sum_request:amount:29100
    sum_request:currency:USD
    operations:0:id:45712154
    operations:0:type:sale
    operations:0:status:awaiting redirect result
    operations:0:date:2019-10-09T07:13:36+0000
    operations:0:processing_time:
    operations:0:request_id:3bd75dc1977cc8c05b50855b
    operations:0:sum:amount:29100
    operations:0:sum:currency:USD
    operations:0:code:9999
    operations:0:message:Awaiting processing
    
  3. Sort the strings in alphabetical order:
    account:number:79879496816
    description:TEST_1543831735980
    general:payment_id:457822332658
    general:project_id:123
    operations:0:code:9999
    operations:0:date:2019-10-09T07:13:36+0000
    operations:0:id:45712154
    operations:0:message:Awaiting processing
    operations:0:processing_time:
    operations:0:request_id:3bd75dc1977cc8c05b50855b
    operations:0:status:awaiting redirect result
    operations:0:sum:amount:29100
    operations:0:sum:currency:USD
    operations:0:type:sale
    payment:attempts_timeout:0
    payment:cascading_with_redirect:0
    payment:date:2019-10-09T07:13:36+0000
    payment:id:123456789
    payment:is_new_attempts_available:0
    payment:method:webmoney
    payment:provider_id:1234
    payment:result_code:9999
    payment:result_message:Awaiting processing
    payment:status:awaiting redirect result
    request_id:17cd0535c5ffecf5-335e5b5e
    return_url:body:failUrl:https://mysite.com/process/failure
    return_url:body:iframe:true
    return_url:body:shop:1234567
    return_url:body:successUrl:https://mysite.com/process/success
    return_url:body:target:iframe
    return_url:body:transaction:45712154
    return_url:method:GET
    rrn:
    sum_real:amount:29100
    sum_real:currency:USD
    sum_request:amount:29100
    sum_request:currency:USD
    transaction:date:2019-10-09T07:13:36+0000
    transaction:id:82452138542211
    transaction:type:purchase
    
  4. Join all the strings in a single string by using semicolon (;) as a delimiter:
    account:number:79879496816;description:TEST_1543831735980;general:payment_id:457822332658;general:project_id:123;operations:0:code:9999;operations:0:date:2019-10-09T07:13:36+0000;operations:0:id:45712154;operations:0:message:Awaiting processing;operations:0:processing_time:;operations:0:request_id:3bd75dc1977cc8c05b50855b-544f7f6af3d989dd42ef8e6ff02df56eef7c5f4e-521457;operations:0:status:awaiting redirect result;operations:0:sum:amount:29100;operations:0:sum:currency:USD;operations:0:type:sale;payment:attempts_timeout:0;payment:cascading_with_redirect:0;payment:date:2019-10-09T07:13:36+0000;payment:id:123456789;payment:is_new_attempts_available:0;payment:method:webmoney;payment:provider_id:1234;payment:result_code:9999;payment:result_message:Awaiting processing;payment:status:awaiting redirect result;request_id:17cd0535c5ffecf5-335e5b5e;return_url:body:failUrl:https://mysite.com/process/failure;return_url:body:iframe:true;return_url:body:shop:1234567;return_url:body:successUrl:https://mysite.com/process/success;return_url:body:target:iframe;return_url:body:transaction:45712154;return_url:method:GET;rrn:;sum_real:amount:29100;sum_real:currency:USD;sum_request:amount:29100;sum_request:currency:USD;transaction:date:2019-10-09T07:13:36+0000;transaction:id:82452138542211;transaction:type:purchase
  5. Calculate the HMAC code for the string by using the SHA-512 hash function and secret key. The HMAC code must be calculated as a raw binary data.
  6. Encode the HMAC code by using the Base64 scheme.

    rnv1OS3PJUKEJ5kw5wqoK0ftZGSd4Q6LX5A5NxK6d5alpND4sQTRFt7/9aFV+m3SRwNB8ba98GMsOY91yTVhEQ==

    This is the calculated signature.

  7. Compare the calculated signature and the one included in the callback.

    In our case, the signature differ which means that the callback is invalid and must be ignored.

    There are several reasons why the signatures may differ: the callback was tampered with, the sender incorrectly calculated the signature, or you incorrectly calculated the control signature. The last one is the most popular cause of signature verification failure. Therefore, before opening a case with the support, make sure you use the correct procedure and the correct secret key.