Module jumpscale.sals.billing.models

Expand source code
from jumpscale.core.base import Base, fields, StoredFactory
import datetime
from jumpscale.loader import j
from jumpscale.clients.stellar import TRANSACTION_FEES
from decimal import Decimal
import datetime


class PaymentTransactionRefund(Base):
    refund_transaction_hash = fields.String()
    success = fields.Boolean(default=False)


class PaymentTransaction(Base):
    transaction_hash = fields.String(required=True)
    transaction_refund = fields.Object(PaymentTransactionRefund)
    success = fields.Boolean(default=False)

    def refund(self, wallet):
        if self.transaction_refund.success:
            return True
        try:
            amount = round(self.get_amount(wallet) - Decimal(TRANSACTION_FEES), 6)
            if amount < 0:
                self.transaction_refund.success = True
            else:
                a = wallet._get_asset()
                sender_address = wallet.get_sender_wallet_address(self.transaction_hash)
                j.logger.info(
                    f"refunding transaction: {self.transaction_hash} with amount: {amount} to address: {sender_address}"
                )
                self.transaction_refund.transaction_hash = wallet.transfer(
                    sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
                )
                self.transaction_refund.success = True
                j.logger.info(
                    f"transaction: {self.transaction_hash} refunded successfully with amount: {amount} to address: {sender_address} in transaction: {self.transaction_refund.transaction_hash}"
                )
        except Exception as e:
            j.logger.critical(f"failed to refund transaction: {self.transaction_hash} due to error: {str(e)}")
        return self.transaction_refund.success

    def get_amount(self, wallet):
        try:
            effects = wallet.get_transaction_effects(self.transaction_hash)
        except Exception as e:
            j.logger.warning(f"failed to get transaction effects of hash {self.transaction_hash} due to error {str(e)}")
            raise e
        trans_amount = 0
        for effect in effects:
            if effect.asset_code != "TFT":
                continue
            trans_amount += effect.amount
        return trans_amount


class PaymentResult(Base):
    success = fields.Boolean(default=False)
    extra_paid = fields.Boolean(default=False)
    transactions = fields.List(fields.Object(PaymentTransaction))

    def refund_extra(self):
        if self.extra_paid and self.parent.refund_extra:
            for transaction in self.transactions:
                if transaction.success:
                    trans_amount = transaction.get_amount(self.parent.wallet)
                    diff = float(trans_amount) - self.parent.amount
                    if diff <= TRANSACTION_FEES:
                        self.extra_paid = False
                        break
                    sender_address = self.parent.wallet.get_sender_wallet_address(transaction.transaction_hash)
                    amount = round(diff - TRANSACTION_FEES, 6)
                    try:
                        j.logger.info(
                            f"refunding extra amount: {amount} of transaction {transaction.transaction_hash} to address: {sender_address}"
                        )
                        a = self.parent.wallet._get_asset()
                        refund_hash = self.parent.wallet.transfer(
                            sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
                        )
                        self.extra_paid = False
                        j.logger.info(
                            f"extra amount: {amount} of transaction {transaction.transaction_hash} refunded successfully in transaction: {refund_hash} to address: {sender_address}"
                        )
                    except Exception as e:
                        j.logger.critical(
                            f"failed to refund extra amount {amount} for payment: {self.parent.payment_id} due to error: {str(e)}"
                        )
            self.parent.save()
        return self.extra_paid


class Payment(Base):
    payment_id = fields.String()
    wallet_name = fields.String(required=True)
    amount = fields.Float(required=True)
    memo_text = fields.String(default=lambda: j.data.idgenerator.chars(28))
    created_at = fields.DateTime(default=datetime.datetime.utcnow)
    deadline = fields.DateTime(default=lambda: datetime.datetime.utcnow() + datetime.timedelta(minutes=5))
    result = fields.Object(PaymentResult, required=True)
    refund_extra = fields.Boolean(default=True)
    description = fields.String()

    def is_finished(self):
        if self.deadline.timestamp() < j.data.time.utcnow().timestamp or self.result.success:
            return True

        return False

    @property
    def wallet(self):
        return j.clients.stellar.get(self.wallet_name)

    def update_status(self):
        if self.is_finished():
            return
        if self.amount == 0:
            self.result.success = True
            self.save()
            return
        j.logger.info(f"updating payment: {self.payment_id} status")
        transactions = self.wallet.list_transactions()
        current_transactions = {t.transaction_hash: t for t in self.result.transactions}
        for transaction in transactions:
            transaction_hash = transaction.hash
            if transaction_hash in current_transactions:
                continue
            trans_memo_text = transaction.memo_text
            if not trans_memo_text:
                continue

            if trans_memo_text != self.memo_text:
                continue

            j.logger.info(f"adding transaction {transaction_hash} to payment: {self.payment_id}")

            trans_obj = PaymentTransaction()
            trans_obj.transaction_hash = transaction_hash
            self.result.transactions.append(trans_obj)

            if not self.result.success:
                try:
                    trans_amount = trans_obj.get_amount(self.wallet)
                    j.logger.info(
                        f"adding transaction {transaction_hash} to payment: {self.payment_id} with amount: {trans_amount}"
                    )
                except Exception as e:
                    j.logger.error(
                        f"failed to update payment {self.instance_name} with transaction {transaction_hash} due to error {str(e)}"
                    )
                    continue
                if trans_amount >= Decimal(self.amount) or abs(trans_amount - Decimal(self.amount)) <= 0.000001:
                    j.logger.info(
                        f"payment: {self.payment_id} fulfilled by transaction: {transaction_hash} with amount: {trans_amount}"
                    )
                    trans_obj.success = True
                    self.result.success = True
                    if trans_amount > Decimal(self.amount):
                        j.logger.info(f"payment: {self.payment_id} is marked as extra paid")
                        self.result.extra_paid = True
            self.save()


class PaymentFactory(StoredFactory):
    def find_by_id(self, payment_id):
        instance_name = f"payment_{payment_id}"
        return self.find(instance_name)

    def list_failed_payments(self):
        for name in self.list_all():
            payment = self.find(name)
            payment.update_status()
            if payment.is_finished():
                if not payment.result.success:
                    yield payment
                else:
                    for transaction in payment.result.transactions:
                        if not transaction.success and not transaction.transaction_refund.success:
                            yield payment
                            break

    def list_active_payments(self):
        for name in self.list_all():
            payment = self.find(name)
            if not payment.is_finished():
                yield payment

    def list_extra_paid_payments(self):
        _, _, payments = self.find_many(refund_extra=True)
        for payment in payments:
            if payment.result.extra_paid and payment.is_finished():
                yield payment


PAYMENT_FACTORY = PaymentFactory(Payment)
PAYMENT_FACTORY.always_reload = True


class RefundRequest(Base):
    payment_id = fields.String(required=True)
    success = fields.Boolean(default=False)
    refund_transaction_hash = fields.String()
    last_tried = fields.DateTime()
    amount = fields.Float(default=-1)

    def apply(self):
        payment = PAYMENT_FACTORY.find_by_id(self.payment_id)
        if not payment.is_finished():
            j.logger.warning(f"can't refund active payment {self.payment_id}")
            return False

        self.last_tried = datetime.datetime.utcnow()
        amount = payment.amount
        # check if refund extra is False. then amount should be same as successful transaction in case of extra was paid but not refunded automatically
        sender_address = None
        for transaction in payment.result.transactions:
            if transaction.success:
                sender_address = payment.wallet.get_sender_wallet_address(transaction.transaction_hash)
                if not payment.refund_extra:
                    amount = float(transaction.get_amount(payment.wallet))

        # if a specific amount was specified by the refund request
        if self.amount > 0:
            amount = self.amount

        if amount <= TRANSACTION_FEES or not sender_address:
            self.success = True
        else:
            try:
                a = payment.wallet._get_asset()
                self.refund_transaction_hash = payment.wallet.transfer(
                    sender_address, amount=round(amount - TRANSACTION_FEES, 6), asset=f"{a.code}:{a.issuer}"
                )
                self.success = True
                j.logger.info(
                    f"refund request successful for payment: {self.payment_id} amount: {amount} to address: {sender_address} in transaction: {self.refund_transaction_hash}"
                )
            except Exception as e:
                j.logger.critical(f"failed to apply refund request for payment {self.payment_id} due to error {str(e)}")
        self.save()
        return self.success


class RefundFactory(StoredFactory):
    def list_active_requests(self):
        _, _, refunds = self.find_many(success=False)
        return refunds


REFUND_FACTORY = RefundFactory(RefundRequest)
REFUND_FACTORY.always_reload = True

Classes

class Payment (parent_=None, instance_name_=None, **values)

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

class Person(Base):
    name = fields.String()
    age = fields.Float()

p = Person(name="ahmed", age="19")
print(p.name, p.age)

Args

parent_ : Base, optional
parent instance. Defaults to None.
instance_name_ : str, optional
instance name. Defaults to None.
**values
any given field values to initiate the instance with
Expand source code
class Payment(Base):
    payment_id = fields.String()
    wallet_name = fields.String(required=True)
    amount = fields.Float(required=True)
    memo_text = fields.String(default=lambda: j.data.idgenerator.chars(28))
    created_at = fields.DateTime(default=datetime.datetime.utcnow)
    deadline = fields.DateTime(default=lambda: datetime.datetime.utcnow() + datetime.timedelta(minutes=5))
    result = fields.Object(PaymentResult, required=True)
    refund_extra = fields.Boolean(default=True)
    description = fields.String()

    def is_finished(self):
        if self.deadline.timestamp() < j.data.time.utcnow().timestamp or self.result.success:
            return True

        return False

    @property
    def wallet(self):
        return j.clients.stellar.get(self.wallet_name)

    def update_status(self):
        if self.is_finished():
            return
        if self.amount == 0:
            self.result.success = True
            self.save()
            return
        j.logger.info(f"updating payment: {self.payment_id} status")
        transactions = self.wallet.list_transactions()
        current_transactions = {t.transaction_hash: t for t in self.result.transactions}
        for transaction in transactions:
            transaction_hash = transaction.hash
            if transaction_hash in current_transactions:
                continue
            trans_memo_text = transaction.memo_text
            if not trans_memo_text:
                continue

            if trans_memo_text != self.memo_text:
                continue

            j.logger.info(f"adding transaction {transaction_hash} to payment: {self.payment_id}")

            trans_obj = PaymentTransaction()
            trans_obj.transaction_hash = transaction_hash
            self.result.transactions.append(trans_obj)

            if not self.result.success:
                try:
                    trans_amount = trans_obj.get_amount(self.wallet)
                    j.logger.info(
                        f"adding transaction {transaction_hash} to payment: {self.payment_id} with amount: {trans_amount}"
                    )
                except Exception as e:
                    j.logger.error(
                        f"failed to update payment {self.instance_name} with transaction {transaction_hash} due to error {str(e)}"
                    )
                    continue
                if trans_amount >= Decimal(self.amount) or abs(trans_amount - Decimal(self.amount)) <= 0.000001:
                    j.logger.info(
                        f"payment: {self.payment_id} fulfilled by transaction: {transaction_hash} with amount: {trans_amount}"
                    )
                    trans_obj.success = True
                    self.result.success = True
                    if trans_amount > Decimal(self.amount):
                        j.logger.info(f"payment: {self.payment_id} is marked as extra paid")
                        self.result.extra_paid = True
            self.save()

Ancestors

  • Base
  • types.SimpleNamespace

Instance variables

var amount

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var created_at

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var deadline

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var description

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var memo_text

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var payment_id

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var refund_extra

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var result

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var wallet
Expand source code
@property
def wallet(self):
    return j.clients.stellar.get(self.wallet_name)
var wallet_name

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)

Methods

def is_finished(self)
Expand source code
def is_finished(self):
    if self.deadline.timestamp() < j.data.time.utcnow().timestamp or self.result.success:
        return True

    return False
def update_status(self)
Expand source code
def update_status(self):
    if self.is_finished():
        return
    if self.amount == 0:
        self.result.success = True
        self.save()
        return
    j.logger.info(f"updating payment: {self.payment_id} status")
    transactions = self.wallet.list_transactions()
    current_transactions = {t.transaction_hash: t for t in self.result.transactions}
    for transaction in transactions:
        transaction_hash = transaction.hash
        if transaction_hash in current_transactions:
            continue
        trans_memo_text = transaction.memo_text
        if not trans_memo_text:
            continue

        if trans_memo_text != self.memo_text:
            continue

        j.logger.info(f"adding transaction {transaction_hash} to payment: {self.payment_id}")

        trans_obj = PaymentTransaction()
        trans_obj.transaction_hash = transaction_hash
        self.result.transactions.append(trans_obj)

        if not self.result.success:
            try:
                trans_amount = trans_obj.get_amount(self.wallet)
                j.logger.info(
                    f"adding transaction {transaction_hash} to payment: {self.payment_id} with amount: {trans_amount}"
                )
            except Exception as e:
                j.logger.error(
                    f"failed to update payment {self.instance_name} with transaction {transaction_hash} due to error {str(e)}"
                )
                continue
            if trans_amount >= Decimal(self.amount) or abs(trans_amount - Decimal(self.amount)) <= 0.000001:
                j.logger.info(
                    f"payment: {self.payment_id} fulfilled by transaction: {transaction_hash} with amount: {trans_amount}"
                )
                trans_obj.success = True
                self.result.success = True
                if trans_amount > Decimal(self.amount):
                    j.logger.info(f"payment: {self.payment_id} is marked as extra paid")
                    self.result.extra_paid = True
        self.save()

Inherited members

class PaymentFactory (type_, name_=None, parent_instance_=None, parent_factory_=None)

Stored factories are a custom type of Factory, which uses current configured store backend to store all instance configurations.

get a new stored factory given the type to create and store instances for.

Any factory can have a name, parent Base instance and a parent factory.

Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

Args

type_ : Base
Base class type
name_ : str, optional
factory name. Defaults to None.
parent_instance_ : Base, optional
a parent Base instance. Defaults to None.
parent_factory_ : Factory, optional
a parent Factory. Defaults to None.
Expand source code
class PaymentFactory(StoredFactory):
    def find_by_id(self, payment_id):
        instance_name = f"payment_{payment_id}"
        return self.find(instance_name)

    def list_failed_payments(self):
        for name in self.list_all():
            payment = self.find(name)
            payment.update_status()
            if payment.is_finished():
                if not payment.result.success:
                    yield payment
                else:
                    for transaction in payment.result.transactions:
                        if not transaction.success and not transaction.transaction_refund.success:
                            yield payment
                            break

    def list_active_payments(self):
        for name in self.list_all():
            payment = self.find(name)
            if not payment.is_finished():
                yield payment

    def list_extra_paid_payments(self):
        _, _, payments = self.find_many(refund_extra=True)
        for payment in payments:
            if payment.result.extra_paid and payment.is_finished():
                yield payment

Ancestors

Subclasses

  • jumpscale.core.base.factory.PaymentFactory

Methods

def find_by_id(self, payment_id)
Expand source code
def find_by_id(self, payment_id):
    instance_name = f"payment_{payment_id}"
    return self.find(instance_name)
def list_active_payments(self)
Expand source code
def list_active_payments(self):
    for name in self.list_all():
        payment = self.find(name)
        if not payment.is_finished():
            yield payment
def list_extra_paid_payments(self)
Expand source code
def list_extra_paid_payments(self):
    _, _, payments = self.find_many(refund_extra=True)
    for payment in payments:
        if payment.result.extra_paid and payment.is_finished():
            yield payment
def list_failed_payments(self)
Expand source code
def list_failed_payments(self):
    for name in self.list_all():
        payment = self.find(name)
        payment.update_status()
        if payment.is_finished():
            if not payment.result.success:
                yield payment
            else:
                for transaction in payment.result.transactions:
                    if not transaction.success and not transaction.transaction_refund.success:
                        yield payment
                        break

Inherited members

class PaymentResult (parent_=None, instance_name_=None, **values)

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

class Person(Base):
    name = fields.String()
    age = fields.Float()

p = Person(name="ahmed", age="19")
print(p.name, p.age)

Args

parent_ : Base, optional
parent instance. Defaults to None.
instance_name_ : str, optional
instance name. Defaults to None.
**values
any given field values to initiate the instance with
Expand source code
class PaymentResult(Base):
    success = fields.Boolean(default=False)
    extra_paid = fields.Boolean(default=False)
    transactions = fields.List(fields.Object(PaymentTransaction))

    def refund_extra(self):
        if self.extra_paid and self.parent.refund_extra:
            for transaction in self.transactions:
                if transaction.success:
                    trans_amount = transaction.get_amount(self.parent.wallet)
                    diff = float(trans_amount) - self.parent.amount
                    if diff <= TRANSACTION_FEES:
                        self.extra_paid = False
                        break
                    sender_address = self.parent.wallet.get_sender_wallet_address(transaction.transaction_hash)
                    amount = round(diff - TRANSACTION_FEES, 6)
                    try:
                        j.logger.info(
                            f"refunding extra amount: {amount} of transaction {transaction.transaction_hash} to address: {sender_address}"
                        )
                        a = self.parent.wallet._get_asset()
                        refund_hash = self.parent.wallet.transfer(
                            sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
                        )
                        self.extra_paid = False
                        j.logger.info(
                            f"extra amount: {amount} of transaction {transaction.transaction_hash} refunded successfully in transaction: {refund_hash} to address: {sender_address}"
                        )
                    except Exception as e:
                        j.logger.critical(
                            f"failed to refund extra amount {amount} for payment: {self.parent.payment_id} due to error: {str(e)}"
                        )
            self.parent.save()
        return self.extra_paid

Ancestors

  • Base
  • types.SimpleNamespace

Instance variables

var extra_paid

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var success

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var transactions

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)

Methods

def refund_extra(self)
Expand source code
def refund_extra(self):
    if self.extra_paid and self.parent.refund_extra:
        for transaction in self.transactions:
            if transaction.success:
                trans_amount = transaction.get_amount(self.parent.wallet)
                diff = float(trans_amount) - self.parent.amount
                if diff <= TRANSACTION_FEES:
                    self.extra_paid = False
                    break
                sender_address = self.parent.wallet.get_sender_wallet_address(transaction.transaction_hash)
                amount = round(diff - TRANSACTION_FEES, 6)
                try:
                    j.logger.info(
                        f"refunding extra amount: {amount} of transaction {transaction.transaction_hash} to address: {sender_address}"
                    )
                    a = self.parent.wallet._get_asset()
                    refund_hash = self.parent.wallet.transfer(
                        sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
                    )
                    self.extra_paid = False
                    j.logger.info(
                        f"extra amount: {amount} of transaction {transaction.transaction_hash} refunded successfully in transaction: {refund_hash} to address: {sender_address}"
                    )
                except Exception as e:
                    j.logger.critical(
                        f"failed to refund extra amount {amount} for payment: {self.parent.payment_id} due to error: {str(e)}"
                    )
        self.parent.save()
    return self.extra_paid

Inherited members

class PaymentTransaction (parent_=None, instance_name_=None, **values)

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

class Person(Base):
    name = fields.String()
    age = fields.Float()

p = Person(name="ahmed", age="19")
print(p.name, p.age)

Args

parent_ : Base, optional
parent instance. Defaults to None.
instance_name_ : str, optional
instance name. Defaults to None.
**values
any given field values to initiate the instance with
Expand source code
class PaymentTransaction(Base):
    transaction_hash = fields.String(required=True)
    transaction_refund = fields.Object(PaymentTransactionRefund)
    success = fields.Boolean(default=False)

    def refund(self, wallet):
        if self.transaction_refund.success:
            return True
        try:
            amount = round(self.get_amount(wallet) - Decimal(TRANSACTION_FEES), 6)
            if amount < 0:
                self.transaction_refund.success = True
            else:
                a = wallet._get_asset()
                sender_address = wallet.get_sender_wallet_address(self.transaction_hash)
                j.logger.info(
                    f"refunding transaction: {self.transaction_hash} with amount: {amount} to address: {sender_address}"
                )
                self.transaction_refund.transaction_hash = wallet.transfer(
                    sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
                )
                self.transaction_refund.success = True
                j.logger.info(
                    f"transaction: {self.transaction_hash} refunded successfully with amount: {amount} to address: {sender_address} in transaction: {self.transaction_refund.transaction_hash}"
                )
        except Exception as e:
            j.logger.critical(f"failed to refund transaction: {self.transaction_hash} due to error: {str(e)}")
        return self.transaction_refund.success

    def get_amount(self, wallet):
        try:
            effects = wallet.get_transaction_effects(self.transaction_hash)
        except Exception as e:
            j.logger.warning(f"failed to get transaction effects of hash {self.transaction_hash} due to error {str(e)}")
            raise e
        trans_amount = 0
        for effect in effects:
            if effect.asset_code != "TFT":
                continue
            trans_amount += effect.amount
        return trans_amount

Ancestors

  • Base
  • types.SimpleNamespace

Instance variables

var success

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var transaction_hash

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var transaction_refund

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)

Methods

def get_amount(self, wallet)
Expand source code
def get_amount(self, wallet):
    try:
        effects = wallet.get_transaction_effects(self.transaction_hash)
    except Exception as e:
        j.logger.warning(f"failed to get transaction effects of hash {self.transaction_hash} due to error {str(e)}")
        raise e
    trans_amount = 0
    for effect in effects:
        if effect.asset_code != "TFT":
            continue
        trans_amount += effect.amount
    return trans_amount
def refund(self, wallet)
Expand source code
def refund(self, wallet):
    if self.transaction_refund.success:
        return True
    try:
        amount = round(self.get_amount(wallet) - Decimal(TRANSACTION_FEES), 6)
        if amount < 0:
            self.transaction_refund.success = True
        else:
            a = wallet._get_asset()
            sender_address = wallet.get_sender_wallet_address(self.transaction_hash)
            j.logger.info(
                f"refunding transaction: {self.transaction_hash} with amount: {amount} to address: {sender_address}"
            )
            self.transaction_refund.transaction_hash = wallet.transfer(
                sender_address, amount=amount, asset=f"{a.code}:{a.issuer}"
            )
            self.transaction_refund.success = True
            j.logger.info(
                f"transaction: {self.transaction_hash} refunded successfully with amount: {amount} to address: {sender_address} in transaction: {self.transaction_refund.transaction_hash}"
            )
    except Exception as e:
        j.logger.critical(f"failed to refund transaction: {self.transaction_hash} due to error: {str(e)}")
    return self.transaction_refund.success

Inherited members

class PaymentTransactionRefund (parent_=None, instance_name_=None, **values)

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

class Person(Base):
    name = fields.String()
    age = fields.Float()

p = Person(name="ahmed", age="19")
print(p.name, p.age)

Args

parent_ : Base, optional
parent instance. Defaults to None.
instance_name_ : str, optional
instance name. Defaults to None.
**values
any given field values to initiate the instance with
Expand source code
class PaymentTransactionRefund(Base):
    refund_transaction_hash = fields.String()
    success = fields.Boolean(default=False)

Ancestors

  • Base
  • types.SimpleNamespace

Instance variables

var refund_transaction_hash

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var success

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)

Inherited members

class RefundFactory (type_, name_=None, parent_instance_=None, parent_factory_=None)

Stored factories are a custom type of Factory, which uses current configured store backend to store all instance configurations.

get a new stored factory given the type to create and store instances for.

Any factory can have a name, parent Base instance and a parent factory.

Once a stored factory is created, it tries to lazy-load all current configuration for given type_.

Args

type_ : Base
Base class type
name_ : str, optional
factory name. Defaults to None.
parent_instance_ : Base, optional
a parent Base instance. Defaults to None.
parent_factory_ : Factory, optional
a parent Factory. Defaults to None.
Expand source code
class RefundFactory(StoredFactory):
    def list_active_requests(self):
        _, _, refunds = self.find_many(success=False)
        return refunds

Ancestors

Subclasses

  • jumpscale.core.base.factory.RefundFactory

Methods

def list_active_requests(self)
Expand source code
def list_active_requests(self):
    _, _, refunds = self.find_many(success=False)
    return refunds

Inherited members

class RefundRequest (parent_=None, instance_name_=None, **values)

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

base class implementation for any class with fields which supports getting/setting raw data for any instance fields.

any instance can have an optional name and a parent.

class Person(Base):
    name = fields.String()
    age = fields.Float()

p = Person(name="ahmed", age="19")
print(p.name, p.age)

Args

parent_ : Base, optional
parent instance. Defaults to None.
instance_name_ : str, optional
instance name. Defaults to None.
**values
any given field values to initiate the instance with
Expand source code
class RefundRequest(Base):
    payment_id = fields.String(required=True)
    success = fields.Boolean(default=False)
    refund_transaction_hash = fields.String()
    last_tried = fields.DateTime()
    amount = fields.Float(default=-1)

    def apply(self):
        payment = PAYMENT_FACTORY.find_by_id(self.payment_id)
        if not payment.is_finished():
            j.logger.warning(f"can't refund active payment {self.payment_id}")
            return False

        self.last_tried = datetime.datetime.utcnow()
        amount = payment.amount
        # check if refund extra is False. then amount should be same as successful transaction in case of extra was paid but not refunded automatically
        sender_address = None
        for transaction in payment.result.transactions:
            if transaction.success:
                sender_address = payment.wallet.get_sender_wallet_address(transaction.transaction_hash)
                if not payment.refund_extra:
                    amount = float(transaction.get_amount(payment.wallet))

        # if a specific amount was specified by the refund request
        if self.amount > 0:
            amount = self.amount

        if amount <= TRANSACTION_FEES or not sender_address:
            self.success = True
        else:
            try:
                a = payment.wallet._get_asset()
                self.refund_transaction_hash = payment.wallet.transfer(
                    sender_address, amount=round(amount - TRANSACTION_FEES, 6), asset=f"{a.code}:{a.issuer}"
                )
                self.success = True
                j.logger.info(
                    f"refund request successful for payment: {self.payment_id} amount: {amount} to address: {sender_address} in transaction: {self.refund_transaction_hash}"
                )
            except Exception as e:
                j.logger.critical(f"failed to apply refund request for payment {self.payment_id} due to error {str(e)}")
        self.save()
        return self.success

Ancestors

  • Base
  • types.SimpleNamespace

Instance variables

var amount

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var last_tried

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var payment_id

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var refund_transaction_hash

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)
var success

getter method this property

will call _get_value, which would if the value is already defined and will get the default value if not

Returns

any
the field value
Expand source code
def getter(self):
    """
    getter method this property

    will call `_get_value`, which would if the value is already defined
    and will get the default value if not

    Returns:
        any: the field value
    """
    return self._get_value(name, field)

Methods

def apply(self)
Expand source code
def apply(self):
    payment = PAYMENT_FACTORY.find_by_id(self.payment_id)
    if not payment.is_finished():
        j.logger.warning(f"can't refund active payment {self.payment_id}")
        return False

    self.last_tried = datetime.datetime.utcnow()
    amount = payment.amount
    # check if refund extra is False. then amount should be same as successful transaction in case of extra was paid but not refunded automatically
    sender_address = None
    for transaction in payment.result.transactions:
        if transaction.success:
            sender_address = payment.wallet.get_sender_wallet_address(transaction.transaction_hash)
            if not payment.refund_extra:
                amount = float(transaction.get_amount(payment.wallet))

    # if a specific amount was specified by the refund request
    if self.amount > 0:
        amount = self.amount

    if amount <= TRANSACTION_FEES or not sender_address:
        self.success = True
    else:
        try:
            a = payment.wallet._get_asset()
            self.refund_transaction_hash = payment.wallet.transfer(
                sender_address, amount=round(amount - TRANSACTION_FEES, 6), asset=f"{a.code}:{a.issuer}"
            )
            self.success = True
            j.logger.info(
                f"refund request successful for payment: {self.payment_id} amount: {amount} to address: {sender_address} in transaction: {self.refund_transaction_hash}"
            )
        except Exception as e:
            j.logger.critical(f"failed to apply refund request for payment {self.payment_id} due to error {str(e)}")
    self.save()
    return self.success

Inherited members