From 4c0c38902e88cfe0a7a012840654caf67734d7e0 Mon Sep 17 00:00:00 2001 From: Pavlo Zhuk Date: Sat, 4 Apr 2020 20:18:35 +0300 Subject: [PATCH 1/6] create __issue_invoice() --- worker.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/worker.py b/worker.py index f998d72..bc2fc85 100644 --- a/worker.py +++ b/worker.py @@ -630,19 +630,17 @@ class ChatWorker(threading.Thread): else: # Exit the function return + # Issue the invoice + self.__make_payment(amount=value) + + def __make_payment(self, amount): # Set the invoice active invoice payload self.invoice_payload = str(uuid.uuid4()) # Create the price array - prices = [telegram.LabeledPrice(label=strings.payment_invoice_label, amount=int(value))] + prices = [telegram.LabeledPrice(label=strings.payment_invoice_label, amount=int(amount))] # If the user has to pay a fee when using the credit card, add it to the prices list - fee_percentage = float(configloader.config["Credit Card"]["fee_percentage"]) / 100 - fee_fixed = int(configloader.config["Credit Card"]["fee_fixed"]) - total_fee = value * fee_percentage + fee_fixed - if total_fee > 0: - prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label, amount=int(total_fee))) - else: - # Otherwise, set the fee to 0 to ensure no accidental discounts are applied - total_fee = 0 + prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label, amount=int(self.__get_total_fee(amount)))) + # Create the invoice keyboard inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_pay, pay=True)], [telegram.InlineKeyboardButton(strings.menu_cancel, @@ -650,7 +648,7 @@ class ChatWorker(threading.Thread): # The amount is valid, send the invoice self.bot.send_invoice(self.chat.id, title=strings.payment_invoice_title, - description=strings.payment_invoice_description.format(amount=str(value)), + description=strings.payment_invoice_description.format(amount=str(amount)), payload=self.invoice_payload, provider_token=configloader.config["Credit Card"]["credit_card_token"], start_parameter="tempdeeplink", @@ -660,7 +658,7 @@ class ChatWorker(threading.Thread): need_email=configloader.config["Credit Card"]["email_required"] == "yes", need_phone_number=configloader.config["Credit Card"]["phone_required"] == "yes", reply_markup=inline_keyboard) - # Wait for the invoice + precheckoutquery = self.__wait_for_precheckoutquery(cancellable=True) # Check if the user has cancelled the invoice if isinstance(precheckoutquery, CancelSignal): @@ -672,10 +670,11 @@ class ChatWorker(threading.Thread): successfulpayment = self.__wait_for_successfulpayment() # Create a new database transaction transaction = db.Transaction(user=self.user, - value=successfulpayment.total_amount - int(total_fee), + value=int(successfulpayment.total_amount - self.__get_total_fee(amount=amount)), provider="Credit Card", telegram_charge_id=successfulpayment.telegram_payment_charge_id, provider_charge_id=successfulpayment.provider_payment_charge_id) + if successfulpayment.order_info is not None: transaction.payment_name = successfulpayment.order_info.name transaction.payment_email = successfulpayment.order_info.email @@ -685,6 +684,17 @@ class ChatWorker(threading.Thread): # Commit all the changes self.session.commit() + @staticmethod + def __get_total_fee(amount): + #Calculate a fee for the required amount + fee_percentage = float(configloader.config["Credit Card"]["fee_percentage"]) / 100 + fee_fixed = int(configloader.config["Credit Card"]["fee_fixed"]) + total_fee = amount * fee_percentage + fee_fixed + if total_fee > 0: + return total_fee + # Set the fee to 0 to ensure no accidental discounts are applied + return 0 + def __bot_info(self): """Send information about the bot.""" self.bot.send_message(self.chat.id, strings.bot_info) From 5be2f9e76282c133c02ee0d1d7085e1fa3f3bbcf Mon Sep 17 00:00:00 2001 From: Pavlo Zhuk Date: Sat, 4 Apr 2020 20:53:10 +0300 Subject: [PATCH 2/6] add configurable payment presets --- config/template_config.ini | 3 +++ worker.py | 10 ++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/config/template_config.ini b/config/template_config.ini index cbe2381..149eca7 100644 --- a/config/template_config.ini +++ b/config/template_config.ini @@ -72,6 +72,9 @@ phone_required = yes ; Display the full order information to the customers instead of the shortened version ; The full order information includes the order number and the timestamp of the order placement full_order_info = no +; Payment presets would be suggested to the user as buttons, when making credit card transaction +payment_presets = 10.00,25.00,50.00,100.00 + # Exception reporting settings [Error Reporting] diff --git a/worker.py b/worker.py index bc2fc85..c2716ea 100644 --- a/worker.py +++ b/worker.py @@ -593,11 +593,9 @@ class ChatWorker(threading.Thread): def __add_credit_cc(self): """Add money to the wallet through a credit card payment.""" # Create a keyboard to be sent later - keyboard = [[telegram.KeyboardButton(str(utils.Price("10.00")))], - [telegram.KeyboardButton(str(utils.Price("25.00")))], - [telegram.KeyboardButton(str(utils.Price("50.00")))], - [telegram.KeyboardButton(str(utils.Price("100.00")))], - [telegram.KeyboardButton(strings.menu_cancel)]] + presets = configloader.config["Appearance"]["payment_presets"].split(',') + keyboard = [[telegram.KeyboardButton(str(utils.Price(preset)))] for preset in presets] + keyboard.append([telegram.KeyboardButton(strings.menu_cancel)]); # Boolean variable to check if the user has cancelled the action cancelled = False # Loop used to continue asking if there's an error during the input @@ -630,7 +628,7 @@ class ChatWorker(threading.Thread): else: # Exit the function return - # Issue the invoice + # Issue the payment invoice self.__make_payment(amount=value) def __make_payment(self, amount): From 23f395ee8a2e7d97dd862297427f721167c0e8b3 Mon Sep 17 00:00:00 2001 From: Pavlo Zhuk Date: Sat, 4 Apr 2020 23:37:03 +0300 Subject: [PATCH 3/6] allow refill on insufficient funds during checkout --- config/template_config.ini | 3 ++- worker.py | 16 +++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/config/template_config.ini b/config/template_config.ini index 149eca7..c9e107e 100644 --- a/config/template_config.ini +++ b/config/template_config.ini @@ -74,7 +74,8 @@ phone_required = yes full_order_info = no ; Payment presets would be suggested to the user as buttons, when making credit card transaction payment_presets = 10.00,25.00,50.00,100.00 - +; Allow balance refill during the order checkout in case of unsufficient balance +refill_on_checkout = yes # Exception reporting settings [Error Reporting] diff --git a/worker.py b/worker.py index c2716ea..5d81b93 100644 --- a/worker.py +++ b/worker.py @@ -484,14 +484,20 @@ class ChatWorker(threading.Thread): order_id=order.order_id) self.session.add(order_item) # Ensure the user has enough credit to make the purchase - if self.user.credit - self.__get_cart_value(cart) < 0: + credit_required = self.__get_cart_value(cart) - self.user.credit + # Notify user In case of insufficient credit + if credit_required > 0: self.bot.send_message(self.chat.id, strings.error_not_enough_credit) + # Suggest payment for missing credit value if configuration allows refill + if configloader.config["Appearance"]["refill_on_checkout"] == 'yes': + self.__make_payment(utils.Price(credit_required)) + # If afer requested payment credit is still insufficient (either payment failure or cancel) + if self.user.credit < self.__get_cart_value(cart): # Rollback all the changes self.session.rollback() - return - - # User has credit and valid order, perform transaction now - self.__order_transaction(order=order, value=-int(self.__get_cart_value(cart))) + else: + # User has credit and valid order, perform transaction now + self.__order_transaction(order=order, value=-int(self.__get_cart_value(cart))) @staticmethod def __get_cart_value(cart): From c9561f0da5c15b7f98b2c3f8a0d0e476031a02fa Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 7 Apr 2020 01:30:13 +0200 Subject: [PATCH 4/6] Bump up config file version --- config/template_config.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/template_config.ini b/config/template_config.ini index c9e107e..edc234d 100644 --- a/config/template_config.ini +++ b/config/template_config.ini @@ -5,7 +5,7 @@ # Config file parameters [Config] ; Config file version. DO NOT EDIT THIS! -version = 15 +version = 16 ; Set this to no when you are done editing the file is_template = yes ; Language code for string file From 9df1203b3e6a1c2d5a93c39ceb67cdfc3c5a6f36 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 7 Apr 2020 01:44:23 +0200 Subject: [PATCH 5/6] Made a few changes --- worker.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/worker.py b/worker.py index 5d81b93..bee1423 100644 --- a/worker.py +++ b/worker.py @@ -581,7 +581,8 @@ class ChatWorker(threading.Thread): self.bot.send_message(self.chat.id, strings.conversation_payment_method, reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True)) # Wait for a reply from the user - selection = self.__wait_for_specific_message([strings.menu_cash, strings.menu_credit_card, strings.menu_cancel], cancellable=True) + selection = self.__wait_for_specific_message([strings.menu_cash, strings.menu_credit_card, strings.menu_cancel], + cancellable=True) # If the user has selected the Cash option... if selection == strings.menu_cash: # Go to the pay with cash function @@ -601,7 +602,7 @@ class ChatWorker(threading.Thread): # Create a keyboard to be sent later presets = configloader.config["Appearance"]["payment_presets"].split(',') keyboard = [[telegram.KeyboardButton(str(utils.Price(preset)))] for preset in presets] - keyboard.append([telegram.KeyboardButton(strings.menu_cancel)]); + keyboard.append([telegram.KeyboardButton(strings.menu_cancel)]) # Boolean variable to check if the user has cancelled the action cancelled = False # Loop used to continue asking if there's an error during the input @@ -643,8 +644,10 @@ class ChatWorker(threading.Thread): # Create the price array prices = [telegram.LabeledPrice(label=strings.payment_invoice_label, amount=int(amount))] # If the user has to pay a fee when using the credit card, add it to the prices list - prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label, amount=int(self.__get_total_fee(amount)))) - + fee = int(self.__get_total_fee(amount)) + if fee > 0: + prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label, + amount=fee)) # Create the invoice keyboard inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_pay, pay=True)], [telegram.InlineKeyboardButton(strings.menu_cancel, @@ -662,7 +665,7 @@ class ChatWorker(threading.Thread): need_email=configloader.config["Credit Card"]["email_required"] == "yes", need_phone_number=configloader.config["Credit Card"]["phone_required"] == "yes", reply_markup=inline_keyboard) - + # Wait for the precheckout query precheckoutquery = self.__wait_for_precheckoutquery(cancellable=True) # Check if the user has cancelled the invoice if isinstance(precheckoutquery, CancelSignal): @@ -674,7 +677,7 @@ class ChatWorker(threading.Thread): successfulpayment = self.__wait_for_successfulpayment() # Create a new database transaction transaction = db.Transaction(user=self.user, - value=int(successfulpayment.total_amount - self.__get_total_fee(amount=amount)), + value=int(successfulpayment.total_amount) - fee, provider="Credit Card", telegram_charge_id=successfulpayment.telegram_payment_charge_id, provider_charge_id=successfulpayment.provider_payment_charge_id) @@ -690,7 +693,7 @@ class ChatWorker(threading.Thread): @staticmethod def __get_total_fee(amount): - #Calculate a fee for the required amount + # Calculate a fee for the required amount fee_percentage = float(configloader.config["Credit Card"]["fee_percentage"]) / 100 fee_fixed = int(configloader.config["Credit Card"]["fee_fixed"]) total_fee = amount * fee_percentage + fee_fixed From 9d6e21fb4ee22c9f45654d5d393a6a3265a0971e Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 7 Apr 2020 02:02:07 +0200 Subject: [PATCH 6/6] Move the presets to the [Credit Card] section --- config/template_config.ini | 5 +++-- worker.py | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/config/template_config.ini b/config/template_config.ini index edc234d..2b95dd1 100644 --- a/config/template_config.ini +++ b/config/template_config.ini @@ -53,6 +53,9 @@ credit_card_token = 123456789:YOUR_TOKEN_HERE_ min_amount = 1000 ; Maximum wallet payment accepted (in miniumum currency units, $1.00 = 100 units) max_amount = 10000 +; The preset selections that can be made when adding credit to the wallet with a credit card +; Presets are pipe-separated |, and should never be outside the bounds provided by the min_amount and max_amount options +payment_presets = 10.00 | 25.00 | 50.00 | 100.00 ; Make the user pay a extra fee when adding credit to the wallet with a credit card ; The formula for determining the total cost is: ; cost = added_funds + added_funds * fee_percentage / 100 + fee_fixed @@ -72,8 +75,6 @@ phone_required = yes ; Display the full order information to the customers instead of the shortened version ; The full order information includes the order number and the timestamp of the order placement full_order_info = no -; Payment presets would be suggested to the user as buttons, when making credit card transaction -payment_presets = 10.00,25.00,50.00,100.00 ; Allow balance refill during the order checkout in case of unsufficient balance refill_on_checkout = yes diff --git a/worker.py b/worker.py index bee1423..c4c6496 100644 --- a/worker.py +++ b/worker.py @@ -600,7 +600,7 @@ class ChatWorker(threading.Thread): def __add_credit_cc(self): """Add money to the wallet through a credit card payment.""" # Create a keyboard to be sent later - presets = configloader.config["Appearance"]["payment_presets"].split(',') + presets = list(map(lambda s: s.strip(" "), configloader.config["Credit Card"]["payment_presets"].split('|'))) keyboard = [[telegram.KeyboardButton(str(utils.Price(preset)))] for preset in presets] keyboard.append([telegram.KeyboardButton(strings.menu_cancel)]) # Boolean variable to check if the user has cancelled the action @@ -623,12 +623,14 @@ class ChatWorker(threading.Thread): if value > utils.Price(int(configloader.config["Credit Card"]["max_amount"])): self.bot.send_message(self.chat.id, strings.error_payment_amount_over_max.format( - max_amount=utils.Price(configloader.config["Payments"]["max_amount"]))) + max_amount=utils.Price(configloader.config["Credit Card"]["max_amount"])) + ) continue elif value < utils.Price(int(configloader.config["Credit Card"]["min_amount"])): self.bot.send_message(self.chat.id, strings.error_payment_amount_under_min.format( - min_amount=utils.Price(configloader.config["Payments"]["min_amount"]))) + min_amount=utils.Price(configloader.config["Credit Card"]["min_amount"])) + ) continue break # If the user cancelled the action...