Beispiel #1
0
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)
Beispiel #2
0
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)
Beispiel #3
0
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)
Beispiel #4
0
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)
Beispiel #5
0
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)
Beispiel #6
0
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)
Beispiel #7
0
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)
Beispiel #8
0
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)
Beispiel #9
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)
Beispiel #10
0
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)
Beispiel #11
0
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, )
Beispiel #12
0
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, )
Beispiel #13
0
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)
Beispiel #14
0
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, )
Beispiel #15
0
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, )
Beispiel #16
0
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)
Beispiel #17
0
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))
Beispiel #18
0
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)
Beispiel #19
0
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)