Module jumpscale.sals.billing.billing
Expand source code
from .models import PAYMENT_FACTORY, REFUND_FACTORY
import uuid, datetime
import gevent
from jumpscale.loader import j
from jumpscale.clients.stellar import TRANSACTION_FEES
class BillingManager:
def submit_payment(self, amount, wallet_name, refund_extra=True, expiry=5, description=""):
"""Submit payment.
Args:
amount (float): amount of current payment.
wallet_name (str): name of wallet that submit the payment.
refund_extra (bool, optional): if the payment has extra amount it will be refunded. Defaults to True.
expiry (int, optional): time in minutes the payment will expire after it. Defaults to 5.
description (str, optional): describe the payment. Defaults to "".
Returns:
tuple(str, str): payment_id and memo_text that used as confirmation for the payment.
"""
payment_id = uuid.uuid4().hex
instance_name = f"payment_{payment_id}"
payment = PAYMENT_FACTORY.new(
instance_name,
payment_id=payment_id,
amount=round(amount, 6),
wallet_name=wallet_name,
refund_extra=refund_extra,
description=description,
)
payment.deadline = datetime.datetime.utcnow() + datetime.timedelta(minutes=expiry)
payment.save()
j.logger.info(
f"payment {payment_id} submitted for wallet: {wallet_name} amount: {amount}, description: {description}, expiry: {expiry}, memo_text: {payment.memo_text}"
)
return payment_id, payment.memo_text
def wait_payment(self, payment_id, bot=None, notes=None):
"""Wait payment amount of time.
Args:
payment_id (str): payment id to wait on.
bot : used in case of using it with threebot deployer or VDC chatflow. Defaults to None.
notes (str, optional): optional note that appears while waiting. Defaults to None.
Returns:
bool: return result of this payment, True if payment finished, False if payment expired.
"""
j.logger.info(f"waiting payment: {payment_id}")
payment = PAYMENT_FACTORY.find_by_id(payment_id)
if bot:
self._show_payment(bot, payment, notes)
while not payment.is_finished():
gevent.sleep(3)
payment = PAYMENT_FACTORY.find_by_id(payment_id)
j.logger.info(f"payment: {payment_id} result {payment.result.success}")
return payment.result.success
def _show_payment(self, bot, payment_obj, notes=None):
notes = notes or []
qr_code = (
f"TFT:{payment_obj.wallet.address}?amount={payment_obj.amount}&message={payment_obj.memo_text}&sender=me"
)
notes_text = "\n".join([f"<h4>Note: {note}</h4>" for note in notes])
qr_encoded = j.tools.qrcode.base64_get(qr_code, scale=2)
msg_text = f"""Please scan the QR Code below (Using ThreeFold Connect Application) for the payment details
<div class="text-center">
<img style="border:1px dashed #85929E" src="data:image/png;base64,{qr_encoded}"/>
</div>
<h4> Destination Wallet Address: </h4> {payment_obj.wallet.address} \n
<h4> Currency: </h4> TFT \n
<h4> Memo Text (Message): </h4> {payment_obj.memo_text} \n
<h4> Total Amount: </h4> {payment_obj.amount} TFT \n
{notes_text}
<h5>When using manual payment please note that inserting the memo-text is an important way to identify a transaction recipient beyond a wallet address. Failure to do so will result in a failed payment. Please also keep in mind that an additional Transaction fee of {TRANSACTION_FEES} TFT will automatically occur per transaction.</h5>
"""
bot.md_show_update(msg_text, html=True)
def refund_failed_payments(self):
"""Refund any failed payment.
Example: transfer amount less than payment amount.
"""
for payment in PAYMENT_FACTORY.list_failed_payments():
refund_result = True
for transaction in payment.result.transactions:
if transaction.success or transaction.transaction_refund.success:
continue
j.logger.info(f"refunding transaction: {transaction.transaction_hash} of payment: {payment.payment_id}")
refund_result = refund_result and transaction.refund(payment.wallet)
payment.save()
def issue_refund(self, payment_id, amount=-1):
"""Issue Refund.
It is used if refund needed even after successfull payment.
Args:
payment_id (str): payment id of the payment needs to be refunded.
amount (int, optional): amount of the refund value. Defaults to -1(Meaning refund the total amount of the payment).
"""
instance_name = f"refund_{payment_id}"
request = REFUND_FACTORY.new(instance_name, payment_id=payment_id, amount=amount)
request.save()
j.logger.info(f"refund request created for payment: {payment_id}")
return
def check_refund(self, payment_id):
"""Check refund request status.
Args:
payment_id (str): payment id of the payment needs to check refund status.
Raises:
j.exceptions.Input: raise error if no refund request belong to the payment.
Returns:
bool: return the state of the refund request, True if refund success.
"""
instance_name = f"refund_{payment_id}"
request = REFUND_FACTORY.find(instance_name)
if not request:
raise j.exceptions.Input(f"not refunds were issues for payment {payment_id}")
return request.success
def process_refunds(self):
"""Process any active refund.
list all active refund and apply it.
"""
for request in REFUND_FACTORY.list_active_requests():
j.logger.info(f"applying active refund for payment: {request.payment_id}")
request.apply()
def process_payments(self):
"""Process any active payment.
list all active payment and apply it.
"""
for payment in PAYMENT_FACTORY.list_active_payments():
j.logger.info(f"updating active payment: {payment.payment_id}")
payment.update_status()
def refund_extra(self):
"""Process any active extra refund.
list all active extra refund and apply it.
"""
for payment in PAYMENT_FACTORY.list_extra_paid_payments():
j.logger.info(f"refund extra paid for payment: {payment.payment_id}")
payment.result.refund_extra()
Classes
class BillingManager
-
Expand source code
class BillingManager: def submit_payment(self, amount, wallet_name, refund_extra=True, expiry=5, description=""): """Submit payment. Args: amount (float): amount of current payment. wallet_name (str): name of wallet that submit the payment. refund_extra (bool, optional): if the payment has extra amount it will be refunded. Defaults to True. expiry (int, optional): time in minutes the payment will expire after it. Defaults to 5. description (str, optional): describe the payment. Defaults to "". Returns: tuple(str, str): payment_id and memo_text that used as confirmation for the payment. """ payment_id = uuid.uuid4().hex instance_name = f"payment_{payment_id}" payment = PAYMENT_FACTORY.new( instance_name, payment_id=payment_id, amount=round(amount, 6), wallet_name=wallet_name, refund_extra=refund_extra, description=description, ) payment.deadline = datetime.datetime.utcnow() + datetime.timedelta(minutes=expiry) payment.save() j.logger.info( f"payment {payment_id} submitted for wallet: {wallet_name} amount: {amount}, description: {description}, expiry: {expiry}, memo_text: {payment.memo_text}" ) return payment_id, payment.memo_text def wait_payment(self, payment_id, bot=None, notes=None): """Wait payment amount of time. Args: payment_id (str): payment id to wait on. bot : used in case of using it with threebot deployer or VDC chatflow. Defaults to None. notes (str, optional): optional note that appears while waiting. Defaults to None. Returns: bool: return result of this payment, True if payment finished, False if payment expired. """ j.logger.info(f"waiting payment: {payment_id}") payment = PAYMENT_FACTORY.find_by_id(payment_id) if bot: self._show_payment(bot, payment, notes) while not payment.is_finished(): gevent.sleep(3) payment = PAYMENT_FACTORY.find_by_id(payment_id) j.logger.info(f"payment: {payment_id} result {payment.result.success}") return payment.result.success def _show_payment(self, bot, payment_obj, notes=None): notes = notes or [] qr_code = ( f"TFT:{payment_obj.wallet.address}?amount={payment_obj.amount}&message={payment_obj.memo_text}&sender=me" ) notes_text = "\n".join([f"<h4>Note: {note}</h4>" for note in notes]) qr_encoded = j.tools.qrcode.base64_get(qr_code, scale=2) msg_text = f"""Please scan the QR Code below (Using ThreeFold Connect Application) for the payment details <div class="text-center"> <img style="border:1px dashed #85929E" src="data:image/png;base64,{qr_encoded}"/> </div> <h4> Destination Wallet Address: </h4> {payment_obj.wallet.address} \n <h4> Currency: </h4> TFT \n <h4> Memo Text (Message): </h4> {payment_obj.memo_text} \n <h4> Total Amount: </h4> {payment_obj.amount} TFT \n {notes_text} <h5>When using manual payment please note that inserting the memo-text is an important way to identify a transaction recipient beyond a wallet address. Failure to do so will result in a failed payment. Please also keep in mind that an additional Transaction fee of {TRANSACTION_FEES} TFT will automatically occur per transaction.</h5> """ bot.md_show_update(msg_text, html=True) def refund_failed_payments(self): """Refund any failed payment. Example: transfer amount less than payment amount. """ for payment in PAYMENT_FACTORY.list_failed_payments(): refund_result = True for transaction in payment.result.transactions: if transaction.success or transaction.transaction_refund.success: continue j.logger.info(f"refunding transaction: {transaction.transaction_hash} of payment: {payment.payment_id}") refund_result = refund_result and transaction.refund(payment.wallet) payment.save() def issue_refund(self, payment_id, amount=-1): """Issue Refund. It is used if refund needed even after successfull payment. Args: payment_id (str): payment id of the payment needs to be refunded. amount (int, optional): amount of the refund value. Defaults to -1(Meaning refund the total amount of the payment). """ instance_name = f"refund_{payment_id}" request = REFUND_FACTORY.new(instance_name, payment_id=payment_id, amount=amount) request.save() j.logger.info(f"refund request created for payment: {payment_id}") return def check_refund(self, payment_id): """Check refund request status. Args: payment_id (str): payment id of the payment needs to check refund status. Raises: j.exceptions.Input: raise error if no refund request belong to the payment. Returns: bool: return the state of the refund request, True if refund success. """ instance_name = f"refund_{payment_id}" request = REFUND_FACTORY.find(instance_name) if not request: raise j.exceptions.Input(f"not refunds were issues for payment {payment_id}") return request.success def process_refunds(self): """Process any active refund. list all active refund and apply it. """ for request in REFUND_FACTORY.list_active_requests(): j.logger.info(f"applying active refund for payment: {request.payment_id}") request.apply() def process_payments(self): """Process any active payment. list all active payment and apply it. """ for payment in PAYMENT_FACTORY.list_active_payments(): j.logger.info(f"updating active payment: {payment.payment_id}") payment.update_status() def refund_extra(self): """Process any active extra refund. list all active extra refund and apply it. """ for payment in PAYMENT_FACTORY.list_extra_paid_payments(): j.logger.info(f"refund extra paid for payment: {payment.payment_id}") payment.result.refund_extra()
Methods
def check_refund(self, payment_id)
-
Check refund request status.
Args
payment_id
:str
- payment id of the payment needs to check refund status.
Raises
j.exceptions.Input
- raise error if no refund request belong to the payment.
Returns
bool
- return the state of the refund request, True if refund success.
Expand source code
def check_refund(self, payment_id): """Check refund request status. Args: payment_id (str): payment id of the payment needs to check refund status. Raises: j.exceptions.Input: raise error if no refund request belong to the payment. Returns: bool: return the state of the refund request, True if refund success. """ instance_name = f"refund_{payment_id}" request = REFUND_FACTORY.find(instance_name) if not request: raise j.exceptions.Input(f"not refunds were issues for payment {payment_id}") return request.success
def issue_refund(self, payment_id, amount=-1)
-
Issue Refund. It is used if refund needed even after successfull payment.
Args
payment_id
:str
- payment id of the payment needs to be refunded.
amount
:int
, optional- amount of the refund value. Defaults to -1(Meaning refund the total amount of the payment).
Expand source code
def issue_refund(self, payment_id, amount=-1): """Issue Refund. It is used if refund needed even after successfull payment. Args: payment_id (str): payment id of the payment needs to be refunded. amount (int, optional): amount of the refund value. Defaults to -1(Meaning refund the total amount of the payment). """ instance_name = f"refund_{payment_id}" request = REFUND_FACTORY.new(instance_name, payment_id=payment_id, amount=amount) request.save() j.logger.info(f"refund request created for payment: {payment_id}") return
def process_payments(self)
-
Process any active payment. list all active payment and apply it.
Expand source code
def process_payments(self): """Process any active payment. list all active payment and apply it. """ for payment in PAYMENT_FACTORY.list_active_payments(): j.logger.info(f"updating active payment: {payment.payment_id}") payment.update_status()
def process_refunds(self)
-
Process any active refund. list all active refund and apply it.
Expand source code
def process_refunds(self): """Process any active refund. list all active refund and apply it. """ for request in REFUND_FACTORY.list_active_requests(): j.logger.info(f"applying active refund for payment: {request.payment_id}") request.apply()
def refund_extra(self)
-
Process any active extra refund. list all active extra refund and apply it.
Expand source code
def refund_extra(self): """Process any active extra refund. list all active extra refund and apply it. """ for payment in PAYMENT_FACTORY.list_extra_paid_payments(): j.logger.info(f"refund extra paid for payment: {payment.payment_id}") payment.result.refund_extra()
def refund_failed_payments(self)
-
Refund any failed payment. Example: transfer amount less than payment amount.
Expand source code
def refund_failed_payments(self): """Refund any failed payment. Example: transfer amount less than payment amount. """ for payment in PAYMENT_FACTORY.list_failed_payments(): refund_result = True for transaction in payment.result.transactions: if transaction.success or transaction.transaction_refund.success: continue j.logger.info(f"refunding transaction: {transaction.transaction_hash} of payment: {payment.payment_id}") refund_result = refund_result and transaction.refund(payment.wallet) payment.save()
def submit_payment(self, amount, wallet_name, refund_extra=True, expiry=5, description='')
-
Submit payment.
Args
amount
:float
- amount of current payment.
wallet_name
:str
- name of wallet that submit the payment.
refund_extra
:bool
, optional- if the payment has extra amount it will be refunded. Defaults to True.
expiry
:int
, optional- time in minutes the payment will expire after it. Defaults to 5.
description
:str
, optional- describe the payment. Defaults to "".
Returns
tuple(str, str): payment_id and memo_text that used as confirmation for the payment.
Expand source code
def submit_payment(self, amount, wallet_name, refund_extra=True, expiry=5, description=""): """Submit payment. Args: amount (float): amount of current payment. wallet_name (str): name of wallet that submit the payment. refund_extra (bool, optional): if the payment has extra amount it will be refunded. Defaults to True. expiry (int, optional): time in minutes the payment will expire after it. Defaults to 5. description (str, optional): describe the payment. Defaults to "". Returns: tuple(str, str): payment_id and memo_text that used as confirmation for the payment. """ payment_id = uuid.uuid4().hex instance_name = f"payment_{payment_id}" payment = PAYMENT_FACTORY.new( instance_name, payment_id=payment_id, amount=round(amount, 6), wallet_name=wallet_name, refund_extra=refund_extra, description=description, ) payment.deadline = datetime.datetime.utcnow() + datetime.timedelta(minutes=expiry) payment.save() j.logger.info( f"payment {payment_id} submitted for wallet: {wallet_name} amount: {amount}, description: {description}, expiry: {expiry}, memo_text: {payment.memo_text}" ) return payment_id, payment.memo_text
def wait_payment(self, payment_id, bot=None, notes=None)
-
Wait payment amount of time.
Args
payment_id
:str
- payment id to wait on.
- bot : used in case of using it with threebot deployer or VDC chatflow. Defaults to None.
notes
:str
, optional- optional note that appears while waiting. Defaults to None.
Returns
bool
- return result of this payment, True if payment finished, False if payment expired.
Expand source code
def wait_payment(self, payment_id, bot=None, notes=None): """Wait payment amount of time. Args: payment_id (str): payment id to wait on. bot : used in case of using it with threebot deployer or VDC chatflow. Defaults to None. notes (str, optional): optional note that appears while waiting. Defaults to None. Returns: bool: return result of this payment, True if payment finished, False if payment expired. """ j.logger.info(f"waiting payment: {payment_id}") payment = PAYMENT_FACTORY.find_by_id(payment_id) if bot: self._show_payment(bot, payment, notes) while not payment.is_finished(): gevent.sleep(3) payment = PAYMENT_FACTORY.find_by_id(payment_id) j.logger.info(f"payment: {payment_id} result {payment.result.success}") return payment.result.success