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()
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.)"