Exemplo n.º 1
0
Arquivo: models.py Projeto: webee/pay
class RefundRecord(db.Model):
    __tablename__ = 'refund_record'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_id = db.Column(db.BigInteger,
                      db.ForeignKey('transaction.id'),
                      nullable=False,
                      unique=True)
    tx = db.relationship('Transaction',
                         backref=db.backref('refund_record', lazy='dynamic'),
                         lazy='joined')

    sn = db.Column(db.CHAR(32), nullable=False)

    payment_sn = db.Column(db.CHAR(32), nullable=False)
    payment_state = db.Column(db.VARCHAR(32), nullable=False)
    payer_id = db.Column(db.Integer, nullable=False)
    payee_id = db.Column(db.Integer, nullable=False)
    order_id = db.Column(db.VARCHAR(64), nullable=False)

    amount = db.Column(db.Numeric(16, 2), nullable=False)
    client_notify_url = db.Column(db.VARCHAR(128))
    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)

    updated_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)

    def __repr__(self):
        return '<Refund %r, %r<-%r$%r>' % (self.id, self.payer_id,
                                           self.payee_id, self.amount)
Exemplo n.º 2
0
class Bankcard(db.Model):
    __tablename__ = 'bankcard'

    id = db.Column(db.Integer, primary_key=True)

    user_id = db.Column(db.Integer, nullable=False)

    # 0-对私, 1-对公
    flag = db.Column(db.SmallInteger, nullable=False)
    card_no = db.Column(db.VARCHAR(21), nullable=False)
    card_type = db.Column(db.Enum('DEBIT', 'CREDIT'), nullable=False)
    acct_name = db.Column(db.VARCHAR(32), nullable=False)
    bank_code = db.Column(db.CHAR(9), nullable=False)
    province_code = db.Column(db.VARCHAR(12), nullable=False)
    city_code = db.Column(db.VARCHAR(12), nullable=False)
    bank_name = db.Column(db.VARCHAR(32), nullable=False)
    brabank_name = db.Column(db.VARCHAR(50), nullable=False)
    prcptcd = db.Column(db.CHAR(12), default='')
    is_bounded = db.Column(db.Boolean, nullable=False, default=True)

    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)
    updated_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)

    # index
    __table_args__ = (db.UniqueConstraint('user_id',
                                          'card_no',
                                          name='user_card_uniq_idx'), )

    def to_dict(self):
        return {
            'id': self.id,
            'flag': self.flag,
            'card_no': self.card_no,
            'card_type': self.card_type,
            'acct_name': self.acct_name,
            'bank_code': self.bank_code,
            'province_code': self.province_code,
            'city_code': self.city_code,
            'bank_name': self.bank_name,
            'brabank_name': self.brabank_name,
            'prcptcd': self.prcptcd,
            'created_on': self.created_on
        }

    def __repr__(self):
        return 'Bankcard<%r, %r>' % (self.card_no, self.bank_name)
Exemplo n.º 3
0
class UserWithdrawLog(db.Model):
    __tablename__ = 'user_withdraw_log'

    id = db.Column(db.Integer, primary_key=True)
    tx_sn = db.Column(db.CHAR(32), nullable=False)
    user_id = db.Column(db.Integer, nullable=False)
    bankcard_id = db.Column(db.Integer,
                            db.ForeignKey('bankcard.id'),
                            nullable=False)

    amount = db.Column(db.Numeric(16, 2), nullable=False)
    actual_amount = db.Column(db.Numeric(16, 2), nullable=False)
    fee = db.Column(db.Numeric(16, 2), nullable=False, default=0)

    state = db.Column(db.Enum(WithdrawState.PROCESSING, WithdrawState.FAILED,
                              WithdrawState.SUCCESS),
                      nullable=False)

    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)
    updated_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)
Exemplo n.º 4
0
Arquivo: models.py Projeto: webee/pay
class PrepaidRecord(db.Model):
    __tablename__ = 'prepaid_record'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_id = db.Column(db.BigInteger,
                      db.ForeignKey('transaction.id'),
                      nullable=False,
                      unique=True)
    tx = db.relationship('Transaction',
                         backref=db.backref('prepaid_record', lazy='dynamic'),
                         lazy='joined')

    sn = db.Column(db.CHAR(32), nullable=False)

    to_id = db.Column(db.Integer, nullable=False)
    amount = db.Column(db.Numeric(16, 2), nullable=False)

    client_callback_url = db.Column(db.VARCHAR(128))
    client_notify_url = db.Column(db.VARCHAR(128))
    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)

    @property
    def payer_id(self):
        return self.to_id

    def __repr__(self):
        return '<Prepaid %r->%r>' % (self.amount, self.to_id)
Exemplo n.º 5
0
Arquivo: models.py Projeto: webee/pay
class DuplicatedPaymentRecord(db.Model):
    __tablename__ = 'duplicated_payment_record'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_id = db.Column(db.BigInteger,
                      db.ForeignKey('transaction.id'),
                      nullable=False,
                      unique=True)
    tx = db.relationship('Transaction',
                         backref=db.backref('duplicated_payment_record',
                                            lazy='dynamic'),
                         lazy='joined')

    sn = db.Column(db.CHAR(32), nullable=False)
    source = db.Column(db.Enum(TransactionType.PAYMENT,
                               TransactionType.PREPAID),
                       nullable=False)

    vas_name = db.Column(db.VARCHAR(32), nullable=False, default='')
    vas_sn = db.Column(db.VARCHAR(128), nullable=False, default='')

    event_id = db.Column(db.BigInteger, nullable=False, default=0L)

    status = db.Column(db.SmallInteger, nullable=False, default=0)
    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)
    updated_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)
Exemplo n.º 6
0
Arquivo: models.py Projeto: webee/pay
class TransactionSnStack(db.Model):
    """ 用于记录tx的sn变化,比如支付时为防止evas异常,会定次定时变化sn,
    在回调中可以根据sn的记录来确定是否是同一笔交易。
    """
    __tablename__ = 'transaction_sn_stack'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_id = db.Column(db.BigInteger,
                      db.ForeignKey('transaction.id'),
                      nullable=False)
    tx = db.relationship('Transaction',
                         backref=db.backref('sns', lazy='dynamic'),
                         lazy='joined')
    sn = db.Column(db.CHAR(32), index=True)
    generated_on = db.Column(db.DateTime, nullable=False)
    state = db.Column(db.VARCHAR(32), nullable=False)
    # 改变原因、类型
    change = db.Column(db.Enum(PaymentChangeType.EXPIRED,
                               PaymentChangeType.AMOUNT,
                               PaymentChangeType.INFO),
                       default=None)

    pushed_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)

    def __repr__(self):
        return '<TransactionSnStack %s, %s, %s, %s>' % (
            self.tx_id, self.sn, self.state, self.change)
Exemplo n.º 7
0
class Event(db.Model):
    __tablename__ = 'event'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_sn = db.Column(db.CHAR(32), nullable=False)
    vas_name = db.Column(db.VARCHAR(32), nullable=False)

    user_id = db.Column(db.Integer,
                        db.ForeignKey('account_user.id'),
                        nullable=False)
    user = db.relationship('AccountUser',
                           backref=db.backref('events', lazy='dynamic'))

    type = db.Column(db.Enum(EventType.TRANSFER_IN, EventType.TRANSFER_OUT,
                             EventType.FREEZE, EventType.UNFREEZE,
                             EventType.TRANSFER_IN_FROZEN,
                             EventType.TRANSFER_OUT_FROZEN),
                     nullable=False)

    amount = db.Column(db.Numeric(16, 2), nullable=False)
    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)

    def __repr__(self):
        return '<Event %r, %r, %r>' % (self.id, self.user_id, self.amount)
Exemplo n.º 8
0
Arquivo: models.py Projeto: webee/pay
class WithdrawRecord(db.Model):
    __tablename__ = 'withdraw_record'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_id = db.Column(db.BigInteger,
                      db.ForeignKey('transaction.id'),
                      nullable=False)
    tx = db.relationship('Transaction',
                         backref=db.backref('withdraw_record', lazy='dynamic'),
                         lazy='joined')

    sn = db.Column(db.CHAR(32), nullable=False)

    from_user_id = db.Column(db.Integer, nullable=False)
    flag_card = db.Column(db.CHAR(1), nullable=False)
    card_type = db.Column(db.Enum('DEBIT', 'CREDIT'), nullable=False)
    card_no = db.Column(db.VARCHAR(21), nullable=False)
    acct_name = db.Column(db.VARCHAR(12), nullable=False)
    bank_code = db.Column(db.CHAR(9))
    province_code = db.Column(db.VARCHAR(12))
    city_code = db.Column(db.VARCHAR(12))
    bank_name = db.Column(db.VARCHAR(32), nullable=False)
    brabank_name = db.Column(db.VARCHAR(50))
    prcptcd = db.Column(db.CHAR(12))

    amount = db.Column(db.Numeric(16, 2), nullable=False)
    actual_amount = db.Column(db.Numeric(16, 2), nullable=False)
    fee = db.Column(db.Numeric(16, 2), nullable=False, default=0)

    client_notify_url = db.Column(db.VARCHAR(128))

    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)
    updated_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)

    def __repr__(self):
        return '<Withdraw %r>' % (self.id, )
Exemplo n.º 9
0
Arquivo: models.py Projeto: webee/pay
class PaymentRecordLianlianPayExtra(db.Model):
    __tablename__ = 'payment_record_lianlian_pay_extra'

    id = db.Column(db.BigInteger, primary_key=True)
    payment_record_id = db.Column(db.BigInteger,
                                  db.ForeignKey('payment_record.id'),
                                  nullable=False,
                                  unique=True)

    pay_type = db.Column(db.CHAR(1))

    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)
Exemplo n.º 10
0
Arquivo: models.py Projeto: webee/pay
class DebitNoteDetail(db.Model):
    __tablename__ = 'debit_note_detail'

    id = db.Column(db.BigInteger, primary_key=True)
    sn = db.Column(db.CHAR(32), nullable=False)
    vas_name = db.Column(db.VARCHAR(32), nullable=False)
    amount = db.Column(db.Numeric(16, 2), nullable=False)
    order_id = db.Column(db.VARCHAR(64), nullable=False, default='')
    state = db.Column(db.Boolean, nullable=False,
                      default=False)  # False:Lvyeok    True:LLok
    valid = db.Column(db.Boolean, nullable=False,
                      default=False)  # False:checking  True:ok
    type = db.Column(db.Enum(TransactionType.PAYMENT, TransactionType.REFUND),
                     nullable=False)
    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)

    def __repr__(self):
        return '<DebitNoteDetail %r, %r, %r, %r, %r, %r, %r, %r>' % (
            self.id, self.sn, self.vas_name, self.amount, self.order_id,
            self.state, self.valid, self.created_on)
Exemplo n.º 11
0
Arquivo: models.py Projeto: webee/pay
class TransferRecord(db.Model):
    __tablename__ = 'transfer_record'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_id = db.Column(db.BigInteger,
                      db.ForeignKey('transaction.id'),
                      nullable=False)
    tx = db.relationship('Transaction',
                         backref=db.backref('transfer_record', lazy='dynamic'),
                         lazy='joined')

    sn = db.Column(db.CHAR(32), nullable=False)

    from_id = db.Column(db.Integer, nullable=False)
    to_id = db.Column(db.Integer, nullable=False)
    amount = db.Column(db.Numeric(16, 2), nullable=False)

    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)

    def __repr__(self):
        return '<Transfer %r>' % (self.id, )
Exemplo n.º 12
0
class BankcardBin(db.Model):
    __tablename__ = 'bankcard_bin'

    id = db.Column(db.Integer, primary_key=True)

    card_no = db.Column(db.VARCHAR(21), nullable=False, unique=True)
    bank_code = db.Column(db.CHAR(9), nullable=False)
    bank_name = db.Column(db.VARCHAR(32), nullable=False)
    card_type = db.Column(db.Enum('DEBIT', 'CREDIT'), nullable=False)

    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)

    def to_dict(self):
        return {
            'card_no': self.card_no,
            'bank_code': self.bank_code,
            'bank_name': self.bank_name,
            'card_type': self.card_type
        }

    def __repr__(self):
        return 'BankcardBin<%r, %r>' % (self.card_no, self.bank_name)
Exemplo n.º 13
0
Arquivo: models.py Projeto: webee/pay
class Transaction(db.Model):
    __tablename__ = 'transaction'

    id = db.Column(db.BigInteger, primary_key=True)
    # 父tx
    super_id = db.Column(db.BigInteger,
                         db.ForeignKey('transaction.id'),
                         nullable=True,
                         default=None)
    super = db.relationship('Transaction',
                            remote_side=id,
                            backref=db.backref('subs', lazy='dynamic'),
                            lazy='joined')

    sn = db.Column(db.CHAR(32), unique=True)
    type = db.Column(db.Enum(TransactionType.PAYMENT, TransactionType.REFUND,
                             TransactionType.WITHDRAW,
                             TransactionType.TRANSFER, TransactionType.PREPAID,
                             TransactionType.CHEQUE),
                     nullable=False)

    channel_name = db.Column(db.VARCHAR(32), nullable=False)
    order_id = db.Column(db.VARCHAR(64), nullable=False, default='')
    # 涉及到的总金额
    # 支付金额,退款金额,提现金额(+手续费), 充值金额
    amount = db.Column(db.Numeric(16, 2), nullable=False)
    comments = db.Column(db.VARCHAR(128), default='')
    state = db.Column(db.VARCHAR(32), nullable=False)

    # need update on notify.
    vas_name = db.Column(db.VARCHAR(32), nullable=False, default='')
    vas_sn = db.Column(db.VARCHAR(128), nullable=False, default='')

    tried_times = db.Column(db.Integer, nullable=False, default=1)

    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)
    updated_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)

    def __init__(self, *args, **kwargs):
        super(Transaction, self).__init__(*args, **kwargs)
        self.__record = None
        self.__source_sn = None
        self.__stack_sn_item = None

    @property
    def channel(self):
        from api_x.zyt.user_mapping import get_channel_by_name
        return get_channel_by_name(self.channel_name)

    @staticmethod
    def get_tx_from_hashed_sn(hashed_sn):
        try:
            tx_id, _hash = utils.aes_decrypt(hashed_sn).split('$', 1)
            return Transaction.query.get(tx_id), _hash
        except Exception as e:
            logger.warn('bad hashed sn: [{0}], [{1}]'.format(
                hashed_sn, e.message))
        return None, None

    @property
    def sn_with_expire_hash(self):
        now = times.timestamp()
        expired = now + etc.Biz.PAYMENT_CHECKOUT_VALID_SECONDS

        key = str(self.id) + str(self.tried_times) + etc.KEY
        data = str(int(expired))

        logger.info('hash sn: [%s], now: [%s], expired: [%s]' %
                    (self.sn, now, expired))
        _hash = utils.aes_encrypt(data, key)

        return utils.aes_encrypt('%s$%s' % (self.id, _hash))

    def check_expire_hash(self, expire_hash):
        now = times.timestamp()
        key = str(self.id) + str(self.tried_times) + etc.KEY
        try:
            logger.info('try check sn: [%s], now: [%s], _hash: [%s]' %
                        (self.sn, now, expire_hash))
            expired = long(utils.aes_decrypt(expire_hash, key))
        except Exception as _:
            return False

        timestamp = times.timestamp()
        logger.info('done check sn: [%s], timestamp: [%s], expired: [%s]' %
                    (self.sn, timestamp, expired))
        return timestamp < expired

    @property
    def record(self):
        if hasattr(self, '_Transaction__record') and self.__record:
            return self.__record
        if self.type == TransactionType.PAYMENT:
            self.__record = self.payment_record.one()
        elif self.type == TransactionType.REFUND:
            self.__record = self.refund_record.one()
        elif self.type == TransactionType.WITHDRAW:
            self.__record = self.withdraw_record.one()
        elif self.type == TransactionType.TRANSFER:
            self.__record = self.transfer_record.one()
        elif self.type == TransactionType.PREPAID:
            self.__record = self.prepaid_record.one()
        elif self.type == TransactionType.CHEQUE:
            self.__record = self.cheque_record.one()

        return self.__record

    def get_role(self, role):
        return self.users.filter_by(role=role).first()

    @property
    def source_sn(self):
        """ 表示当前使用的sn,参考 TransactionSnStack
        """
        # NOTE: Model类的属性名前面都添加了_<ClassName>前缀
        if hasattr(self, '_Transaction__source_sn') and self.__source_sn:
            return self.__source_sn
        return self.sn

    @source_sn.setter
    def source_sn(self, sn):
        self.__source_sn = sn

    @property
    def stack_sn_item(self):
        """ 表示当前使用的sn,参考 TransactionSnStack
        """
        # NOTE: Model类的属性名前面都添加了_<ClassName>前缀
        if hasattr(self,
                   '_Transaction__stack_sn_item') and self.__stack_sn_item:
            return self.__stack_sn_item
        return self.sn

    @stack_sn_item.setter
    def stack_sn_item(self, sn):
        self.__stack_sn_item = sn

    def __repr__(self):
        return '<Transaction %s, %s, %s, %s>' % (self.type, str(
            self.amount), self.state, str(self.created_on))
Exemplo n.º 14
0
Arquivo: models.py Projeto: webee/pay
class ChequeRecord(db.Model):
    __tablename__ = 'cheque_record'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_id = db.Column(db.BigInteger,
                      db.ForeignKey('transaction.id'),
                      nullable=False)
    tx = db.relationship('Transaction',
                         backref=db.backref('cheque_record', lazy='dynamic'),
                         lazy='joined')

    sn = db.Column(db.CHAR(32), nullable=False)

    type = db.Column(db.Enum(ChequeType.INSTANT, ChequeType.LAZY),
                     nullable=False)
    signature = db.Column(db.CHAR(32))
    info = db.Column(db.CHAR(128), default='')
    from_id = db.Column(db.Integer, nullable=False)
    # 兑现时才知道
    to_id = db.Column(db.Integer, nullable=True)
    amount = db.Column(db.Numeric(16, 2), nullable=False)
    client_notify_url = db.Column(db.VARCHAR(128))

    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)
    updated_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)
    expired_at = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)

    @property
    def cash_token(self):
        tx_id = self.tx_id
        signature = self.signature
        signer = Signer('key', 'sign', etc.KEY, etc.LVYE_PRI_KEY, None)
        data = {'tx_id': tx_id, 'signature': signature}

        sign = signer.sign(data, SignType.RSA)
        return '%s_%s' % (ints.int_to_base36(tx_id),
                          hashlib.md5(sign).hexdigest()[::2])

    @staticmethod
    def get_cheque_record_from_cash_token(cash_token):
        try:
            from api_x.zyt.biz.transaction.dba import get_tx_by_id
            tx_id, hash_sign = cash_token.split('_')
            tx_id = ints.base36_to_int(tx_id)

            tx = get_tx_by_id(tx_id)
            if tx is None or tx.type != TransactionType.CHEQUE:
                return None
            cheque_record = tx.record
            if cheque_record.cash_token == cash_token:
                return cheque_record
        except Exception as _:
            pass
        return None

    def __repr__(self):
        return '<Check %r, %r>' % (self.id, self.amount)
Exemplo n.º 15
0
Arquivo: models.py Projeto: webee/pay
class PaymentRecord(db.Model):
    __tablename__ = 'payment_record'

    id = db.Column(db.BigInteger, primary_key=True)
    tx_id = db.Column(db.BigInteger,
                      db.ForeignKey('transaction.id'),
                      nullable=False,
                      unique=True)
    tx = db.relationship('Transaction',
                         backref=db.backref('payment_record', lazy='dynamic'),
                         lazy='joined')

    sn = db.Column(db.CHAR(32), nullable=False)

    type = db.Column(db.Enum(PaymentType.DIRECT, PaymentType.GUARANTEE),
                     nullable=False)

    payer_id = db.Column(db.Integer, nullable=False)
    payee_id = db.Column(db.Integer, nullable=False)
    channel_id = db.Column(db.Integer, nullable=False)
    order_id = db.Column(db.VARCHAR(64), nullable=False)
    origin = db.Column(db.VARCHAR(32), default=None)
    product_name = db.Column(db.VARCHAR(150), nullable=False)
    product_category = db.Column(db.VARCHAR(50), nullable=False)
    product_desc = db.Column(db.VARCHAR(350), nullable=False)
    amount = db.Column(db.Numeric(16, 2), nullable=False)
    real_amount = db.Column(db.Numeric(16, 2), nullable=False, default=0)
    paid_amount = db.Column(db.Numeric(16, 2), nullable=False, default=0)
    refunded_amount = db.Column(db.Numeric(16, 2), nullable=False, default=0)
    client_callback_url = db.Column(db.VARCHAR(128))
    client_notify_url = db.Column(db.VARCHAR(128))
    created_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow)

    tried_times = db.Column(db.Integer, nullable=False, default=1)
    updated_on = db.Column(db.DateTime,
                           nullable=False,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)

    # index
    __table_args__ = (db.UniqueConstraint(
        'channel_id',
        'order_id',
        'origin',
        name='channel_order_id_origin_uniq_idx'), )

    def is_finished(self):
        return self.real_amount == self.paid_amount - self.refunded_amount

    @transactional
    def add_refund(self, refund_record, event_id):
        from api_x.constant import PaymentTxState
        from api_x.zyt.biz.transaction import transit_transaction_state
        self.refunded_amount += refund_record.amount
        # 全部金额都退款,则状态为已退款
        is_refunded = self.paid_amount == self.refunded_amount

        if self.tx.state == PaymentTxState.REFUNDING:
            if is_refunded:
                transit_transaction_state(self.tx_id, PaymentTxState.REFUNDING,
                                          PaymentTxState.REFUNDED, event_id)
            else:
                transit_transaction_state(self.tx_id, PaymentTxState.REFUNDING,
                                          refund_record.payment_state,
                                          event_id)
        db.session.add(self)

        super_tx = self.tx.super
        if super_tx and super_tx.type == TransactionType.PAYMENT:
            super_payment = super_tx.record
            super_payment.add_refund(refund_record, event_id)

    @transactional
    def add_paid(self, amount):
        self.paid_amount += amount
        db.session.add(self)

        super_tx = self.tx.super
        if super_tx and super_tx.type == TransactionType.PAYMENT:
            super_payment = super_tx.record
            super_payment.add_paid(amount)

    def __repr__(self):
        return '<Payment %r, %r->%r$%r:%r:%r/%r>' % (
            self.id, self.payer_id, self.payee_id, self.amount,
            self.real_amount, self.paid_amount, self.refunded_amount)