Exemple #1
0
def payment_intent_hook(event, **kwargs):
    # logger.info(f"[dj-stripe: hook]: PaymentIntent - {event.type}")
    if event.type == 'payment_intent.created':
        logger.info(f"[dj-stripe: hook]: {event.type} - create")
        # logger.info(f"[dj-stripe: hook]: {event.data}")
        payment_intent_stripe = event.data['object']
        PaymentIntent.sync_from_stripe_data(payment_intent_stripe)

    elif event.type == 'payment_intent.succeeded':
        logger.info(f"[dj-stripe: hook]: {event.type} - success")
        # logger.info(f"[dj-stripe: hook]: {event.data['object']}")
        try:
            customer_id = event.data['object']['customer']
            customer_qs = Customer.objects.filter(id=customer_id)
            order = Order.objects.get(customer=customer_qs[0], ordered=False)
            # STEP 3: Assign the payment to this order
            order_items = order.items.all()
            order_items.update(ordered=True)
            for item in order_items:
                item.save()
            # STEP 4: Show that order is filled
            order.ordered = True
            # order.payment = payment
            order.ref_code = order_reference()
            order.save()
        except AttributeError as e:
            logging.info(f"[dj-stripe: hook] Error: {e}")

    elif event.type == 'payment_intent.payment_failed':
        logger.info(f"[dj-stripe: hook]: {event.type} - failed")

    # payment_intent.amount_capturable_updated
    else:
        logger.info(f"[dj-stripe: hook]: {event.type} - updated")
    def test_canceled_intent(self, customer_retrieve_mock):
        fake_payment_intent = deepcopy(FAKE_PAYMENT_INTENT_I)

        fake_payment_intent["status"] = "canceled"
        fake_payment_intent["canceled_at"] = 1567524169

        for reason in (
            None,
            "duplicate",
            "fraudulent",
            "requested_by_customer",
            "abandoned",
            "failed_invoice",
            "void_invoice",
            "automatic",
        ):
            fake_payment_intent["cancellation_reason"] = reason
            payment_intent = PaymentIntent.sync_from_stripe_data(fake_payment_intent)

            if reason is None:
                # enums nulls are coerced to "" by StripeModel._stripe_object_to_record
                self.assertEqual(payment_intent.cancellation_reason, "")
            else:
                self.assertEqual(payment_intent.cancellation_reason, reason)

            # trigger model field validation (including enum value choices check)
            payment_intent.full_clean()
    def test_status_enum(
        self,
        invoice_retrieve_mock,
        customer_retrieve_mock,
        product_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
    ):
        fake_payment_intent = deepcopy(FAKE_PAYMENT_INTENT_I)

        for status in (
                "requires_payment_method",
                "requires_confirmation",
                "requires_action",
                "processing",
                "requires_capture",
                "canceled",
                "succeeded",
        ):
            fake_payment_intent["status"] = status
            payment_intent = PaymentIntent.sync_from_stripe_data(
                fake_payment_intent)

            # trigger model field validation (including enum value choices check)
            payment_intent.full_clean()
Exemple #4
0
def remove_single_item_from_cart(request, pk):
    project = get_object_or_404(NewProject, pk=pk)
    customer_query_set = Customer.objects.filter(subscriber=request.user)
    order_query_set = Order.objects.filter(customer=customer_query_set[0], ordered=False)
    if order_query_set.exists():
        order = order_query_set[0]
        # check if order_item is already in the order
        if order.items.filter(item__id=project.id).exists():
            order_item = OrderItem.objects.filter(
                item=project,
                user=request.user,
                ordered=False
            )[0]
            if order_item.quantity > 1:
                order_item.quantity -= 1
                order_item.save()
            else:
                # Identify active PaymentIntent and cancel
                customer_qs = Customer.objects.filter(subscriber=request.user)
                payment_intent_qs = PaymentIntent.objects.filter(customer=customer_qs[0]).exclude(
                    status='succeeded').exclude(status='canceled')
                if payment_intent_qs.exists():
                    # logging.info(f"['remove_single_item_from_cart'] select PaymentIntent to cancel: {payment_intent_qs}")
                    payment_intent = payment_intent_qs.get(customer=customer_qs[0])
                    stripe_data = stripe.PaymentIntent.cancel(payment_intent.id,)
                    PaymentIntent.sync_from_stripe_data(stripe_data)
                # Remove OrderItem object locally
                OrderItem.objects.filter(
                    item=project,
                    user=request.user,
                    ordered=False
                )[0].delete()
            messages.info(request, "The item quantity was updated.")
            return redirect('order:order-summary')
        else:
            messages.info(request, "This item is not in your cart, yet.")
            return redirect('newproject-detail', pk)
    else:
        messages.info(request, "You do not have an order, yet.")
        return redirect('newproject-detail', pk)
    def test_sync_from_stripe_data(self, customer_retrieve_mock):
        fake_payment_intent = deepcopy(FAKE_PAYMENT_INTENT_I)

        payment_intent = PaymentIntent.sync_from_stripe_data(fake_payment_intent)

        self.assert_fks(
            payment_intent,
            expected_blank_fks={
                "djstripe.Customer.coupon",
                "djstripe.Customer.subscriber",
                "djstripe.PaymentIntent.on_behalf_of",
                "djstripe.PaymentIntent.payment_method",
            },
        )
    def test_status_enum(self, customer_retrieve_mock):
        fake_payment_intent = deepcopy(FAKE_PAYMENT_INTENT_I)

        payment_intent = PaymentIntent.sync_from_stripe_data(fake_payment_intent)

        for status in (
            "requires_payment_method",
            "requires_confirmation",
            "requires_action",
            "processing",
            "requires_capture",
            "canceled",
            "succeeded",
        ):
            payment_intent.status = status
            payment_intent.full_clean()
            payment_intent.save()
    def test___str__(self, fake_intent_data, has_account, has_customer, monkeypatch):
        def mock_customer_get(*args, **kwargs):
            return deepcopy(FAKE_CUSTOMER)

        def mock_account_get(*args, **kwargs):
            data = deepcopy(FAKE_ACCOUNT)
            # Otherwise Account.api_retrieve will invoke File.api_retrieve...
            data["settings"]["branding"] = {}
            return data

        def mock_payment_method_get(*args, **kwargs):
            return deepcopy(FAKE_PAYMENT_METHOD_I)

        # monkeypatch stripe.Product.retrieve, stripe.Price.retrieve, and  stripe.PaymentMethod.retrieve calls to return
        # the desired json response.
        monkeypatch.setattr(stripe.Account, "retrieve", mock_account_get)
        monkeypatch.setattr(stripe.Customer, "retrieve", mock_customer_get)
        monkeypatch.setattr(stripe.PaymentMethod, "retrieve", mock_payment_method_get)

        pi = PaymentIntent.sync_from_stripe_data(fake_intent_data)
        account = Account.objects.filter(id=fake_intent_data["on_behalf_of"]).first()
        customer = Customer.objects.filter(id=fake_intent_data["customer"]).first()

        if has_account and has_customer:

            assert (
                f"{pi.human_readable_amount} ({PaymentIntentStatus.humanize(fake_intent_data['status'])}) "
                f"for {account} "
                f"by {customer}"
            ) == str(pi)

        elif has_account and not has_customer:

            assert (
                f"{pi.human_readable_amount} for {account}. {PaymentIntentStatus.humanize(fake_intent_data['status'])}"
            ) == str(pi)

        elif has_customer and not has_account:

            assert (
                f"{pi.human_readable_amount} by {customer}. {PaymentIntentStatus.humanize(fake_intent_data['status'])}"
            ) == str(pi)
        elif not has_customer and not has_account:
            f"{pi.human_readable_amount} ({PaymentIntentStatus.humanize(fake_intent_data['status'])})" == str(
                pi
            )
    def test_canceled_intent(self, customer_retrieve_mock):
        fake_payment_intent = deepcopy(FAKE_PAYMENT_INTENT_I)

        fake_payment_intent["status"] = "canceled"
        fake_payment_intent["canceled_at"] = 1567524169

        for reason in (
            "duplicate",
            "fraudulent",
            "requested_by_customer",
            "abandoned",
            "failed_invoice",
            "void_invoice",
            "automatic",
        ):
            fake_payment_intent["cancellation_reason"] = reason
            payment_intent = PaymentIntent.sync_from_stripe_data(fake_payment_intent)
            payment_intent.full_clean()
            payment_intent.save()
    def test_sync_from_stripe_data(
        self,
        invoice_retrieve_mock,
        customer_retrieve_mock,
        product_retrieve_mock,
        paymentmethod_card_retrieve_mock,
        charge_retrieve_mock,
        subscription_retrieve_mock,
        balance_transaction_retrieve_mock,
    ):

        payment_intent = PaymentIntent.sync_from_stripe_data(
            deepcopy(FAKE_PAYMENT_INTENT_I))

        self.assert_fks(
            payment_intent,
            expected_blank_fks={
                "djstripe.Charge.latest_upcominginvoice (related name)",
                "djstripe.Charge.application_fee",
                "djstripe.Charge.dispute",
                "djstripe.Charge.on_behalf_of",
                "djstripe.Charge.source_transfer",
                "djstripe.Charge.transfer",
                "djstripe.Customer.coupon",
                "djstripe.Customer.default_payment_method",
                "djstripe.Customer.subscriber",
                "djstripe.Invoice.default_payment_method",
                "djstripe.Invoice.default_source",
                "djstripe.PaymentIntent.on_behalf_of",
                "djstripe.PaymentIntent.payment_method",
                "djstripe.PaymentIntent.upcominginvoice (related name)",
                "djstripe.Subscription.default_payment_method",
                "djstripe.Subscription.default_source",
                "djstripe.Subscription.pending_setup_intent",
                "djstripe.Subscription.schedule",
            },
        )

        self.assertIsNotNone(payment_intent.invoice)
    def test___str__(self, fake_intent_data, has_account, has_customer,
                     monkeypatch):
        def mock_customer_get(*args, **kwargs):
            """Monkeypatched stripe.Customer.retrieve"""
            return deepcopy(FAKE_CUSTOMER)

        def mock_account_get(*args, **kwargs):
            """Monkeypatched stripe.Account.retrieve"""
            data = deepcopy(FAKE_ACCOUNT)
            # Otherwise Account.api_retrieve will invoke File.api_retrieve...
            data["settings"]["branding"] = {}
            return data

        def mock_payment_method_get(*args, **kwargs):
            """Monkeypatched stripe.PaymentMethod.retrieve"""
            return deepcopy(FAKE_PAYMENT_METHOD_I)

        def mock_invoice_get(*args, **kwargs):
            """Monkeypatched stripe.Invoice.retrieve"""
            return deepcopy(FAKE_INVOICE)

        def mock_subscription_get(*args, **kwargs):
            """Monkeypatched stripe.Subscription.retrieve"""
            return deepcopy(FAKE_SUBSCRIPTION)

        def mock_balance_transaction_get(*args, **kwargs):
            """Monkeypatched stripe.BalanceTransaction.retrieve"""
            return deepcopy(FAKE_BALANCE_TRANSACTION)

        def mock_product_get(*args, **kwargs):
            """Monkeypatched stripe.Product.retrieve"""
            return deepcopy(FAKE_PRODUCT)

        def mock_charge_get(*args, **kwargs):
            """Monkeypatched stripe.Charge.retrieve"""
            return deepcopy(FAKE_CHARGE)

        # monkeypatch stripe.Product.retrieve, stripe.Price.retrieve, stripe.PaymentMethod.retrieve, and stripe.PaymentIntent.retrieve calls to return
        # the desired json response.
        monkeypatch.setattr(stripe.Account, "retrieve", mock_account_get)
        monkeypatch.setattr(stripe.Customer, "retrieve", mock_customer_get)
        monkeypatch.setattr(stripe.PaymentMethod, "retrieve",
                            mock_payment_method_get)

        # because of Reverse o2o field sync due to PaymentIntent.sync_from_stripe_data..
        monkeypatch.setattr(stripe.Invoice, "retrieve", mock_invoice_get)
        monkeypatch.setattr(stripe.Subscription, "retrieve",
                            mock_subscription_get)
        monkeypatch.setattr(stripe.BalanceTransaction, "retrieve",
                            mock_balance_transaction_get)
        monkeypatch.setattr(stripe.Product, "retrieve", mock_product_get)
        monkeypatch.setattr(stripe.Charge, "retrieve", mock_charge_get)

        pi = PaymentIntent.sync_from_stripe_data(fake_intent_data)

        # due to reverse o2o sync invoice should also get created
        if fake_intent_data.get("invoice"):
            assert pi.invoice is not None

        if has_account and has_customer:

            assert (
                str(pi) ==
                "$1902.00 USD (The funds are in your account.) for dj-stripe by Michael Smith"
            )

        elif has_account and not has_customer:

            assert (
                str(pi)
            ) == "$1902.00 USD for dj-stripe. The funds are in your account."

        elif has_customer and not has_account:

            assert (
                str(pi)
            ) == "$20.00 USD by Michael Smith. The funds are in your account."
        elif not has_customer and not has_account:

            assert str(pi) == "$20.00 USD (The funds are in your account.)"