Signature generation and verification
Overview
The data communication between the merchant web service and the ecommpay payment platform is protected by using the TLS protocol version 1.2 or later. This ensures the confidentiality of the data being transmitted, though the protocol cannot guarantee the message integrity and ensure that the message author possesses the secret key. Therefore, every message must be digitally signed by using the secret key issued by ecommpay for the merchant and known only to the merchant and the ecommpay payment platform.
Regardless of the interface that is used for working with the payment platform, digital signatures should be included in all requests, callbacks (for the requests processed within the asynchronous interaction model), and certain responses (for the requests processed within the synchronous interaction model). The responses that do not include signatures are usually the ones that contain only auxiliary information (for example, the response stating that the request has been declined due to the incorrect data) or general information without the payment or customer details (for example, the response containing the list of available banks). In other cases, signatures are included in the responses.
Thus, before you submit a request to the ecommpay payment platform, a digital signature should be generated and included in the request; after the callbacks and responses that contain signatures have been received from the payment platform, it is necessary to verify the received data by comparing the signatures to the ones generated on the merchant side. To implement the digital signature generation and verification, you can use either your own solutions or the SDK available from ecommpay. (For more information about the SDK, see Integration using SDK). The data integrity can be compromised for various reasons, but whenever such cases are detected, this data should not be considered valid.
This section describes the algorithms for the digital signature generation and the data integrity verification, including examples with the use of these algorithms and interactive forms for testing the workflows using signatures.
Signature generation
Signing algorithm
The algorithm input includes the following:
- Data to sign.
Generally, this is request body in JSON format without the
signature
parameter. - Signing key.
For the purposes of debugging and testing your signing algorithm implementation, you are free to use any signing key. For signing requests in production environment, you are required to use your production secret key.
Depending on the algorithm implementation, its output may be either signature or signed data. Generally, the algorithm output consists of request body in JSON format with integrated signature
parameter.
The algorithm description, the example, and test form below include the most common algorithm implementation that includes request body in JSON format both as input (without the signature
parameter) and output (with the signature
parameter).
Thus, the algorithm includes the following steps:
-
Input validation. Make sure the following requirements are met:
- Data conforms to the JSON format.
- Data to sign does not contain any
signature
parameter even if it is empty. - The signing key is readily available.
-
Conversion of all the strings to UTF-8 with alphabetical sorting. Complete the following steps:
- Encode any Boolean values as follows: replace
false
with0
, replacetrue
with1
. Mind that this rule applies only to Boolean values. If any string parameter contains"false"
or"true"
string value, the value is not replaced with0
or1
but is treated as any other string value (for example therecurring: "{type: \"U\",register: true}"
parameter doesn’t require replacingtrue
with1
). -
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>
where 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.
- Leave any empty parameter values empty. In other words, do not replace any empty values with blank space or
null
. For instance,"payment_description":""
is replaced withpayment_description:
. - Add index numbers to array elements starting with zero, for instance
["alpha", "beta", "gamma"]
is replaced with three strings:0:alpha
,1:beta
, and2:gamma
. - Empty arrays are totally ignored and not included in the string set used to generate the signature.
- Convert all the strings to UTF-8.
- Sort the strings in alphabetical order and join them in a single string by using semicolon (;) as a delimiter.
- Encode any Boolean values as follows: replace
- Calculation of HMAC code by using the key and the SHA-512 hash function. 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.
- Encoding the HMAC code by using the Base64 scheme. Encoding the HMAC code by using the Base64 scheme to obtain the signature for the initial data.
- Adding signature. Add the
signature
parameter to the request body using the signature from the previous step as its value.
Example of purchase request through Gate
Suppose that you need to generate the signature for a Gate request in the following scenario:
- Signing key :
secret
. - Preliminary request body version where the value for the
signature
is missing:{ "general": { "project_id": 3254, "payment_id": "id_38202316", "signature": "<digital signature to generate>" }, "customer": { "id": "585741", "email": "johndoe@mycompany.com", "first_name": "John", "last_name": "Doe", "address": "Downing str., 23", "identify": { "doc_number": "54122312544" }, "ip_address": "111.222.333.444" }, "payment": { "amount": 10800, "currency": "USD", "description": "Computer keyboards" }, "receipt_data": { "positions": [ { "quantity": "10", "amount": "108", "description": "Computer keyboard" } ] }, "return_url": { "success": "https://paymentpage.mycompany.com/complete-redirect?id=success", "decline": "https://paymentpage.mycompany.com/complete-redirect?id=decline" } }
The task is to generate the signature; in other words, you need to compute the value for the signature
parameter and add it in the request. Complete the following steps:
- Make sure there is no
signature
parameter in your request even if it is empty:{ "general": { "project_id": 3254, "payment_id": "id_38202316", "signature": "<digital signature to generate>" }, "customer": { "id": "585741", "email": "johndoe@mycompany.com", "first_name": "John", "last_name": "Doe", "address": "Downing str., 23", "identify": { "doc_number": "54122312544" }, "ip_address": "111.222.333.444" }, "payment": { "amount": 10800, "currency": "USD", "description": "Computer keyboards" }, "receipt_data": { "positions": [ { "quantity": "10", "amount": "108", "description": "Computer keyboard" } ] }, "return_url": { "success": "https://paymentpage.mycompany.com/complete-redirect?id=success", "decline": "https://paymentpage.mycompany.com/complete-redirect?id=decline" } }
- Convert all the strings to UTF-8 as per algorithm description:
general:project_id:3254 general:payment_id:id_38202316 customer:id:585741 customer:email:johndoe@mycompany.com customer:first_name:John customer:last_name:Doe customer:address:Downing str., 23 customer:identify:doc_number:54122312544 customer:ip_address:111.222.333.444 payment:amount:10800 payment:currency:USD payment:description:Computer keyboards receipt_data:positions:0:quantity:10 receipt_data:positions:0:amount:108 receipt_data:positions:0:description:Computer keyboard return_url:success:https://paymentpage.mycompany.com/complete-redirect?id=success return_url:decline:https://paymentpage.mycompany.com/complete-redirect?id=decline
- Sort the strings in alphabetical order:
customer:address:Downing str., 23 customer:email:johndoe@mycompany.com customer:first_name:John customer:id:585741 customer:identify:doc_number:54122312544 customer:ip_address:111.222.333.444 customer:last_name:Doe general:payment_id:id_38202316 general:project_id:3254 payment:amount:10800 payment:currency:USD payment:description:Computer keyboards receipt_data:positions:0:amount:108 receipt_data:positions:0:description:Computer keyboard receipt_data:positions:0:quantity:10 return_url:decline:https://paymentpage.mycompany.com/complete-redirect?id=decline return_url:success:https://paymentpage.mycompany.com/complete-redirect?id=success
- Join all the strings in a single string by using semicolon (;) as a delimiter:
customer:address:Downing str., 23;customer:email:johndoe@mycompany.com;customer:first_name:John;customer:id:585741;customer:identify:doc_number:54122312544;customer:ip_address:111.222.333.444;customer:last_name:Doe;general:payment_id:id_38202316;general:project_id:3254;payment:amount:10800;payment:currency:USD;payment:description:Computer keyboards;receipt_data:positions:0:amount:108;receipt_data:positions:0:description:Computer keyboard;receipt_data:positions:0:quantity:10;return_url:decline:https://paymentpage.mycompany.com/complete-redirect?id=decline;return_url:success:https://paymentpage.mycompany.com/complete-redirect?id=success
- Calculate the HMAC code for the string by using the SHA-512 hash function and secret key, and then encode the HMAC code by using the Base64 scheme:
VLLZzVNGevQNhr1b4TEhbC4qqHD17Kyn/M6FPNN93ttyk/amJgD/R6dayTKVvW6/QCRdq4hOf8R2w/xbUa8f2w==
- Add the resulting signature in the request body:
{ "general": { "project_id": 3254, "payment_id": "id_38202316", "signature": "VLLZzVNGevQNhr1b4TEhbC4qqHD17Kyn/M6FPNN93ttyk/amJgD/R6dayTKVvW6/QCRdq4hOf8R2w/xbUa8f2w==" }, "customer": { "id": "585741", "email": "johndoe@mycompany.com", "first_name": "John", "last_name": "Doe", "address": "Downing str., 23", "identify": { "doc_number": "54122312544" }, "ip_address": "111.222.333.444" }, "payment": { "amount": 10800, "currency": "USD", "description": "Computer keyboards" }, "receipt_data": { "positions": [ { "quantity": "10", "amount": "108", "description": "Computer keyboard" } ] }, "return_url": { "success": "https://paymentpage.mycompany.com/complete-redirect?id=success", "decline": "https://paymentpage.mycompany.com/complete-redirect?id=decline" } }
Request signature generation form
Signature verification
Verification algorithm
The algorithm input includes the following:
- Signed data to verify.
Generally, this is callback or response body in JSON format with the
signature
parameter. - Verification key. It must be the same key that was previously used for signing the data to verify.
Depending on the algorithm implementation, its output may be either a generated signature or the information whether the generated signature matches the one included in the response or callback.
The algorithm description, the example, and test form below use the most common algorithm implementation that includes callback or response body in JSON format as input (without the signature
parameter) and output (with the signature
parameter) which is the result of signature verification.
Thus, the algorithm includes the following steps:
-
Input validation Make sure the following requirements are met:
- Data conforms to the JSON format.
- Data to sign contains a
signature
parameter with the signature value. - The signing key is readily available.
- Extracting signature from the data to verify. Store the value of the
signature
parameter value for further reference and remove the parameter from the input data. - Generate signature for the data to verify. Complete steps 2 through 4 as specified in the signature generation algorithm description. (For more details, see the following link):
- Conversion of all the strings to UTF-8 with alphabetical sorting.
- Calculation of HMAC code by using the key and the SHA-512 hash function.
- Encoding the HMAC code by using the Base64 scheme.
- Signature matching Compare the generated signature with the signature stored in step 2. If the signatures match, the data are authentic and its integrity is considered confirmed; otherwise, the data is considered compromised and cannot be used for production purposes.
Example of callback verification
Suppose that you need to verify the signature a callback in the following scenario:
- Signing key :
secret
. - The callback body contains the following information:
Figure: Callback body
{ "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" } ] }
The signature is verified as follows:
- 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" } ] }
- Convert all parameter strings to UTF-8 according to the algorithm description:
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
- 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
- Join all 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
- Calculate the HMAC code for the string by using the SHA-512 hash function and secret key, and then encode the HMAC code by using the Base64 scheme:
rnv1OS3PJUKEJ5kw5wqoK0ftZGSd4Q6LX5A5NxK6d5alpND4sQTRFt7/9aFV+m3SRwNB8ba98GMsOY91yTVhEQ==
- Compare the generated 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.
Signature verification form