python – How does the bitcoinpay documentation get the hash from the server response?

Question:

I accept payments through bitcoinpay.com In the documentation I came across an example of payment verification. Before using this method, I decided to do what is described in the example. And no matter how hard I tried, I can't get the same hash that they get. Here's an example from the documentation . But I will cite it here as well. An example of a response from the server:

{"data": {"status": "pending", "payment_id": "rvnEvs3pzGVlBQaE", "settled_currency": "USD", "server_time": 1438681512, "paid_amount": "0.03545288", "reference": "{\"customer_email\": \"john.doe@example.org\", \"order_number\": 1234, \"customer_name\": \"John Doe\"}", "payment_url": "https://bitcoinpay.com/en/sci/invoice/btc/rvnEvs3pzGVlBQaE/", "price": "10.00", "confirmations": -1, "settled_amount": "6.95", "txid": "", "currency": "USD", "create_time": 1438688711, "item": "Order #1234", "address": "172eh5xbTW9Fu4EvaNZt1ymoUz9pV2snYG", "timeout_time": 1438689611, "paid_currency": "BTC", "description": "Order #1234 description"}}

It is known that callback password, as I understand it, api key – QKG7m{dzv32mmYGN

the hash is calculated from a string like <response> <password> and the hash is 3ee96c641d5fe230343950839aff469fa7c79b52bfefc5790bb9308ddeab605a

I reduced everything to a string, and then I calculated the hash, tried it all and so

j = {"data": {.....}}
d = j["data"]

Then I came across other documentation from the same site, where the hash was calculated not from the whole answer, but from the date section, and the values ​​were sorted by keys in alphabetical order. I also tried to do it for the example above.

import hashlib

d = {"status": "pending", "payment_id": "rvnEvs3pzGVlBQaE", "settled_currency": "USD", "server_time": 1438681512, "paid_amount": "0.03545288", "reference": "{\"customer_email\": \"john.doe@example.org\", \"order_number\": 1234, \"customer_name\": \"John Doe\"}", "payment_url": "https://bitcoinpay.com/en/sci/invoice/btc/rvnEvs3pzGVlBQaE/", "price": "10.00", "confirmations": -1, "settled_amount": "6.95", "txid": "", "currency": "USD", "create_time": 1438688711, "item": "Order #1234", "address": "172eh5xbTW9Fu4EvaNZt1ymoUz9pV2snYG", "timeout_time": 1438689611, "paid_currency": "BTC", "description": "Order #1234 description"}
a = "QKG7m{dzv32mmYGN"

s = ""
l = d.keys()
l.sort()
for c in l:
    s+=str(d[c])

s +=a
h = hashlib.sha256(s).hexdigest()
print h

The hash didn't match anyway. Tell me how they calculated this hash in the first example? And then I am at a loss as to how to count it at home. It seems that it is written that the line is from whole data + callback password, but in reality it turns out not like that. Maybe someone has already implemented it in python? I would be glad to see examples.

PS I came across a ready-made example from a module for some cms, there it looked something like this:

hash('sha256', '$RESPONSE_HTTP' . '$CALLBACK_PASSWORD') 

I'm not familiar with PHP, but I think that there is a concatenation of the response with a password and the calculation of the hash.

PPS I tried to calculate using a live example (listing below). That in the first, that in the second way, the hashes did not converge.

#!/usr/bin/python
#--*--coding: utf-8--*--

import json
import hashlib
from urllib2 import Request, urlopen

pwd = "мой_callback_password"

login = "zxcv432fg"
email = "zxc@dfg.ru"
satoshi = 0.001
values = {
    "settled_currency": "BTC",
    "return_url": "http://site.ru/buy/thankyou.html",
    "notify_url": "http://site.ru/cgi-bin/btc/order-received.cgi",
    "notify_email": "mail@mail.ru",
    "price": satoshi,
    "currency": "BTC",
    "reference": {
      "customer_name": login,
      "order_number": 123,
      "customer_email": email
    },
    "item": "la2coin",
    "description": "buy la2coin"
  }

data = json.dumps(values)
headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Token мой_ключ_апи'
}
request = Request('https://www.bitcoinpay.com/api/v1/payment/btc',
                     data=data, headers=headers)

response = urlopen(request)
response_body = urlopen(request).read()

signature = response.info()['BPSignature']
data_validate = response_body + pwd

hash_string = hashlib.sha256(data_validate).hexdigest()

print "ответ - ", response_body, '\n'
print "строка для хеширования - ", data_validate, '\n'
print "Хеш с заголовка - ", signature, '\n'
print "Рассчитаный хеш - ", hash_string, '\n'

if signature == hash_string:
    print "Validated succesfully!"
else:
    print "Wrong Signature"

Answer:

So far I have found one possible place for the "error" – incorrect hash calculation

Namely the description field. In the given example data, it is there, which means that its value is included in the string for calculating the hash. This field is not mentioned in the documentation.

For a trial, I would not form an array of keys based on the received answer, as you have done with l = d.keys() , but would form it manually in the code, as a list of fields that should take part in the hash calculation. At the same time, you can additionally check that all the required fields were received from the service.

UPDATE: Follow the link https://bitcoinpay.com/api/v1/

Not strong in Python, so I did it in PHP (by the way, the example given in the question is incorrect. Doesn't know PHP about the hash256 hashing algorithm)

 <?php
 $str = '1ADeusgHpfeB5wY3YbgCZty4zdxMF9BcLE31399569216EUR0.12231BTCtE8ZHEqEzJWUCGSDhttps://bitcoinpay.com/sci/invoice/btc/tE8ZHEqEzJWUCGSD/17.5{"customer_email": "customer@example.com", "order_number": 123, "customer_name": "Customer Name"}139956267417.31EURconfirmed1399569276f1be3a5df76864e3b7b13ded87ceac1d5c10887af6f7b1f1541f208f10d970daxxx';

 echo(hash('sha256', $str)."\n");
 ?>

As a result, I get

88fe519e4fae530df49b566d2b493a8f3836d51d2b9cf656ff4859ad9920656b

This is the same as the value of the secret.callback_password_hash field

Those. we can assume that there are errors in the documentation and the bea ... e25 hash specified there is incorrect. Most likely left over from some kind of editing.

Well, in any case, you have to try it on the website – the documentation is not always 100% correct and correct.

Scroll to Top