class UserLiabilityAccountItem(db.Model): __tablename__ = 'user_liability_account_item' id = db.Column(db.BigInteger, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('account_user.id'), nullable=False) user = db.relationship('AccountUser', backref=db.backref('liability_account_items', lazy='dynamic')) type = db.Column(db.Enum('TOTAL', 'AVAILABLE', 'FROZEN'), nullable=False) event_id = db.Column(db.BigInteger, db.ForeignKey('event.id'), nullable=False) event = db.relationship('Event', backref=db.backref('liability_account_items', lazy='dynamic')) side = db.Column(db.Enum('DEBIT', 'CREDIT'), 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 '<UserLiabilityAccountItem %r, %r, %r>' % ( self.user_id, self.side, self.amount)
class ChannelPermission(db.Model): __tablename__ = 'channel_permission' id = db.Column(db.Integer, primary_key=True) channel_id = db.Column(db.Integer, db.ForeignKey('channel.id'), nullable=False) channel = db.relationship('Channel', backref=db.backref('perms', lazy='dynamic'), lazy='joined') api_entry_id = db.Column(db.Integer, db.ForeignKey('api_entry.id'), nullable=False) api_entry = db.relationship('ApiEntry', backref=db.backref('channel_perms', lazy='dynamic'), lazy='joined') created_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) __table_args__ = (db.UniqueConstraint('channel_id', 'api_entry_id', name='channel_api_entry_uniq_idx'), ) def __repr__(self): return 'ChannelPermission<%r->%r>' % (self.channel.name, self.api_entry.path)
class SystemAssetAccountItem(db.Model): __tablename__ = 'system_asset_account_item' id = db.Column(db.BigInteger, primary_key=True) 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('asset_account_items', lazy='dynamic')) event_id = db.Column(db.BigInteger, db.ForeignKey('event.id'), nullable=False) event = db.relationship('Event', backref=db.backref('asset_account_items', lazy='dynamic')) side = db.Column(db.Enum('DEBIT', 'CREDIT'), 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 '<SystemAssetAccountItem %r, %r>' % (self.side, self.amount)
class UserCashBalanceLog(db.Model): __tablename__ = 'user_cash_balance_log' id = db.Column(db.BigInteger, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('account_user.id'), nullable=False) user = db.relationship('AccountUser', backref=db.backref('cash_balance_logs', lazy='dynamic')) event_id = db.Column(db.BigInteger, db.ForeignKey('event.id'), nullable=False) event = db.relationship('Event', backref=db.backref('cash_balance_logs', lazy='dynamic')) total = db.Column(db.Numeric(16, 2), nullable=False, default=0) available = db.Column(db.Numeric(16, 2), nullable=False, default=0) frozen = db.Column(db.Numeric(16, 2), nullable=False, default=0) updated_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow) def __repr__(self): return '<UserCashBalanceLog %r: [%r, %r]>' % (self.user_id, self.total, self.available)
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)
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)
class UserMapping(db.Model): __tablename__ = 'user_mapping' id = db.Column(db.Integer, primary_key=True) user_domain_id = db.Column(db.Integer, db.ForeignKey('user_domain.id'), nullable=False) user_domain = db.relationship('UserDomain', backref=db.backref('user_maps', lazy='dynamic')) user_id = db.Column(db.VARCHAR(32), nullable=False) account_user_id = db.Column(db.Integer, nullable=False, unique=True) # 是否已开通 is_opened = db.Column(db.BOOLEAN, nullable=False, default=False) desc = db.Column(db.VARCHAR(64), default='') created_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) opened_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow) # index # 目前<user_domain_id, user_id>唯一确定一个账号用户,之后可能对应多个 __table_args__ = (db.UniqueConstraint('user_domain_id', 'user_id', name='domain_user_id_uniq_idx'), ) def __repr__(self): return 'UserMapping<%r: %r, %r>' % (self.user_id, self.account_user_id, self.desc)
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)
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)
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)
class ApiEntry(db.Model): __tablename__ = 'api_entry' id = db.Column(db.Integer, primary_key=True) super_id = db.Column(db.Integer, db.ForeignKey('api_entry.id'), nullable=True, default=None) super = db.relationship('ApiEntry', remote_side=id, backref=db.backref('subs', lazy='dynamic'), lazy='joined') name = db.Column(db.VARCHAR(64), nullable=False, index=True) value = db.Column(db.VARCHAR(256), nullable=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) __table_args__ = (db.UniqueConstraint('super_id', 'name', name='super_id_name_uniq_idx'), ) @staticmethod def load_trie(): from api_x.utils import ds trie = ds.Trie() api_entries = ApiEntry.query.all() for api_entry in api_entries: if api_entry.value is not None: trie.put(api_entry.path, api_entry) return api_entries, trie @property def path(self): s, path = self, tuple() while s is not None: path = (s.name, ) + path s = s.super return path @property def id_path(self): s, path = self, tuple() while s is not None: path = (s.id, ) + path s = s.super return path def __repr__(self): return 'ApiEntry<%r>' % (self.path, )
class Channel(db.Model): __tablename__ = 'channel' id = db.Column(db.Integer, primary_key=True) user_domain_id = db.Column(db.Integer, db.ForeignKey('user_domain.id'), nullable=False) user_domain = db.relationship('UserDomain', backref=db.backref('channels', lazy='dynamic'), lazy='joined') # channel name唯一对应一个user_domain name = db.Column(db.VARCHAR(32), nullable=False, unique=True) md5_key = db.Column(db.VARCHAR(64)) public_key = db.Column(db.TEXT) desc = db.Column(db.VARCHAR(64), nullable=False, default='') created_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) # enable zyt pay zyt_pay_enabled = db.Column(db.BOOLEAN, nullable=False, default=False) def __init__(self, *args, **kwargs): super(Channel, self).__init__(*args, **kwargs) def has_entry_perm(self, api_entry_path, level=0): _, api_entry_trie = ApiEntry.load_trie() api_entry = api_entry_trie[api_entry_path] id_path = api_entry.id_path[-level:] return self.perms.filter( ChannelPermission.api_entry_id.in_(id_path)).count() > 0 def get_user_map(self, user_id): return self.user_domain.user_maps.filter_by(user_id=user_id).first() def get_add_user_map(self, user_id): return self.user_domain.get_add_user_map(user_id) def __repr__(self): return 'Channel<%r>' % (self.name, )
class UserTransaction(db.Model): __tablename__ = 'user_transaction' id = db.Column(db.BigInteger, primary_key=True) user_id = db.Column(db.Integer, nullable=False) tx_id = db.Column(db.BigInteger, db.ForeignKey('transaction.id'), nullable=False) tx = db.relationship('Transaction', backref=db.backref('users', lazy='dynamic'), lazy='joined') role = db.Column(db.Enum(UserRole.FROM, UserRole.GUARANTOR, UserRole.TO, UserRole.TX_FROM, UserRole.TX_TO), nullable=False) def __repr__(self): return '<UserTransaction #%.6d %9s:%.5d, %.6d>' % ( self.id, self.role, self.user_id, self.tx_id)
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, )
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, )
class TransactionStateLog(db.Model): __tablename__ = 'transaction_state_log' id = db.Column(db.BigInteger, primary_key=True) group_id = db.Column(db.BigInteger, nullable=False, default=0L) tx_id = db.Column(db.BigInteger, db.ForeignKey('transaction.id'), nullable=False) tx = db.relationship('Transaction', backref=db.backref('state_logs', lazy='dynamic'), lazy='joined') prev_state = db.Column(db.VARCHAR(32), nullable=False) state = db.Column(db.VARCHAR(32), nullable=False) event_id = db.Column(db.BigInteger, nullable=True, default=0L) created_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) def __repr__(self): return '<TransactionStateLog %r: %r->%r@%r>' % ( self.tx_id, self.prev_state, self.state, self.event_id)
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))
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)
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)