From c64295885234768fcca3ade8a6e6e9d32641ab24 Mon Sep 17 00:00:00 2001 From: IKalonji2 Date: Mon, 25 Jul 2022 22:47:21 +0200 Subject: [PATCH] Completed MVP for XDC ChatPay --- .gitignore | 2 + Procfile | 1 + README.md | 44 ++++++++++++++ app.py | 125 ++++++++++++++++++++++++++++++++++++++ messages.py | 123 +++++++++++++++++++++++++++++++++++++ registered_users.py | 27 +++++++++ requirements.txt | Bin 0 -> 512 bytes user.py | 20 ++++++ wallet_handler.py | 145 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 487 insertions(+) create mode 100644 Procfile create mode 100644 README.md create mode 100644 app.py create mode 100644 messages.py create mode 100644 registered_users.py create mode 100644 requirements.txt create mode 100644 user.py create mode 100644 wallet_handler.py diff --git a/.gitignore b/.gitignore index 67045665..666b01d3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ yarn-debug.log* yarn-error.log* lerna-debug.log* +/xdcchatpay + # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/Procfile b/Procfile new file mode 100644 index 00000000..8001d1a5 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn app:app \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..ae5ddc79 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +## Inspiration +Although crytpto and blockchain has been on an increase in developing nations, there has been slow uptake due to low literacy levels, trust, device constraints and the lack of cash on/off ramps. + +We are looking at building and launching XDC ChatPay as a simple yet effective remittance solution that addresses the challenges above. Being simple yet effective, our solution will drive users to use the remittance platform which has been built on the XDC network in order to increase adoption. + +## What it does + +XDC ChatPay allows users to easily on/off ramp by voucher top-ups or withdrawals directly to and from accounts using the [1Voucher](https://1foryou.com/) service, users are also able to send and receive funds in seconds. We are looking into adding additional functionalities like purchasing of utility vouchers, airtime, and allowing for scanning of QR codes for easy payments + +## How we built it + +The MVP was built using: + +**Twilio for the Whatsapp messaging functionality** +**Python Flask app for the backend** +**Tatum.io for the XDC chain account integration** +**Heroku for hosting** + +## Challenges we ran into + +When ran into challenges when conneting the XinFin api endpoints on Tatum for account creation, however I was able to overcome this and get it to work. Also, doing the user research proved to be challenging because of time constraints. The business approval time on the 1Voucher platform is lengthy so I was not able to include the live voucher authentication to the demo + +## Accomplishments that we're proud of + +We managed to build a working MVP using the mainnet endpoints, + +## What we learned + +We learned a lot about the XinFin chain and also about designing simple yet effective systems. + +## What's next for XDC ChatPay + +Implement live voucher integration +Improve on application and user security +Add additional functionality to application + +## TO run the application locally + +- Create an account on Twilio +- Install ngrok +- Create a Python virtual environment +- PIP install the requirements.txt file +- Run the flask application +- Connect the ngrok endpoint to your Twilio account and start sending XDC ChatPay messages. \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 00000000..cbaa8ca7 --- /dev/null +++ b/app.py @@ -0,0 +1,125 @@ +import click +from flask import Flask, render_template, request, redirect, url_for, flash, jsonify +from twilio.twiml.messaging_response import MessagingResponse +from messages import Messages + +from registered_users import RegisteredUsers +from user import User +from wallet_handler import WalletHandler + +app = Flask(__name__) + +registered_users = RegisteredUsers() + +wallet_handler = WalletHandler() + +message_template = Messages() + +@app.route('/') +def index(): + return 'XDC ChatPay' + +@app.route('/chat', methods=['POST']) +def chat(): + + resp = MessagingResponse() + + print(request.form) + incoming_msg = request.values.get('Body', '').lower() + + responded = False + + if incoming_msg == 'hello xdc': + resp.message(message_template.home_menu()) + responded = True + + elif incoming_msg == '1': + resp.message(message_template.register_menu()) + responded = True + + elif incoming_msg == '2': + incoming_msg = request.values.get('WaId', '').lower() + user = registered_users.get_user(incoming_msg) + if user is None: + resp.message(message_template.wallet_not_found()) + responded = True + else: + wallet = user.get_wallet() + resp.message(message_template.view_wallet(wallet_address=wallet)) + responded = True + + elif incoming_msg == '3': + incoming_msg = request.values.get('WaId', '').lower() + user = registered_users.get_user(incoming_msg) + if user is None: + resp.message(message_template.wallet_not_found()) + responded = True + else: + wallet = user.get_wallet() + balance = wallet_handler.get_balance(wallet) + resp.message(message_template.wallet_balance(balance=balance)) + responded = True + + + elif incoming_msg == '4': + resp.message(message_template.new_payment()) + responded = True + + elif incoming_msg == '5': + resp.message(message_template.top_up_menu()) + responded = True + + elif incoming_msg == '6': + incoming_msg = request.values.get('WaId', '').lower() + user = registered_users.get_user(incoming_msg) + if user is None: + resp.message(message_template.wallet_not_found()) + responded = True + else: + wallet = user.get_wallet() + transactions = wallet_handler.get_transactions(wallet) + resp.message(message_template.transaction_history(transactions=transactions)) + responded = True + + + elif incoming_msg == '7': + resp.message(message_template.withdraw_menu()) + responded = True + + elif incoming_msg == '8': + resp.message(message_template.exit_menu()) + responded = True + + elif len(incoming_msg.split(" ")) == 2 and incoming_msg.split()[0] == 'register': + name = incoming_msg.split()[1] + account_number, accountId = wallet_handler.create_wallet(name) + key = request.values.get('WaId', '').lower() + user = registered_users.register(User(name,account_number,accountId, key)) + resp.message(message_template.wallet_created(accountId)) + responded = True + + elif len(incoming_msg.split(" ")) == 2 and incoming_msg.split()[0] == 'topup': + voucher = incoming_msg.split()[1] + user_number = request.values.get('WaId', '').lower() + user = registered_users.get_user(user_number) + topup_response = wallet_handler.buy_xdc(user.get_accountId()) + resp.message(message_template.top_up_success()) + responded = True + + elif len(incoming_msg.split(" ")) == 3 and incoming_msg.split()[0] == 'pay': + wallet_address = incoming_msg.split()[1] + amount = incoming_msg.split()[2] + user_number = request.values.get('WaId', '').lower() + user = registered_users.get_user(user_number) + payment_response = wallet_handler.send_xdc(wallet_address, amount) + resp.message(message_template.payment_sent(amount, wallet_address)) + responded = True + + if not responded: + resp.message("Sorry, I don't understand.") + return str(resp) + +if __name__ == '__main__': + app.run(debug=True) + # app.run(host='localhost', port=8080, debug=True) + diff --git a/messages.py b/messages.py new file mode 100644 index 00000000..8485cfc8 --- /dev/null +++ b/messages.py @@ -0,0 +1,123 @@ +import random + + +class Messages: + def __init__(self): + pass + + def home_menu(self): + return """ + 1. Register a wallet +2. View wallet +3. View balance +4. Send XDC +5. Buy XDC +6. Transactions +7. Withdraw XDC with 1Voucher +7. Exit +""" + + def register_menu(self): + return """ + Please enter your username in one word: +example: register JohnSmith123 + """ + + def wallet_created(self, wallet_address): + return """ + Thank you for registering your wallet. +Your wallet address is: {0} +""".format(wallet_address) + + def wallet_not_created(self): + return """ + Sorry, your wallet could not be created. +Please try again. +""" + + def wallet_not_found(self): + return """ + Sorry, your wallet could not be found. +Please try again. +""" + + def view_wallet(self, wallet_address): + return """ + Your wallet address is: {0} + """.format(wallet_address) + + def wallet_balance(self, balance): + return """ + Your wallet balance is: {0} + """.format(balance) + + def new_payment(self): + return """ + Enter; pay + wallet/number + an amount. +example: pay 123456789 100 +""" + + def payment_sent(self, amount, address): + return """ + You have sent {0} XDC to {1} + """.format(amount, address) + + def payment_not_sent(self): + return """ + Sorry, your payment could not be sent. +Please try again. +""" + + def payment_received(self, amount, address): + return """ + You have received {0} XDC from {1} + """.format(amount, address) + + def transaction_history(self, transactions): + return """ + Your transaction history is: + {0} + """.format(transactions) + + def top_up_menu(self): + return """ + Please enter topup + your 1Voucher reference number: +example: topup 123456789 +""" + + def top_up_success(self): + return """ + You have successfully toped up your wallet with 50 XDC. + """ + + def top_up_failure(self): + return """ + Sorry, your top up could not be completed. +Please try again. +""" + + def withdraw_menu(self): + return """ + Please withdraw + the amount you wish to withdraw: +example: withdraw 100 +""" + + def withdraw_success(self, amount): + return """ + You have successfully withdrawn {0} XDC. +Your 1Voucher reference is: {1} +""".format(amount, self.generate_reference()) + + def withdraw_failure(self): + return """ + Sorry, your withdrawal could not be completed. +Please try again. +""" + + def exit_menu(self): + return """ + Thank you for using XDC ChatPay. + """ + + def generate_reference(self): + return str(random.randint(1000000, 9999999)) \ No newline at end of file diff --git a/registered_users.py b/registered_users.py new file mode 100644 index 00000000..69f02441 --- /dev/null +++ b/registered_users.py @@ -0,0 +1,27 @@ +class RegisteredUsers(): + + def __init__(self): + self.registered_users = {} + + def register(self, user): + if user.number in self.registered_users: + return False + else: + self.registered_users[user.number] = user + return True + + def get_user(self, number): + if number in self.registered_users: + return self.registered_users[number] + else: + return None + + def get_all_users(self): + return self.registered_users + + def delete_user(self, number): + if number in self.registered_users: + del self.registered_users[number] + return True + else: + return False \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..99e4765af78b078d03be72f84dac2c8693c09ad4 GIT binary patch literal 512 zcmYk3OH0F05QWcL@TW91x4t%RT@-|ZLf0{EwGWdTZxH?Q>Nhi{hHxR7ne)xbxj)~v z*6NhC)rND+lQiWd6}sY-y41PORO>|*lkd!DP1LC6>y_F8-Ir5BZJGi`sH|IS@8lcj z6`K9^TC_%~oMY-K6BnG=c}KruW2nuMkwd-J3J;ZYXQOInenq{}vwLdQQyDu;zI~3b zMmr|ciDC5U-c#?`+v}(Y-Z!wujM5nkeQK_y9#1{<9)EtMm&qkmL!zPUpuNFf