Skip to content

Commit

Permalink
Merge pull request #13 from F-Node-Karlsruhe/manual-payment-check
Browse files Browse the repository at this point in the history
Manual payment check
  • Loading branch information
F-Node-Karlsruhe authored Jun 28, 2021
2 parents c7aa15a + 34c9f8c commit 5bfee75
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 12 deletions.
9 changes: 9 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ def proxy(slug):
.strftime('%d.%m.%y %H:%M UTC')))



# socket endpoint to receive payment event
@socketio.on('await_payment')
def await_payment(data):
Expand All @@ -128,6 +129,14 @@ def await_payment(data):
iota_listener.socket_session_ids[user_token_hash] = request.sid


# socket endpoint for manual check on payment
@socketio.on('check_payment')
def await_payment(data):

socketio.start_background_task(iota_listener.manual_payment_check, data['iota_address'], data['user_token_hash'])





if __name__ == '__main__':
Expand Down
4 changes: 2 additions & 2 deletions data.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def user_token_hash_exists(user_token_hash):
def user_token_hash_valid(user_token_hash):
return datetime.fromisoformat(paid_db[user_token_hash]) > datetime.utcnow()

def add_to_paid_db(user_token_hash, lifetime):
paid_db[user_token_hash] = (datetime.utcnow() + timedelta(hours = lifetime)).isoformat()
def add_to_paid_db(user_token_hash, exp_time):
paid_db[user_token_hash] = exp_time.isoformat()

def pop_from_paid_db(user_token_hash):
return paid_db.pop(user_token_hash)
Expand Down
97 changes: 88 additions & 9 deletions iota.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from datetime import date, datetime
import iota_client
import queue
import json
import logging
from datetime import datetime, timedelta
from dotenv import load_dotenv
from data import get_iota_listening_addresses, add_to_paid_db, is_own_address
import os
Expand Down Expand Up @@ -41,6 +43,9 @@ def __init__(self, socketio):
# link user_token_hashes to session ids
self.socket_session_ids = {}

# manual payment checks
self.manual_payment_checks = set()

# create the iota client
self.client = iota_client.Client(nodes_name_password=[[node_url]],
mqtt_broker_options=broker_options)
Expand All @@ -63,6 +68,10 @@ def start(self):


def add_listening_address(self, iota_address):
'''
Adds an iota_address to the topics of the mqtt listener
'''

self.client.subscribe_topic('addresses/%s/outputs' % iota_address, self.on_mqtt_event)


Expand All @@ -79,24 +88,42 @@ def mqtt_worker(self):

message = self.client.get_message_data(json.loads(event['payload'])['messageId'])

if self.check_payment(message):
if self.payment_valid(message):
# this must be easier to access within value transfers
user_token_hash = bytes(message['payload']['transaction'][0]['essence']['payload']['indexation'][0]['data']).decode()

add_to_paid_db(user_token_hash, session_lifetime)

if user_token_hash in self.socket_session_ids.keys():
self.unlock_content(user_token_hash)

# emit pamyent received event to the user
self.socketio.emit('payment_received', room=self.socket_session_ids.pop(user_token_hash))


except Exception as e:
LOG.warning(e)

self.q.task_done()


def check_payment(self, message):
def unlock_content(self, user_token_hash, exp_time=None):
'''
Sends the user_token_hash to the db with the right expiration time and informs the user via socket
'''

if exp_time is None:

exp_time = datetime.utcnow() + timedelta(hours = session_lifetime)

add_to_paid_db(user_token_hash, exp_time)

# prevent key errors
if user_token_hash in self.socket_session_ids.keys():

# emit pamyent received event to the user
self.socketio.emit('payment_received', room=self.socket_session_ids.pop(user_token_hash))


def payment_valid(self, message):
'''
Check if the right amount arrived on the rigth address
'''

for output in message['payload']['transaction'][0]['essence']['outputs']:

Expand All @@ -108,10 +135,62 @@ def check_payment(self, message):

return False


def manual_payment_check(self, address, user_token_hash):
'''
Triggers a crawl on the designated address to find a payment in the past and add it to the db
'''

# easy checks first to prevent overload
if user_token_hash not in self.manual_payment_checks and is_own_address(address):

self.manual_payment_checks.add(user_token_hash)

outputs = self.client.find_outputs(addresses=[address])

for output in outputs:

message = self.client.get_message_data(output['message_id'])

if user_token_hash == bytes(message['payload']['transaction'][0]['essence']['payload']['indexation'][0]['data']).decode():

if self.payment_valid(message):

exp_time = self.get_payment_expiry(output['message_id'])

if exp_time > datetime.utcnow():

self.unlock_content(user_token_hash, exp_time)

# prevent key errors
if user_token_hash in self.socket_session_ids.keys():

# emit pamyent not found
self.socketio.emit('payment_not_found', room=self.socket_session_ids[user_token_hash])

self.manual_payment_checks.remove(user_token_hash)

def get_payment_expiry(self, message_id):
'''
Fetches the tangle to get the timestamp of the milstone referencing the message
'''

milestone_index = self.client.get_message_metadata(message_id)['referenced_by_milestone_index']

message_timestamp = datetime.fromtimestamp(self.client.get_milestone(milestone_index)['timestamp'])

return message_timestamp + timedelta(hours = session_lifetime)



def stop(self):
self.q.put(self.STOP)
LOG.info('MQTT worker stopped')
'''
Stops the iota listener gracefully
'''

self.client.disconnect()
LOG.info('MQTT client stopped')
self.q.put(self.STOP)
LOG.info('MQTT worker stopped')
self.q.queue.clear()
LOG.info('Working queue cleared')
15 changes: 14 additions & 1 deletion templates/pay.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,18 @@
socket.on('payment_received', function() {
location.reload();
});

socket.on('payment_not_found', function() {
console.log('Payment not found');
});
$('#check_payment_button').on('click', function(){
socket.emit('check_payment', {user_token_hash: '{{ user_token_hash }}', iota_address: '{{ iota_address }}'});
});
// show manual payment check after 30 seconds time
setTimeout("showCheckPayment()", 30000);
});
function showCheckPayment() {
document.getElementById("check_payment").style.display = "inline";
}
</script>
<style>
.custom-box {
Expand Down Expand Up @@ -84,5 +94,8 @@ <h5>
Firefly wallet
</a>
</h5>
<div id="check_payment" style="display:none">
<button id="check_payment_button">Check for payment</button>
</div>
</div>
</div>

0 comments on commit 5bfee75

Please sign in to comment.