class CreditCard(db.Model):
    IS_EXPIRING_THRESHOLD_MONTHS = 60 

    __tablename__ = 'credit_cards'

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

    # Foreign key
    user_id = db.Column(db.Integer, db.ForeignKey('users.id', onupdate='CASCADE', ondelete='CASCADE'), index=True, nullable=False)

    # Card details
    brand = db.Column(db.String(32))
    last4 = db.Column(db.Integer)
    exp_date = db.Column(db.Date, index=True)
    is_expiring = db.Column(db.Boolean(), nullable=False, server_default='0')

    def __init__(self, **kwargs):
        super(CreditCard, self).__init__(**kwargs)

    @classmethod
    def is_expiring_soon(cls, compare_date=None, exp_date=None):
        """Check card to expire in 60 days"""
        return exp_date <= timedelta_days(CreditCard.IS_EXPIRING_THRESHOLD_MONTHS, compare_date=compare_date)

    @classmethod
    def mark_old_credit_cards(cls, compare_date=None):
        """Mark credit card that are going to expire in two months or has expired"""

        today_with_delta = timedelta_days(CreditCard.IS_EXPIRING_THRESHOLD_MONTHS, compare_date=compare_date)

        CreditCard.query.filter(CreditCard.exp_date <= today_with_delta).update({CreditCard.is_expiring:True})

        return db.session.commit()

    @classmethod
    def extract_card_params(cls, customer):
        """
        Extract the credit card info from paying customer 
        """
        card_data = customer.sources.data[0]
        exp_date = datetime.date(card_data.exp_year, card_data.exp_month, 1)

        card = {
            'brand' : card_data.brand,
            'last4' : card_data.last4,
            'exp_date' : exp_date,
            'is_expiring' : CreditCard.is_expiring_soon(exp_date=exp_date)
        }

        return card 
Пример #2
0
class Subscription(db.Model):
    __tablename__ = 'subscriptions'

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

    # Relationships
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        index=True,
                        nullable=False)

    plan = db.Column(db.String(128))

    coupon = db.Column(db.String(128))

    def __init__(self, **kwargs):
        super(Subscription, self).__init__(**kwargs)

    @classmethod
    def get_plan_by_id(cls, plan):
        """Identify plan by id and compare"""

        for key, value in settings.STRIPE_PLANS.items():
            if value.get('id') == plan:
                return settings.STRIPE_PLANS[key]

        return None

    @classmethod
    def get_new_plan(cls, keys):
        """Pick plan based on the plan identifier"""

        for key in keys:
            split_key = key.split('submit_')

            if isinstance(split_key, list) and len(split_key) == 2:
                if Subscription.get_plan_by_id(split_key[1]):
                    return split_key[1]
        return None

    def create(self, user=None, plan=None, coupon=None, token=None, name=None):
        """Create a recurring subscription"""

        if token is None:
            return False

        if coupon:
            self.coupon = coupon.upper()

        customer = PaymentSubscription.create(token=token,
                                              email=user.email,
                                              coupon=None,
                                              plan=plan)

        # Updating user details
        user.name = name
        user.payment_id = customer.id
        user.cancelled_subscription_on = None

        # Set the subscription details
        self.user_id = user.id
        self.plan = plan

        # Redeem the coupon
        if coupon:
            coupon = Coupon.query.filter(Coupon.code == self.coupon).first()
            coupon.redeem()

        credit_card = CreditCard(user_id=user.id,
                                 **CreditCard.extract_card_params(customer))

        db.session.add(user)
        db.session.add(self)
        db.session.add(credit_card)

        db.session.commit()

        return True

    def update(self, user=None, plan=None, coupon=None):
        """Updating an existing subscription plan"""

        PaymentSubscription.update(customer_id=user.payment_id,
                                   coupon=coupon,
                                   plan=plan)

        user.subscription.plan = plan
        if coupon:
            user.subscription.coupon = coupon
            coupon = Coupon.query.filter(Coupon.coupon == coupon).first()

            if coupon:
                coupon.redeem()

        db.session.add(user.subscription)
        db.session.commit()

        return True

    def cancel(self, user=None, discard_credit_card=True):
        """Cancel an existing subscription"""

        PaymentSubscription.cancel(user.payment_id)
        user.payment_id = None
        user.cancelled_subscription_on = datetime.datetime.now(pytz.utc)

        db.session.add(user)
        db.session.delete(user.subscription)

        if discard_credit_card:
            db.session.delete(user.credit_card)

        db.session.commit()

        return True

    def update_payment_method(self,
                              user=None,
                              credit_card=None,
                              name=None,
                              token=None):
        """Updating user existing card"""

        if token is None:
            return False

        customer = PaymentCard.update(customer_id=user.payment_id,
                                      stripe_token=token)

        # Update user details
        user.name = name

        # Update the credit card
        new_card = CreditCard.extract_card_params(customer)
        credit_card.brand = new_card.get('brand')
        credit_card.last4 = new_card.get('last4')
        credit_card.exp_date = new_card.get('exp_date')
        credit_card.is_expiring = new_card.get('is_expiring')

        db.session.add(user)
        db.session.add(credit_card)

        db.session.commit()

        return True
Пример #3
0
class Bet(ResourceMixin, db.Model):
    __tablename__ = 'bets'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    user_id = db.Column(db.Integer, db.ForeignKey('users.id',
                                                  onupdate='CASCADE',
                                                  ondelete='CASCADE'),
                        index=True, nullable=False)

    # Bet details.
    guess = db.Column(db.Integer())
    die_1 = db.Column(db.Integer())
    die_2 = db.Column(db.Integer())
    roll = db.Column(db.Integer())
    wagered = db.Column(db.BigInteger())
    payout = db.Column(db.Float())
    net = db.Column(db.BigInteger())

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Bet, self).__init__(**kwargs)

    @classmethod
    def is_winner(cls, guess, roll):
        """
        Determine if the result is a win or loss.
        :param guess: Dice guess
        :type guess: int
        :param roll: Dice roll
        :type roll: int
        :return: bool
        """
        if guess == roll:
            return True

        return False

    @classmethod
    def determine_payout(cls, payout, is_winner):
        """
        Determine the payout.
        :param payout: Dice guess
        :type payout: float
        :param is_winner: Was the bet won or lost
        :type is_winner: bool
        :return: int
        """
        if is_winner:
            return payout

        return 1.0

    @classmethod
    def calculate_net(cls, wagered, payout, is_winner):
        """
        Calculate the net won or lost.
        :param wagered: Dice guess
        :type wagered: int
        :param payout: Dice roll
        :type payout: float
        :param is_winner: Was the bet won or lost
        :type is_winner: bool
        :return: int
        """
        if is_winner:
            return int(wagered * payout)

        return -wagered

    def save_and_update_user(self, user):
        """
        Commit the bet and update the user's information.
        :return: SQLAlchemy save result
        """
        self.save()

        user.coins += self.net
        user.last_bet_on = tzware_datetime()
        return user.save()

    def to_json(self):
        """
        Return JSON fields to represent a bet.
        :return: dict
        """
        params = {
            'guess': self.guess,
            'die_1': self.die_1,
            'die_2': self.die_2,
            'roll': self.roll,
            'wagered': self.wagered,
            'payout': self.payout,
            'net': self.net,
            'is_winner': Bet.is_winner(self.guess, self.roll)
        }

        return params
Пример #4
0
class Invoice(ResourceMixin, db.Model):
    __tablename__ = 'invoices'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        index=True,
                        nullable=False)

    # Invoice details.
    plan = db.Column(db.String(128), index=True)
    receipt_number = db.Column(db.String(128), index=True)
    description = db.Column(db.String(128))
    period_start_on = db.Column(db.Date)
    period_end_on = db.Column(db.Date)
    currency = db.Column(db.String(8))
    tax = db.Column(db.Integer())
    tax_percent = db.Column(db.Float())
    total = db.Column(db.Integer())

    # De-normalize the card details so we can render a user's history properly
    # even if they have no active subscription or changed cards at some point.
    brand = db.Column(db.String(32))
    last4 = db.Column(db.Integer)
    exp_date = db.Column(db.Date, index=True)

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Invoice, self).__init__(**kwargs)

    @classmethod
    def billing_history(cls, user=None):
        """
        Return the billing history for a specific user.

        :param user: User whose billing history will be retrieved
        :type user: User instance

        :return: Invoices
        """
        invoices = Invoice.query.filter(Invoice.user_id == user.id) \
            .order_by(Invoice.created_on.desc()).limit(12)

        return invoices

    @classmethod
    def parse_from_event(cls, payload):
        """
        Parse and return the invoice information that will get saved locally.
        API Documentation:
          https://stripe.com/docs/api/invoices/object

        :return: dict
        """
        data = payload['data']['object']
        plan_info = data['lines']['data'][0]['plan']

        period_start_on = datetime.datetime.utcfromtimestamp(
            data['lines']['data'][0]['period']['start']).date()
        period_end_on = datetime.datetime.utcfromtimestamp(
            data['lines']['data'][0]['period']['end']).date()

        invoice = {
            'payment_id': data['customer'],
            'plan': plan_info['name'],
            'receipt_number': data['receipt_number'],
            'description': plan_info['statement_descriptor'],
            'period_start_on': period_start_on,
            'period_end_on': period_end_on,
            'currency': data['currency'],
            'tax': data['tax'],
            'tax_percent': data['tax_percent'],
            'total': data['total']
        }

        return invoice

    @classmethod
    def parse_from_api(cls, payload):
        """
        Parse and return the invoice information we are interested in.
        API Documentation:
          https://stripe.com/docs/api/invoices/object

        :return: dict
        """
        plan_info = payload['lines']['data'][0]['plan']
        date = datetime.datetime.utcfromtimestamp(payload['created'])
        plan = plan_info['id'].upper()

        invoice = {
            'plan': plan,
            'description': '{} MONTHLY'.format(plan),
            'next_bill_on': date,
            'amount_due': payload['amount_due'],
            'interval': plan_info['interval']
        }

        return invoice

    @classmethod
    def prepare_and_save(cls, parsed_event):
        """
        Potentially save the invoice after argument the event fields.

        :param parsed_event: Event params to be saved
        :type parsed_event: dict
        :return: User instance
        """
        # Avoid circular imports.
        from snakeeyes.blueprints.user.models import User

        # Only save the invoice if the user is valid at this point.
        id = parsed_event.get('payment_id')
        user = User.query.filter((User.payment_id == id)).first()

        if user and user.credit_card:
            parsed_event['user_id'] = user.id
            parsed_event['brand'] = user.credit_card.brand
            parsed_event['last4'] = user.credit_card.last4
            parsed_event['exp_date'] = user.credit_card.exp_date

            del parsed_event['payment_id']

            invoice = Invoice(**parsed_event)
            invoice.save()

        return user

    @classmethod
    def upcoming(cls, customer_id):
        """
        Return the upcoming invoice item.

        :param customer_id: Stripe customer id
        :type customer_id: int
        :return: Stripe invoice object
        """
        invoice = PaymentInvoice.upcoming(customer_id)

        return Invoice.parse_from_api(invoice)
Пример #5
0
class Subscription(ResourceMixin, db.Model):
    __tablename__ = 'subscriptions'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        index=True,
                        nullable=False)

    # Subscription details.
    plan = db.Column(db.String(128))
    coupon = db.Column(db.String(128))

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Subscription, self).__init__(**kwargs)

    @classmethod
    def get_plan_by_id(cls, plan):
        """
        Pick the plan based on the plan identifier.

        :param plan: Plan identifier
        :type plan: str
        :return: dict or None
        """
        for key, value in settings.STRIPE_PLANS.items():
            if value.get('id') == plan:
                return settings.STRIPE_PLANS[key]

        return None

    @classmethod
    def get_new_plan(cls, keys):
        """
        Pick the plan based on the plan identifier.

        :param keys: Keys to look through
        :type keys: list
        :return: str or None
        """
        for key in keys:
            split_key = key.split('submit_')

            if isinstance(split_key, list) and len(split_key) == 2:
                if Subscription.get_plan_by_id(split_key[1]):
                    return split_key[1]

        return None

    def create(self, user=None, name=None, plan=None, coupon=None, token=None):
        """
        Create a recurring subscription.

        :param user: User to apply the subscription to
        :type user: User instance
        :param name: User's billing name
        :type name: str
        :param plan: Plan identifier
        :type plan: str
        :param coupon: Coupon code to apply
        :type coupon: str
        :param token: Token returned by JavaScript
        :type token: str
        :return: bool
        """
        if token is None:
            return False

        if coupon:
            self.coupon = coupon.upper()

        customer = PaymentCustomer.create(token=token,
                                          email=user.email,
                                          plan=plan,
                                          coupon=self.coupon)

        # Update the user account.
        user.payment_id = customer.id
        user.name = name
        user.previous_plan = plan
        user.coins = add_subscription_coins(
            user.coins, Subscription.get_plan_by_id(user.previous_plan),
            Subscription.get_plan_by_id(plan), user.cancelled_subscription_on)
        user.cancelled_subscription_on = None

        # Set the subscription details.
        self.user_id = user.id
        self.plan = plan

        # Redeem the coupon.
        if coupon:
            coupon = Coupon.query.filter(Coupon.code == self.coupon).first()
            coupon.redeem()

        # Create the credit card.
        credit_card = CreditCard(user_id=user.id,
                                 **CreditCard.extract_card_params(customer))

        db.session.add(user)
        db.session.add(credit_card)
        db.session.add(self)

        db.session.commit()

        return True

    def update(self, user=None, coupon=None, plan=None):
        """
        Update an existing subscription.

        :param user: User to apply the subscription to
        :type user: User instance
        :param coupon: Coupon code to apply
        :type coupon: str
        :param plan: Plan identifier
        :type plan: str
        :return: bool
        """
        PaymentSubscription.update(user.payment_id, coupon, plan)

        user.previous_plan = user.subscription.plan
        user.subscription.plan = plan
        user.coins = add_subscription_coins(
            user.coins, Subscription.get_plan_by_id(user.previous_plan),
            Subscription.get_plan_by_id(plan), user.cancelled_subscription_on)

        if coupon:
            user.subscription.coupon = coupon
            coupon = Coupon.query.filter(Coupon.code == coupon).first()

            if coupon:
                coupon.redeem()

        db.session.add(user.subscription)
        db.session.commit()

        return True

    def cancel(self, user=None, discard_credit_card=True):
        """
        Cancel an existing subscription.

        :param user: User to apply the subscription to
        :type user: User instance
        :param discard_credit_card: Delete the user's credit card
        :type discard_credit_card: bool
        :return: bool
        """
        PaymentSubscription.cancel(user.payment_id)

        user.payment_id = None
        user.cancelled_subscription_on = datetime.datetime.now(pytz.utc)
        user.previous_plan = user.subscription.plan

        db.session.add(user)
        db.session.delete(user.subscription)

        # Explicitly delete the credit card because the FK is on the
        # user, not subscription so we can't depend on cascading deletes.
        # This is for cases where you may want to keep a user's card
        # on file even if they cancelled.
        if discard_credit_card:
            db.session.delete(user.credit_card)

        db.session.commit()

        return True

    def update_payment_method(self,
                              user=None,
                              credit_card=None,
                              name=None,
                              token=None):
        """
        Update the subscription.

        :param user: User to modify
        :type user: User instance
        :param credit_card: Card to modify
        :type credit_card: Credit Card instance
        :param name: User's billing name
        :type name: str
        :param token: Token returned by JavaScript
        :type token: str
        :return: bool
        """
        if token is None:
            return False

        customer = PaymentCard.update(user.payment_id, token)
        user.name = name

        # Update the credit card.
        new_card = CreditCard.extract_card_params(customer)
        credit_card.brand = new_card.get('brand')
        credit_card.last4 = new_card.get('last4')
        credit_card.exp_date = new_card.get('exp_date')
        credit_card.is_expiring = new_card.get('is_expiring')

        db.session.add(user)
        db.session.add(credit_card)

        db.session.commit()

        return True
Пример #6
0
class Invoice(db.Model):
    __tablename__ = 'invoices'

    # Unique ID
    id = db.Column(db.Integer, primary_key=True)

    # Relationships
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        index=True,
                        nullable=False)

    # Invoices details
    plan = db.Column(db.String(128), index=True)
    receipt_number = db.Column(db.String(128), index=True)
    description = db.Column(db.String(128))
    period_start_on = db.Column(db.Date())
    period_end_on = db.Column(db.Date())
    currency = db.Column(db.String(12))
    tax = db.Column(db.Integer())
    tax_percent = db.Column(db.Float())
    total = db.Column(db.Integer())

    # De-normalize the card details so we can render a user's history properly even if they have no active subscription or change cards at some point

    brand = db.Column(db.String(32))
    last4 = db.Column(db.Integer)
    exp_date = db.Column(db.Date, index=True)

    # Invoice runtime
    created_on = db.Column(db.DateTime(), default=datetime.datetime.utcnow)

    def __init__(self, **kwargs):
        super(Invoice, self).__init__(**kwargs)

    @classmethod
    def billing_history(cls, user=None):
        """
        Return a billing history for a particular user
        """
        invoices = cls.query.filter(cls.user_id == user.id).order_by(
            cls.created_on.desc()).limit(12)

        return invoices

    @classmethod
    def parse_from_event():
        """
        Parse and return the invoice information that will be saved.
        :return : dict 
        """
        data = payload['data']['object']
        plan_info = data['lines']['data'][0]['plan']

        period_start_on = datetime.datetime.utcfromtimestamp(
            data['lines']['data'][0]['period']['start']).date()
        period_end_on = datetime.datetime.utcfromtimestamp(
            data['lines']['data'][0]['period']['end']).date()

        invoice = {
            'payment_id': data['customer'],
            'plan': plan_info['name'],
            'receipt_number': data['receipt_number'],
            'description': plan_info['statement_descriptor'],
            'period_start_on': period_start_on,
            'period_end_on': period_end_on,
            'currency': data['currency'],
            'tax': data['tax'],
            'tax_percent': data['tax_percent'],
            'total': data['total']
        }

        return invoice

    @classmethod
    def parse_from_api(cls, payload):
        """
        Parse and return invoice information we are interested in.
        """
        plan_info = payload['lines']['data'][0]['plan']
        date = datetime.datetime.utcfromtimestamp(payload['date'])

        invoice = {
            'plan': plan_info['name'],
            'description': plan_info['statement_descriptor'],
            'next_bill_on': date,
            'amount_due': payload['amount_due'],
            'interval': plan_info['interval']
        }

        return invoice

    @classmethod
    def prepare_and_save(cls, parsed_event):
        """
        Potentially save the invoice after argument the event fileds.
        :param parsed_event: Event params to be saved
        :type parsed_event: dict
        :return: User instance
        """
        # Avoid circular import
        from snakeeyes.blueprints.user.models import User

        id = parsed_event.get('payment_id')
        user = User.query.filter((User.payment_id == id)).first()

        if user and user.credit_card:
            parsed_event['user_id'] = user.id
            parsed_event['brand'] = user.credit_card.brand
            parsed_event['last4'] = user.credit_card.last4
            parsed_event['exp_date'] = user.credit_card.exp_date

            del parsed_event['payment_id']

            invoice = Invoice(**parsed_event)
            db.session.add(invoice)
            db.session.commit()

        return user

    @classmethod
    def upcoming(cls, customer_id):
        """
        Return the upcoming invoice
        """

        invoice = PaymentInvoice.upcoming(customer_id=customer_id)

        return Invoice.parse_from_api(invoice)
Пример #7
0
class CreditCard(ResourceMixin, db.Model):
    IS_EXPIRING_THRESHOLD_MONTHS = 2

    __tablename__ = 'credit_cards'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        index=True,
                        nullable=False)

    # Card details.
    brand = db.Column(db.String(32))
    last4 = db.Column(db.Integer)
    exp_date = db.Column(db.Date, index=True)
    is_expiring = db.Column(db.Boolean(), nullable=False, server_default='0')

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(CreditCard, self).__init__(**kwargs)

    @classmethod
    def is_expiring_soon(cls, compare_date=None, exp_date=None):
        """
        Determine whether or not this credit card is expiring soon.

        :param compare_date: Date to compare at
        :type compare_date: date
        :param exp_date: Expiration date
        :type exp_date: date
        :return: bool
        """
        return exp_date <= timedelta_months(
            CreditCard.IS_EXPIRING_THRESHOLD_MONTHS, compare_date=compare_date)

    @classmethod
    def mark_old_credit_cards(cls, compare_date=None):
        """
        Mark credit cards that are going to expire soon or have expired.

        :param compare_date: Date to compare at
        :type compare_date: date
        :return: Result of updating the records
        """
        today_with_delta = timedelta_months(
            CreditCard.IS_EXPIRING_THRESHOLD_MONTHS, compare_date)

        CreditCard.query.filter(CreditCard.exp_date <= today_with_delta) \
            .update({CreditCard.is_expiring: True})

        return db.session.commit()

    @classmethod
    def extract_card_params(cls, customer):
        """
        Extract the credit card info from a payment customer object.

        :param customer: Payment customer
        :type customer: Payment customer
        :return: dict
        """
        card_data = customer.sources.data[0]
        exp_date = datetime.date(card_data.exp_year, card_data.exp_month, 1)

        card = {
            'brand': card_data.brand,
            'last4': card_data.last4,
            'exp_date': exp_date,
            'is_expiring': CreditCard.is_expiring_soon(exp_date=exp_date)
        }

        return card
Пример #8
0
class Invoice(ResourceMixin, db.Model):
    __tablename__ = 'invoices'
    id = db.Column(db.Integer, primary_key=True)

    # Relationships.
    user_id = db.Column(db.Integer, db.ForeignKey('users.id',
                                                  onupdate='CASCADE',
                                                  ondelete='CASCADE'),
                        index=True, nullable=False)
    # Invoice details.
    plan = db.Column(db.String(128), index=True)
    receipt_number = db.Column(db.String(128), index=True)
    description = db.Column(db.String(128))
    period_start_on = db.Column(db.Date)
    period_end_on = db.Column(db.Date)
    currency = db.Column(db.String(8))
    tax = db.Column(db.Integer())
    tax_percent = db.Column(db.Float())
    total = db.Column(db.Integer())

    # De-normalize the card details so we can render a user's history properly
    # even if they have no active subscription or changed cards at some point.
    brand = db.Column(db.String(32))
    last4 = db.Column(db.Integer)
    exp_date = db.Column(db.Date, index=True)

    def __init__(self, **kwargs):
        # Call Flask-SQLAlchemy's constructor.
        super(Invoice, self).__init__(**kwargs)

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.
        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        from snakeeyes.blueprints.user.models import User

        if not query:
            return ''
        search_query = '%{0}%'.format(query)
        search_chain = (User.email.ilike(search_query),
                        User.username.ilike(search_query))
        return or_(*search_chain)

    @classmethod
    def billing_history(cls, user=None):
        """
        Return the billing history for a specific user.
        :param user: User whose billing history will be retrieved
        :type user: User instance
        :return: Invoices
        """
        invoices = Invoice.query.filter(Invoice.user_id == user.id) \
            .order_by(Invoice.created_on.desc()).limit(12)

        return invoices

    @classmethod
    def parse_from_event(cls, payload):
        """
        Parse and return the invoice information that will get saved locally.
        :return: dict
        """
        data = payload['data']['object']
        plan_info = data['lines']['data'][0]['plan']

        period_start_on = datetime.datetime.utcfromtimestamp(
            data['lines']['data'][0]['period']['start']).date()
        period_end_on = datetime.datetime.utcfromtimestamp(
            data['lines']['data'][0]['period']['end']).date()

        invoice = {
            'payment_id': data['customer'],
            'plan': plan_info['name'],
            'receipt_number': data['receipt_number'],
            'description': plan_info['statement_descriptor'],
            'period_start_on': period_start_on,
            'period_end_on': period_end_on,
            'currency': data['currency'],
            'tax': data['tax'],
            'tax_percent': data['tax_percent'],
            'total': data['total']
        }

        return invoice

    @classmethod
    def parse_from_api(cls, payload):
        """
        Parse and return the invoice information we are interested in.
        :return: dict
        """
        plan_info = payload['lines']['data'][0]['plan']
        date = datetime.datetime.utcfromtimestamp(payload['date'])

        invoice = {
            'plan': plan_info['name'],
            'description': plan_info['statement_descriptor'],
            'next_bill_on': date,
            'amount_due': payload['amount_due'],
            'interval': plan_info['interval']
        }

        return invoice

    @classmethod
    def prepare_and_save(cls, parsed_event):
        """
        Potentially save the invoice after argument the event fields.
        :param parsed_event: Event params to be saved
        :type parsed_event: dict
        :return: User instance
        """
        # Avoid circular imports.
        from snakeeyes.blueprints.user.models import User

        # Only save the invoice if the user is valid at this point.
        id = parsed_event.get('payment_id')
        user = User.query.filter((User.payment_id == id)).first()

        if user and user.credit_card:
            parsed_event['user_id'] = user.id
            parsed_event['brand'] = user.credit_card.brand
            parsed_event['last4'] = user.credit_card.last4
            parsed_event['exp_date'] = user.credit_card.exp_date

        return user

    @classmethod
    def upcoming(cls, customer_id):
        """
        Return the upcoming invoice item.
        :param customer_id: Stripe customer id
        :type customer_id: int
        :return: Stripe invoice object
        """
        invoice = PaymentInvoice.upcoming(customer_id)

        return Invoice.parse_from_api(invoice)

    def create(self, user=None, currency=None, amount=None, coins=None,
               coupon=None, token=None):
        """
        Create an invoice item.
        :param user: User to apply the subscription to
        :type user: User instance
        :param amount: Stripe currency
        :type amount: str
        :param amount: Amount in cents
        :type amount: int
        :param coins: Amount of coins
        :type coins: int
        :param coupon: Coupon code to apply
        :type coupon: str
        :param token: Token returned by JavaScript
        :type token: str
        :return: bool
        """
        if token is None:
            return False

        customer = PaymentCustomer.create(token=token, email=user.email)

        if coupon:
            self.coupon = coupon.upper()
            coupon = Coupon.query.filter(Coupon.code == self.coupon).first()
            amount = coupon.apply_discount_to(amount)

        charge = PaymentCharge.create(customer.id, currency, amount)

        # Redeem the coupon.
        if coupon:
            coupon.redeem()

        # Add the coins to the user.
        user.coins += coins

        # Create the invoice item.
        period_on = datetime.datetime.utcfromtimestamp(charge.get('created'))
        card_params = CreditCard.extract_card_params(customer)

        self.user_id = user.id
        self.plan = '&mdash;'
        self.receipt_number = charge.get('receipt_number')
        self.description = charge.get('statement_descriptor')
        self.period_start_on = period_on
        self.period_end_on = period_on
        self.currency = charge.get('currency')
        self.tax = None
        self.tax_percent = None
        self.total = charge.get('amount')
        self.brand = card_params.get('brand')
        self.last4 = card_params.get('last4')
        self.exp_date = card_params.get('exp_date')

        db.session.add(user)
        db.session.add(self)
        db.session.commit()

        return True