コード例 #1
0
ファイル: payments_tests.py プロジェクト: vosechu/mltshp
    def test_cancel_handler_clears_paid_status(self):
        self.user.is_paid = 1
        self.user.stripe_customer_id = "fake"
        self.user.save()

        pl = PaymentLog(processor=PaymentLog.STRIPE,
                        user_id=self.user.id,
                        status="payment",
                        subscription_id="fake-sub",
                        transaction_serial_number=1)
        pl.save()

        # stub out the underlying stripe things
        fake_plan = Mock()
        fake_plan.id = options.stripe_annual_plan_id

        fake_subscription = Mock()
        fake_subscription.id = "fake-sub"
        fake_subscription.status = "active"
        fake_subscription.plan = fake_plan
        fake_subscription.current_period_start = 1489464525
        fake_subscription.current_period_end = 1521000525
        fake_subscription.delete.return_value = None

        customer = stripe.Customer(id=self.user.stripe_customer_id)
        customer.subscriptions = Mock()
        customer.subscriptions.data = [fake_subscription]
        customer.subscriptions.retrieve.return_value = fake_subscription

        with patch("stripe.Customer") as CustomerMock:
            CustomerMock.retrieve.return_value = customer
            response = self.post_url("/account/payment/cancel", {})

        user = User.get(self.user.id)
        self.assertEqual(user.is_paid, 0)
コード例 #2
0
ファイル: voucher_tests.py プロジェクト: rey/mltshp
    def test_redeem_voucher_with_good_voucher(self):
        self.sign_out()
        user = test.factories.user(name="free_user",
                                   email="*****@*****.**")
        user.is_paid = 0
        user.save()
        self.sign_in(user.name, "password")
        response = self.fetch_url("/account/settings")
        # verify this account is currently free
        self.assertTrue(
            response.body.find("You are currently using a free account.") > -1)

        arguments = {"key": "unclaimed"}
        # this will post and redirect to the settings page which should
        # then reflect that we are a paid user with 5 years of credit
        response = self.post_url("/account/redeem", arguments)
        self.assertTrue(response.body.find("5 Years") > -1)

        payments = PaymentLog.where("user_id=%s", user.id)
        self.assertEquals(len(payments), 1)
        self.assertEquals(payments[0].operation, "redeem")
        self.assertEquals(payments[0].status, "credit")
        self.assertEquals(payments[0].reference_id, str(self.promotion.id))
        self.assertEquals(payments[0].transaction_id, arguments['key'])
        self.assertEquals(payments[0].buyer_email, user.email)
        self.assertEquals(payments[0].buyer_name, user.name)
        # self.assertEquals(payments[0].next_transaction_date, )

        voucher = Voucher.get("claimed_by_user_id=%s", user.id)
        self.assertEquals(voucher.promotion_id, self.promotion.id)
        self.assertEquals(voucher.claimed_by_user_id, user.id)
        self.assertEquals(voucher.offered_by_user_id, self.admin.id)
コード例 #3
0
    def get(self):
        user = self.get_current_user_object()
        payments = []
        if user.is_paid:
            payments = PaymentLog.last_payments(count=3, user_id=user.id)

        already_requested = self.get_secure_cookie("image_request")

        promotions = Promotion.active()

        return self.render("account/settings.html",
                           user=user,
                           payments=payments,
                           already_requested=already_requested,
                           promotions=promotions)
コード例 #4
0
    def get(self):
        user = self.get_current_user_object()
        payments = []
        if user.is_paid:
            payments = PaymentLog.last_payments(count=3, user_id=user.id)

        already_requested = self.get_secure_cookie("image_request")
        cancel_flag = "canceled" in (user.stripe_plan_id or "")
        migrated_flag = self.get_argument('migrated', 0)

        promotions = Promotion.active()

        has_data_to_migrate = not MigrationState.has_migrated(user.id)

        return self.render("account/settings.html",
                           user=user,
                           payments=payments,
                           already_requested=already_requested,
                           promotions=promotions,
                           plan_name=plan_name(user.stripe_plan_id),
                           has_data_to_migrate=has_data_to_migrate,
                           migrated_flag=migrated_flag,
                           cancel_flag=cancel_flag)
コード例 #5
0
ファイル: voucher.py プロジェクト: vosechu/mltshp
    def apply_to_user(self, user_):
        from models import PaymentLog

        promotion = self.get_promotion()

        self.offered_by_user_id = 0

        if not self.offered_by_user_id and promotion is not None:
            shake = promotion.shake()
            if shake is not None:
                # if this promotion is related to a shake
                # associate the voucher with that shake's owner
                self.offered_by_user_id = shake.user_id

        now = datetime.datetime.utcnow()

        # if the user has a voucher, then we need
        # to apply a credit to their account using
        # payment_log in addition to creating the
        # voucher record and claiming it.
        self.claimed_by_user_id = user_.id
        self.claimed_at = now.strftime("%Y-%m-%d %H:%M:%S")
        self.save()

        # now record to payment_log that this
        # user has claimed a voucher.
        next_date = None
        amount = None

        promotion = self.get_promotion()
        # make a sensible "transaction amount" description
        # that we use for the settings screen
        if promotion is not None \
            and promotion.membership_months > 0:
            months = promotion.membership_months
            days = int((365 * (months/12.0)) + 0.5)
            next_date = now + datetime.timedelta(days=days)
            if months >= 12 and months % 12 == 0:
                years = months / 12
                if years == 1:
                    amount = '1 Year'
                elif years > 1:
                    amount = '%d Years' % years
            elif months == 1:
                amount = '1 Month'
            elif months > 1:
                amount = '%d Months' % months

            pl = PaymentLog(
                user_id                   = user_.id,
                status                    = "credit",
                reference_id              = promotion.id,
                transaction_id            = self.voucher_key,
                operation                 = "redeem",
                transaction_date          = now.strftime("%Y-%m-%d %H:%M:%S"),
                next_transaction_date     = next_date.strftime("%Y-%m-%d %H:%M:%S"),
                buyer_email               = user_.email,
                buyer_name                = (user_.full_name or user_.name),
                transaction_amount        = amount,
                payment_reason            = "MLTSHP Paid Account",
                payment_method            = "voucher",
                processor                 = PaymentLog.VOUCHER,
                transaction_serial_number = 0
            )
            pl.save()

            # update user paid status if necessary
            if user_.is_paid != 1:
                user_.is_paid = 1
                user_.save()
                payment_notifications(user_, "redeemed", amount)
コード例 #6
0
    def post(self):
        current_user = self.get_current_user_object()

        token_id = None
        if current_user.stripe_customer_id is None:
            token_id = self.get_argument("token")
            if token_id is None:
                # invalid request
                raise Exception("Invalid request")

        plan_id = self.get_argument("plan_id")
        if plan_id not in ("mltshp-single", "mltshp-double"):
            raise Exception("Invalid request")

        quantity = 1
        if plan_id == "mltshp-double":
            quantity = int(float(self.get_argument("quantity")))
            if quantity < 24 or quantity > 500:
                raise "Invalid request"

        customer = None
        sub = None
        stripe_customer_id = current_user.stripe_customer_id
        if stripe_customer_id is not None:
            try:
                customer = stripe.Customer.retrieve(stripe_customer_id)
            except stripe.error.InvalidRequestError:
                pass
            if customer and getattr(customer, "deleted", False):
                customer = None

        try:
            if customer is None:
                if token_id is None:
                    # FIXME: handle this more gracefully...
                    raise "Invalid request"

                # create a new customer object for this subscription
                customer = stripe.Customer.create(
                    description="MLTSHP user %s" % current_user.name,
                    email=current_user.email,
                    metadata={"mltshp_user": current_user.name},
                    source=token_id,
                )

            # if this works, we should have a customer with 1 subscription, this one
            if customer.subscriptions.total_count > 0:
                sub = customer.subscriptions.data[0]
                if sub.plan != plan_id:
                    sub.plan = plan_id
                    if plan_id == "mltshp-double":
                        sub.quantity = quantity
                    else:
                        sub.quantity = 1
                    sub.save()
            else:
                if plan_id == "mltshp-double":
                    sub = customer.subscriptions.create(plan=plan_id,
                                                        quantity=quantity)
                else:
                    sub = customer.subscriptions.create(plan=plan_id)
        except stripe.error.CardError as ex:
            return self.render("account/return-subscription-completed.html",
                               error=unicode(ex),
                               has_data_to_migrate=False)

        if not sub:
            raise Exception("Error issuing subscription")

        amount = "USD %0.2f" % ((sub.plan.amount / 100.0) * quantity)
        payment_log = PaymentLog(
            processor=PaymentLog.STRIPE,
            user_id=current_user.id,
            status="subscribed",
            reference_id=sub.id,  # ??
            transaction_id=sub.id,  # ??
            operation="order",
            transaction_date=datetime.datetime.fromtimestamp(
                sub.current_period_start).strftime("%Y-%m-%d %H:%M:%S"),
            next_transaction_date=datetime.datetime.fromtimestamp(
                sub.current_period_end).strftime("%Y-%m-%d %H:%M:%S"),
            buyer_email=current_user.email,
            buyer_name=current_user.display_name(),
            recipient_email="*****@*****.**",
            recipient_name="MLTSHP, Inc.",
            payment_reason="MLTSHP Membership",
            transaction_serial_number=1,
            subscription_id=sub.id,
            payment_method="CC",
            transaction_amount=amount,
        )
        payment_log.save()
        current_user.is_paid = 1
        current_user.stripe_plan_id = plan_id
        if plan_id == "mltshp-double":
            current_user.stripe_plan_rate = quantity
        else:
            current_user.stripe_plan_rate = None

        if current_user.stripe_customer_id != customer.id:
            current_user.stripe_customer_id = customer.id

        current_user.save()

        if options.postmark_api_key:
            pm = postmark.PMMail(
                api_key=options.postmark_api_key,
                sender="*****@*****.**",
                to="*****@*****.**",
                subject="%s has created a subscription" %
                (payment_log.buyer_name),
                text_body=
                "Subscription ID: %s\nBuyer Name:%s\nBuyer Email:%s\nUser ID:%s\n"
                % (payment_log.subscription_id, payment_log.buyer_name,
                   payment_log.buyer_email, current_user.id))
            pm.send()

        payment_notifications(current_user, "subscription", amount)

        has_data_to_migrate = not MigrationState.has_migrated(current_user.id)

        return self.render("account/return-subscription-completed.html",
                           has_data_to_migrate=has_data_to_migrate)
コード例 #7
0
    def post(self):
        current_user = self.get_current_user_object()
        # an existing, paying user can't subscribe again...
        # we also won't allow users with unconfirmed emails to subscribe
        # should we message here?
        if current_user.is_paid == 1 or current_user.email_confirmed != 1:
            return self.redirect("/account/settings")

        token_id = self.get_argument("token")
        if token_id is None:
            # invalid request
            raise "Invalid request"

        customer = None
        sub = None
        stripe_customer_id = current_user.stripe_customer_id
        if stripe_customer_id is not None:
            try:
                customer = stripe.Customer.retrieve(stripe_customer_id)
            except stripe.error.InvalidRequestError:
                pass
            if customer and getattr(customer, "deleted", False):
                customer = None

        try:
            if customer is None:
                # create a new customer object for this subscription
                customer = stripe.Customer.create(
                    description="MLTSHP user %s" % current_user.name,
                    email=current_user.email,
                    plan=options.stripe_annual_plan_id,
                    card=token_id,  # obtained with Checkout.js
                    metadata={"mltshp_user": current_user.name},
                )
                # if this works, we should have a customer with 1 subscription, this one
                if customer.subscriptions.total_count > 0:
                    sub = customer.subscriptions.data[0]
            else:
                sub = customer.subscriptions.create(
                    plan=options.stripe_annual_plan_id, card=token_id)
        except stripe.error.CardError as ex:
            return self.render("account/return-subscription-completed.html",
                               error=unicode(ex))

        if not sub:
            raise Exception("Error issuing subscription")

        amount = "USD %0.2f" % (sub.plan.amount / 100.0)
        payment_log = PaymentLog(
            processor=PaymentLog.STRIPE,
            user_id=current_user.id,
            status="subscribed",
            reference_id=sub.id,  # ??
            transaction_id=sub.id,  # ??
            operation="order",
            transaction_date=datetime.datetime.fromtimestamp(
                sub.current_period_start).strftime("%Y-%m-%d %H:%M:%S"),
            next_transaction_date=datetime.datetime.fromtimestamp(
                sub.current_period_end).strftime("%Y-%m-%d %H:%M:%S"),
            buyer_email=current_user.email,
            buyer_name=current_user.display_name(),
            recipient_email="*****@*****.**",
            recipient_name="MLTSHP, LLC",
            payment_reason="MLTSHP Paid Account",
            transaction_serial_number=1,
            subscription_id=sub.id,
            payment_method="CC",
            transaction_amount=amount,
        )
        payment_log.save()
        current_user.is_paid = 1

        if current_user.stripe_customer_id != customer.id:
            current_user.stripe_customer_id = customer.id

        current_user.save()

        if options.postmark_api_key:
            pm = postmark.PMMail(
                api_key=options.postmark_api_key,
                sender="*****@*****.**",
                to="*****@*****.**",
                subject="%s has created a subscription" %
                (payment_log.buyer_name),
                text_body=
                "Subscription ID: %s\nBuyer Name:%s\nBuyer Email:%s\nUser ID:%s\n"
                % (payment_log.subscription_id, payment_log.buyer_name,
                   payment_log.buyer_email, current_user.id))
            pm.send()

        payment_notifications(current_user, "subscription", amount)

        return self.render("account/return-subscription-completed.html")
コード例 #8
0
ファイル: stripe_hooks.py プロジェクト: vosechu/mltshp
    def post(self):
        # type of message is passed through "type" parameter
        json_response = json.loads(self.request.body)
        body_str = json.dumps(json_response).replace("\n", "\\n")

        stripe_customer_id = None
        period_start = None
        period_end = None
        checkout_id = None
        status = None
        subscription_id = None
        charge_id = None
        amount = 0
        operation = None

        evt = stripe.convert_to_stripe_object(json_response,
                                              options.stripe_secret_key, None)

        if evt.type == 'invoice.payment_failed':
            # subscription failed to be paid due to a problem
            # with the member's credit card or something
            # for now, just email [email protected] about this
            stripe_customer_id = evt.data.object.customer

            subscriber = User.get("stripe_customer_id=%s and deleted=0",
                                  stripe_customer_id)

            if subscriber and options.postmark_api_key:
                pm = postmark.PMMail(
                    api_key=options.postmark_api_key,
                    sender="*****@*****.**",
                    to="*****@*****.**",
                    subject="%s has a subscription failure" %
                    (subscriber.display_name()),
                    text_body=
                    "Subscription ID: %s\nBuyer Name:%s\nBuyer Email:%s\nUser ID:%s\n"
                    % (subscription_id, subscriber.display_name(),
                       subscriber.email, subscriber.id))
                pm.send()

            return self.finish("OK")

        elif evt.type == 'customer.subscription.created':
            # important properties
            #   customer - should be recorded already in account.stripe_customer_id
            #   current_period_start
            #   current_period_end
            #   plan.id (mltshp-annual)
            stripe_customer_id = evt.data.object.customer
            period_start = evt.data.object.current_period_start
            period_end = evt.data.object.current_period_end
            checkout_id = evt.data.object.id
            status = "subscribed"  # evt.type
            operation = "subscribe"
            subscription_id = evt.data.object.id
            amount = evt.data.object.plan.amount

        elif evt.type == "customer.subscription.deleted":
            stripe_customer_id = evt.data.object.customer
            period_start = evt.data.object.current_period_start
            period_end = evt.data.object.current_period_end
            status = "canceled"  # evt.type
            operation = "cancel"
            subscription_id = evt.data.object.id

        elif evt.type == 'invoice.payment_succeeded':
            #   customer
            #   date
            #   lines.subscriptions[0].plan.id
            #       period.start
            #       period.end
            #   total
            line_items = [
                item for item in evt.data.object.lines.data
                if item.type == "subscription" and item.plan.id in
                (options.stripe_annual_plan_id, options.stripe_monthly_plan_id)
            ]
            if line_items:
                line_item = line_items[0]
                stripe_customer_id = evt.data.object.customer
                period_start = line_item.period.start
                period_end = line_item.period.end
                checkout_id = evt.data.object.id
                status = "payment"  # evt.type
                operation = "pay"
                subscription_id = evt.data.object.subscription
                charge_id = evt.data.object.charge
                amount = evt.data.object.total

        else:
            # unsupported event type; just ignore it
            return self.finish("OK")

        subscriber = None
        if stripe_customer_id:
            subscriber = User.get("stripe_customer_id=%s and deleted=0",
                                  stripe_customer_id)

        if subscriber is None:
            # raise an exception for this...
            #raise Exception("failed to locate user for stripe_customer_id %s"
            #    % stripe_customer_id)
            return self.finish("OK")

        #create a payment log record
        amount = "USD %0.2f" % (amount / 100.0)
        pl = PaymentLog(user_id=subscriber.id,
                        status=status,
                        reference_id=checkout_id,
                        transaction_id=charge_id,
                        operation=operation,
                        transaction_date=datetime.datetime.fromtimestamp(
                            period_start).strftime("%Y-%m-%d %H:%M:%S"),
                        next_transaction_date=datetime.datetime.fromtimestamp(
                            period_end).strftime("%Y-%m-%d %H:%M:%S"),
                        buyer_email=subscriber.email,
                        buyer_name=subscriber.display_name(),
                        recipient_email="*****@*****.**",
                        recipient_name="MLTSHP, LLC",
                        payment_reason="MLTSHP Paid Account",
                        transaction_serial_number=1,
                        subscription_id=subscription_id,
                        payment_method='CC',
                        transaction_amount=amount,
                        processor=PaymentLog.STRIPE)
        pl.save()

        if evt.type == "customer.subscription.deleted":
            subscriber.is_paid = 0
        else:
            subscriber.is_paid = 1
        subscriber.save()

        return self.finish("OK")
コード例 #9
0
    def get(self):
        user = self.get_current_user_object()
        payments = []
        if user.is_paid:
            payments = []
            if user.stripe_customer_id:
                customer_id = user.stripe_customer_id
                charges = stripe.Charge.list(limit=5, customer=customer_id)
                for charge in charges.data:
                    payments.append({
                        "transaction_amount":
                        "USD %0.2f" % (charge.amount / 100.0, ),
                        "refund_amount":
                        charge.refunded and "USD %0.2f" %
                        (charge.amount_refunded / 100.0, ) or "",
                        "created_at":
                        datetime.datetime.fromtimestamp(charge.created),
                        "status":
                        "charged",
                        "is_pending":
                        charge.status == "pending",
                        "is_failed":
                        charge.status == "failed",
                        "is_success":
                        charge.status == "succeeded",
                        "is_refund":
                        charge.refunded,
                    })
            else:
                log = PaymentLog.last_payments(count=5, user_id=user.id)
                for payment in log:
                    payments.append({
                        "transaction_amount": payment.transaction_amount,
                        "refund_amount": "",
                        "created_at": payment.created_at,
                        "status": payment.status,
                        "is_pending": False,
                        "is_success": True,
                        "is_failed": False,
                        "is_refund": False,
                    })

        already_requested = self.get_secure_cookie("image_request")
        cancel_flag = "canceled" in (user.stripe_plan_id or "")
        updated_flag = self.get_argument("update", "") == "1"
        migrated_flag = self.get_argument("migrated", 0)
        past_due = False
        source_card_type = None
        source_last_4 = None
        source_expiration = None

        promotions = Promotion.active()

        has_data_to_migrate = not MigrationState.has_migrated(user.id)

        if user.stripe_customer_id:
            customer = None
            try:
                customer = stripe.Customer.retrieve(user.stripe_customer_id)
            except stripe.error.InvalidRequestError:
                pass
            if customer and not hasattr(customer, 'deleted'):
                if customer.subscriptions.total_count >= 1:
                    subscriptions = [
                        sub for sub in customer.subscriptions.data
                        if sub.plan.id == user.stripe_plan_id
                    ]
                    if subscriptions:
                        subscription = subscriptions[0]
                        past_due = subscription.status == "past_due"
                        if customer.sources.total_count > 0:
                            if customer.sources.data[0].object == "card":
                                card = customer.sources.data[0]
                            elif customer.sources.data[0].object == "source":
                                card = customer.sources.data[0].card
                            source_card_type = card.brand
                            source_last_4 = card.last4
                            source_expiration = "%d/%d" % (card.exp_month,
                                                           card.exp_year)

        return self.render("account/settings.html",
                           user=user,
                           payments=payments,
                           already_requested=already_requested,
                           promotions=promotions,
                           plan_name=plan_name(user.stripe_plan_id),
                           past_due=past_due,
                           stripe_public_key=options.stripe_public_key,
                           source_card_type=source_card_type,
                           source_last_4=source_last_4,
                           source_expiration=source_expiration,
                           has_data_to_migrate=has_data_to_migrate,
                           updated_flag=updated_flag,
                           migrated_flag=migrated_flag,
                           cancel_flag=cancel_flag)