Exemplo n.º 1
0
class AppUser(BaseModel):
    __tablename__ = "app_user"

    user_id = db.Column(UUID, db.ForeignKey("user.id"), primary_key=True)
    user = db.relationship("User", backref="user_apps")

    app_id = db.Column(UUID, db.ForeignKey("app.id"), primary_key=True)
    app = db.relationship("App", backref="app_users")

    role = db.Column(db.Enum(Role), nullable=False, server_default="admin")
Exemplo n.º 2
0
class Deposit(BaseModel):
    account_id = db.Column(UUID, db.ForeignKey("account.id"))
    account = db.relationship("Account")

    amount = db.Column(db.Integer, default=0, nullable=False)
    real_amount = db.Column(db.Integer, default=0, nullable=False)

    stripe_charge_id = db.Column(db.Text)
    stripe_data = db.Column(JSONB)
Exemplo n.º 3
0
class Payable(BaseModel):

    app_id = db.Column(UUID, db.ForeignKey("app.id"))
    app = db.relationship("App", back_populates="payables")

    display_name = db.Column(db.Text, nullable=False)
    display_price = db.Column(db.Integer, nullable=False)

    permalink = db.Column(db.Text, nullable=True)
Exemplo n.º 4
0
class User(BaseModel):

    email = db.Column(db.Text, unique=True, nullable=False)
    name = db.Column(db.Text, nullable=True)

    account_id = db.Column(UUID, db.ForeignKey("account.id"))
    account = db.relationship("Account")

    # apps = db.relationship("AppUser", back_populates="user")
    apps = association_proxy("user_apps",
                             "app",
                             creator=lambda app: AppUser(app=app))

    stripe_id = db.Column(db.Text)
    last_4 = db.Column(db.Text)
    card_brand = db.Column(db.Text)

    top_up = db.Column(db.Integer, default=0, nullable=True)

    def owns(self, app_id):
        return id in [app.id for app in self.apps]

    @staticmethod
    def create(email, name, commit=True):
        account = Account()
        user = User(email=email, name=name, account=account)

        db.session.add(account)
        db.session.add(user)

        if commit:
            db.session.commit()

        return user

    def as_stripe_customer(self, customer, commit=True):
        # takes in a stripe customer object and sets fields of user accordingly
        if customer is not None:
            self.stripe_id = customer.stripe_id

            if len(customer.sources.data) > 0:
                self.last_4 = customer.sources.data[0].last4
                self.card_brand = customer.sources.data[0].brand

        if commit:
            db.session.commit()

        return self

    def deposit(self, amount):
        if self.stripe_id is None:
            return None

        return self.account.deposit(amount, self.stripe_id)

    @staticmethod
    def create_for_email(email, name):
        if email is None or name is None:
            return None

        user = User.query.filter_by(email=email).first()
        if user is not None:
            raise ApiException("User with this email already exists")

        return User.create(email, name, commit=False)

    @staticmethod
    def for_token(decoded_token):
        """
        Given a decoded firebase token with fields 'email' and 'name', provides the user matching
        that email if it exists, or creates a new user.
        """
        email = decoded_token["email"]
        name = decoded_token["name"] if "name" in decoded_token.keys(
        ) else None

        if email is None:
            return None, False

        user = User.query.filter_by(email=email).first()
        if user is not None:
            return user, False

        return User.create(email, name)

    @staticmethod
    def for_account(account_id):
        return User.query.filter_by(account_id=account_id).first()
Exemplo n.º 5
0
class App(BaseModel):
    account_id = db.Column(UUID, db.ForeignKey("account.id"))
    account = db.relationship("Account")

    # users = db.relationship("AppUser", back_populates="app")
    users = association_proxy(
        "app_users", "user", creator=lambda user: AppUser(user=user)
    )

    payables = db.relationship("Payable", back_populates="app")

    secret = db.Column(db.Text, nullable=False)
    name = db.Column(db.Text, nullable=False)
    url = db.Column(db.Text, nullable=True)
    description = db.Column(db.Text, nullable=True)

    image_url = db.Column(db.Text, nullable=True)

    stripe_account_id = db.Column(db.Text, nullable=True)

    privateFields = ["secret", "stripe_account_id", "account_id"]

    @staticmethod
    def for_account(account_id):
        return App.query.filter_by(account_id=account_id).first()

    @staticmethod
    def generate_secret():
        return "".join(
            random.choice(string.ascii_letters + string.digits) for _ in range(40)
        )

    @staticmethod
    def create_for_user(user, name, image_url=None, url=None, description=None):
        account = Account()
        app = App(
            account=account,
            name=name,
            secret=App.generate_secret(),
            url=url,
            image_url=image_url,
            description=description,
        )

        user.apps.append(app)

        db.session.add(account)
        db.session.add(app)
        db.session.commit()

        return app

    def app_info(self):
        d = self.to_dict()
        d["balance"] = self.account.balance
        return d

    @staticmethod
    def basic_info(id):
        # returns a publicly viewable dict containing basic info about app given an id
        app = App.query.get(id)
        return {
            "id": app.id,
            "name": app.name,
            "url": app.url,
            "description": app.description,
            "image_url": app.image_url,
        }
Exemplo n.º 6
0
class Payment(BaseModel):
    source_account_id = db.Column(UUID, db.ForeignKey("account.id"))
    source_account = db.relationship("Account", foreign_keys=[source_account_id])

    dest_account_id = db.Column(UUID, db.ForeignKey("account.id"))
    dest_account = db.relationship("Account", foreign_keys=[dest_account_id])

    payable_id = db.Column(UUID, db.ForeignKey("payable.id"))
    payable = db.relationship("Payable")

    content_url = db.Column(db.String)

    amount = db.Column(db.Integer, nullable=False, default=0)

    def get_user_info(self):
        d = BaseModel.to_dict(self)
        d["app"] = App.for_account(self.dest_account_id).to_dict()
        d["user"] = User.for_account(self.source_account_id).to_dict()
        return d

    @staticmethod
    def payments_to(dest_id, page_size, page=1):
        return (
            Payment.query.filter_by(dest_account_id=dest_id)
            .order_by(Payment.time_created.desc())
            .paginate(page, page_size, error_out=False)
        )

    @staticmethod
    def payments_from(source_id, page_size, page=1):
        return (
            Payment.query.filter_by(source_account_id=source_id)
            .order_by(Payment.time_created.desc())
            .paginate(page, page_size, error_out=False)
        )

    @staticmethod
    def payments_summary_to(dest_id):
        return (
            # Payment.query(Payment.time_created, func.sum(Payment.amount).label('total'))

            Payment.query.filter_by(dest_account_id=dest_id)
            # .group_by(func.day(Payment.time_created))
            .order_by(Payment.time_created.desc())
            .all()
        )

    @staticmethod
    def transfer(user, app, price, count):
        balance = user.account.balance
        leftover = 0

        amount = count * price

        if balance is None or balance < price:
            raise ApiException("Not enough funds")

        if balance < amount:
            # Get amount that can fit
            amount = (balance // price) * price

        # Check if there is a matching payment in the last 15 minutes
        since = datetime.now() - timedelta(minutes=15)
        existing = (
            Payment.query.filter(Payment.time_created > since)
            .filter_by(source_account_id=user.account.id)
            .filter_by(dest_account_id=app.account.id)
            .order_by(Payment.time_created.desc())
            .first()
        )
        if existing is None:
            payment = Payment(
                source_account=user.account, dest_account=app.account, amount=amount
            )
            db.session.add(payment)

        else:
            existing.amount += amount
            existing.time_created = datetime.now()
            payment = existing

        user.account.balance -= amount

        if app.account.balance is None:
            app.account.balance = 0
        app.account.balance += amount

        db.session.commit()

        leftover = (price * count) - amount
        return payment, leftover