Skip to content

Commit 84ecb5c

Browse files
Merge pull request #6 from com-chain/webhook
Webhook
2 parents c0aa877 + 96af2cb commit 84ecb5c

File tree

6 files changed

+221
-60
lines changed

6 files changed

+221
-60
lines changed

Webhook.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
$NANT_TRANSFERT = "0xa5f7c148";
4+
$CM_TRANSFERT = "0x60ca9c4c";
5+
$private_key_path ='../ComChain/comchainwebhook_rsa';
6+
$public_key_url ='https://com-chain.org/comchainwebhook_rsa.pub';
7+
8+
9+
10+
function createWebhookMessage($tr_hash, $server_name, $store_id, $store_ref, $from_add,$rawtx){
11+
$funct = $rawtx.substring(78,8);
12+
$type_tr = ("0x".$funct == CM_TRANSFERT)? 'TransferCredit' : 'Transfer';
13+
$dest = "0x".$rawtx.substring(110,40);
14+
$amount = hexdec($rawtx.substring(150,64))/100.0;
15+
$time = time();
16+
17+
$base_link = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? "https" : "http";
18+
$base_link .= "://";
19+
$base_link .= $_SERVER['HTTP_HOST'];
20+
21+
$link = array (
22+
"href"=>$base_link."/api.php?hash=".$tr_hash,
23+
"method"=>"GET"
24+
);
25+
26+
$amount = array (
27+
'sent'=> $amount,
28+
'type'=> $type_tr,
29+
'currency' => $server_name
30+
);
31+
32+
33+
$resources = array (
34+
'id'=>$tr_hash,
35+
'create_time'=> $time,
36+
'state'=>'completed',
37+
'store_id' => $store_id,
38+
'reference' => $store_ref,
39+
'links'=>[$link],
40+
41+
'addr_to' => $dest,
42+
'amount'=>$amount
43+
);
44+
if (strlen($from_add)>0) {
45+
$resources['addr_from']=$from_add;
46+
}
47+
48+
49+
$data = array ('id'=>$tr_hash,
50+
'create_time'=>$time,
51+
'resource_type'=>'sale',
52+
'event_type'=> 'PAYMENT.SALE.COMPLETED',
53+
'summary'=>'A sale has been completed. The payement has been processed.',
54+
'links'=>[$link],
55+
'resources'=>$resources
56+
);
57+
58+
return $data;
59+
}
60+
61+
// Sign and send message
62+
function sendWebhook($url, $message) {
63+
$json_message = json_encode($message);
64+
$hash = crc32($json_message);
65+
$sign = "";
66+
$sign_key = openssl_pkey_get_private($private_key_path);
67+
if (openssl_sign($hash, $sign, $sign_key)) {
68+
$signed = base64_encode($sign);
69+
$ch = curl_init();
70+
curl_setopt($ch, CURLOPT_URL, $url);
71+
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json',
72+
'COMCHAIN-TRANSMISSION-SIG:'.$signed,
73+
'COMCHAIN-AUTH-ALGO:RSA',
74+
'COMCHAIN-CERT-URL:'.$public_key_url));
75+
curl_setopt($ch, CURLOPT_POST, 1);
76+
curl_setopt($ch, CURLOPT_HEADER, 1);
77+
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_message);
78+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
79+
80+
$passed=true;
81+
if (!$response = curl_exec( $ch )){
82+
$passed=false;
83+
}
84+
curl_close($ch);
85+
86+
if ($passed) {
87+
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
88+
$passed = $code>=200 and $code<300;
89+
}
90+
return $passed;
91+
} else {
92+
return false;
93+
}
94+
}
95+
96+
97+
?>

api.php

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22
header('Access-Control-Allow-Origin: *');
3+
include './checkAdmin.php';
4+
include './Webhook.php';
35
require_once 'libs/jsonRPCClient.php';
46

57
$gethRPC = new jsonRPCClient('http://127.0.0.1:8545');
@@ -78,6 +80,9 @@ function getEstimatedGas($txobj, $gethRPC){
7880
}
7981
return json_encode($data);
8082
}
83+
84+
85+
8186
function getEthCall($txobj, $gethRPC){
8287
$data = getDefaultResponse();
8388
try {
@@ -161,13 +166,13 @@ function validateShop($shop_id, $server_name){
161166
$cluster = Cassandra::cluster('127.0.0.1') ->withCredentials("transactions_ro", "Public_transactions")->build();
162167
$keyspace = 'comchain';
163168
$session = $cluster->connect($keyspace);
164-
$query = "SELECT * FROM sellers WHERE store_id = ? AND server_name = ? ";
169+
$query = "SELECT webhook_url FROM sellers WHERE store_id = ? AND server_name = ? ";
165170
$options = array('arguments' => array($shop_id, $server_name));
166171
foreach ($session->execute(new Cassandra\SimpleStatement($query), $options) as $row) {
167-
return true;
172+
return $row['webhook_url'];
168173
}
169174

170-
return false;
175+
return "";
171176
}
172177

173178

@@ -176,10 +181,14 @@ function validateShopData() {
176181
$shop_id=$_REQUEST['shopId'];
177182
$shop_ref=$_REQUEST['txId'];
178183
$server_name=$_REQUEST['serverName'];
179-
return (isset($shop_id) && isset($server_name) && isset($shop_ref) && validateShop( $shop_id, $server_name));
184+
if (isset($shop_id) && isset($server_name) && isset($shop_ref)) {
185+
return validateShop( $shop_id, $server_name);
186+
} else {
187+
return "";
188+
}
180189
}
181190

182-
function storeAdditionalData($transaction_ash) {
191+
function storeAdditionalData($is_valid_shop, $transaction_ash, $web_hook_status) {
183192
$shop_id=$_REQUEST['shopId'];
184193
$shop_ref=$_REQUEST['txId'];
185194
$delegate=$_REQUEST['delegate'];
@@ -189,7 +198,11 @@ function storeAdditionalData($transaction_ash) {
189198
$do_insert=false;
190199
$fields =array();
191200
$val =array();
192-
if (validateShopData()) {
201+
202+
$fields['wh_status'] = $web_hook_status;
203+
$val[]='?';
204+
205+
if ($is_valid_shop) {
193206
$fields['store_id'] = $shop_id;
194207
$fields['store_ref'] = $shop_ref;
195208
$val[]='?';
@@ -211,7 +224,8 @@ function storeAdditionalData($transaction_ash) {
211224
$val[]='?';
212225
}
213226

214-
if (sizeof($fields)>0) {
227+
// Add if not only the status
228+
if (sizeof($fields)>1) {
215229
$fields['hash'] = $transaction_ash;
216230
$val[]='?';
217231
// build the query
@@ -228,16 +242,59 @@ function storeAdditionalData($transaction_ash) {
228242

229243
function sendRawTransaction($rawtx,$gethRPC){
230244
$data = getDefaultResponse();
245+
246+
$shop_url = validateShopData();
247+
248+
249+
250+
$contract = '';
251+
$dest = '';
252+
$amount = 0;
253+
$to_bal = 0;
254+
$wh_status = 0;
231255
try {
256+
if (strlen($shop_url)>0) {
257+
$config = getServerConfig($_REQUEST['serverName']);
258+
$contract = $config->{'contract_1'};
259+
// if so get the dest
260+
$dest = '0x'.$rawtx.substr(110,40);
261+
// get the sender
262+
// TODO $sender = TransactionEcRecover($rawtx)[0];
263+
264+
// get the amount
265+
$amount = hexdec($rawtx.substr(150,64));
266+
// get the balances for dest
267+
$to_bal = getBalance($dest, $contract);
268+
// TODO $from_bal = getBalance($sender, $contract);
269+
$wh_status = 1;
270+
}
271+
232272
$data['data'] = getRPCResponse($gethRPC->eth_sendRawTransaction($rawtx));
273+
274+
if (strlen($shop_url)>0 && $amount > 0) {
275+
// get the balances check if changes compatible the the amount
276+
$to_bal_after = getBalance($dest, $contract);
277+
$from_bal_after = getBalance($sender, $contract);
278+
if ($to_bal_after - $to_bal >= $amount) { // TODO} && $from_bal - $from_bal_after >= $amount) {
279+
// if so : send the webhook
280+
$message = createWebhookMessage($data['data'], $_REQUEST['serverName'],
281+
$_REQUEST['shopId'], $_REQUEST['txId'],
282+
"", $rawtx); // TODO ""=> $sender
283+
$res = sendWebhook($shop_url, $message);
284+
if ($res) {
285+
$wh_status = 3;
286+
} else {
287+
$wh_status = 2;
288+
}
289+
}
290+
}
233291
}
234292
catch (exception $e) {
235293
$data['error'] = true;
236294
$data['msg'] = 'E1'.$e->getMessage();
237295
}
238-
239296
try {
240-
storeAdditionalData($data['data']);
297+
storeAdditionalData(strlen($shop_url)>0, $data['data'], $wh_status);
241298
}
242299
catch (exception $e) {
243300
$data['error'] = true;
@@ -246,6 +303,7 @@ function sendRawTransaction($rawtx,$gethRPC){
246303
return json_encode($data);
247304
}
248305

306+
249307
function formatAddress($addr){
250308
if (substr($addr, 0, 2) == "0x")
251309
return $addr;

checkAdmin.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function getServerConfig($server){
2929
return $json->{'server'};
3030
}
3131

32-
function getAccType($address,$contract){
32+
function getAccType($address, $contract){
3333
$url = getServerAddress()."/api.php";
3434
$ch = curl_init();
3535
$ethCall = ['to' =>$contract,
@@ -53,7 +53,7 @@ function getAccType($address,$contract){
5353
return substr($data,-1);
5454
}
5555

56-
function getAccStatus($address,$contract){
56+
function getAccStatus($address, $contract){
5757
$url = getServerAddress()."/api.php";
5858
$ch = curl_init();
5959
$ethCall = ['to' =>$contract,
@@ -78,6 +78,33 @@ function getAccStatus($address,$contract){
7878
return substr($data,-1);
7979
}
8080

81+
function getBalance($address, $contract){
82+
$url = getServerAddress()."/api.php";
83+
$ch = curl_init();
84+
$ethCall = ['to' =>$contract,
85+
'data' => '0x70a08231000000000000000000000000'.substr($address,2)
86+
];
87+
$fields = ['ethCall'=>$ethCall];
88+
$fields_string = http_build_query($fields);
89+
90+
curl_setopt($ch, CURLOPT_URL, $url);
91+
// Set so curl_exec returns the result instead of outputting it.
92+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
93+
curl_setopt($ch, CURLOPT_POST, count($fields));
94+
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
95+
96+
// Get the response and close the channel.
97+
$response = curl_exec($ch);
98+
curl_close($ch);
99+
100+
$json = json_decode($response);
101+
$data= $json->{'data'};
102+
103+
return hexdec($data);
104+
}
105+
106+
107+
81108
function checkSign($dat, $signature, $caller){
82109

83110
return $caller==personal_ecRecover($dat, $signature);

ecrecover_helper.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@
66
require_once './Keccak.php';
77
use kornrunner\Keccak;
88

9+
10+
function TransactionEcRecover($rawTx) {
11+
// get the signature, last 134 chars
12+
$len = strlen($rawTx);
13+
$len_data = $len - 134;
14+
$data = substr($rawTx, 0, $len_data);
15+
$signature = substr($rawTx, $len_data);
16+
$v = substr($signature,0,2);
17+
$r = substr($signature,4,64);
18+
$s = substr($signature,70,64);
19+
$signed = '0x'.$r.$s.$v;
20+
return ecRecoverPublic($data, $signed);
21+
}
22+
23+
924
function personal_ecRecover($msg, $signed) {
1025
return personal_ecRecoverPublic($msg, $signed)[0];
1126
}
@@ -16,7 +31,7 @@ function ecRecover($hex, $signed) {
1631

1732
function personal_ecRecoverPublic($msg, $signed) {
1833
$personal_prefix_msg = "\x19Ethereum Signed Message:\n". strlen($msg). $msg;
19-
$hex = keccak256($personal_prefix_msg);
34+
$hex = keccak256WithPrefix($personal_prefix_msg);
2035
return ecRecoverPublic($hex, $signed);
2136
}
2237

@@ -45,7 +60,7 @@ function ecRecoverPublic($hex, $signed) {
4560
$publicKey = Signature::recoverPublicKey($rGmp, $sGmp, $messageGmp, $recovery);
4661
$publicKeyString = $publicKey["x"] . $publicKey["y"];
4762

48-
return array('0x'. substr(keccak256(hex2bin($publicKeyString)), -40),$publicKeyString);
63+
return array('0x'. substr(keccak256WithPrefix(hex2bin($publicKeyString)), -40),$publicKeyString);
4964
}
5065

5166
function strToHex($string)
@@ -54,7 +69,7 @@ function strToHex($string)
5469
return '0x' . array_shift($hex);
5570
}
5671

57-
function keccak256($str) {
72+
function keccak256WithPrefix($str) {
5873
return '0x'. Keccak::hash($str, 256);
5974
}
6075

parser.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def find_zip(libprefix):
173173
session.execute(cqlInsertHash)
174174

175175
# Check if the transaction is in the pending transaction table (webshop_transactions)
176-
cqlcommand = "SELECT hash, store_id, store_ref, delegate , message_from, message_to, toTimestamp(now()) AS stamp FROM webshop_transactions WHERE hash='{}'".format(transHash)
176+
cqlcommand = "SELECT hash, store_id, store_ref, wh_status, delegate , message_from, message_to, toTimestamp(now()) AS stamp FROM webshop_transactions WHERE hash='{}'".format(transHash)
177177
rows = sessioStaging.execute(cqlcommand)
178178
additional_fields = []
179179
additional_values = []
@@ -202,11 +202,16 @@ def find_zip(libprefix):
202202
additional_fields.append('store_id')
203203
additional_values.append("'{}'".format(row.store_id))
204204
additional_fields.append('store_ref')
205-
additional_values.append("'{}'".format(row.store_ref)) # check for '
205+
additional_values.append("'{}'".format(row.store_ref))
206+
207+
status = row.wh_status
208+
nb_attempt ='0'
209+
if status>1: # 2 failed attempt / 3 success
210+
nb_attempt ='1'
206211
additional_fields.append('status')
207-
additional_values.append("1") # New shop transction
212+
additional_values.append(status) # New shop transction
208213
additional_fields.append('tr_attempt_nb')
209-
additional_values.append("0")
214+
additional_values.append(nb_attempt)
210215
additional_fields.append('tr_attempt_date')
211216
additional_values.append("'{}'".format(row.stamp-10800000))
212217

0 commit comments

Comments
 (0)