Пример #1
0
def handle_payment_intent_payment_failed_event(
        stripe_payment_intent: stripe.PaymentIntent,
        payment_intent: Event) -> None:
    payment_intent.status = PaymentIntent.get_status_integer_from_status_text(
        stripe_payment_intent.status)
    billing_logger.info(
        "Stripe payment intent failed: %s %s %s %s",
        payment_intent.customer.realm.string_id,
        stripe_payment_intent.last_payment_error.get("type"),
        stripe_payment_intent.last_payment_error.get("code"),
        stripe_payment_intent.last_payment_error.get("param"),
    )
    payment_intent.last_payment_error = {
        "description": stripe_payment_intent.last_payment_error.get("type"),
    }
    payment_intent.last_payment_error[
        "message"] = stripe_payment_intent.last_payment_error.get("message")
    payment_intent.save(update_fields=["status", "last_payment_error"])
Пример #2
0
def stripe_webhook(request: HttpRequest) -> HttpResponse:
    stripe_webhook_endpoint_secret = get_secret(
        "stripe_webhook_endpoint_secret", "")
    if (
            stripe_webhook_endpoint_secret and not settings.TEST_SUITE
    ):  # nocoverage: We can't verify the signature in test suite since we fetch the events
        # from Stripe events API and manually post to the webhook endpoint.
        try:
            stripe_event = stripe.Webhook.construct_event(
                request.body,
                request.META.get("HTTP_STRIPE_SIGNATURE"),
                stripe_webhook_endpoint_secret,
            )
        except ValueError:
            return HttpResponse(status=400)
        except stripe.error.SignatureVerificationError:
            return HttpResponse(status=400)
    else:
        assert not settings.PRODUCTION
        try:
            stripe_event = stripe.Event.construct_from(
                json.loads(request.body), stripe.api_key)
        except Exception:
            return HttpResponse(status=400)

    if stripe_event.api_version != STRIPE_API_VERSION:
        error_message = f"Mismatch between billing system Stripe API version({STRIPE_API_VERSION}) and Stripe webhook event API version({stripe_event.api_version})."
        billing_logger.error(error_message)
        return HttpResponse(status=400)

    if stripe_event.type not in [
            "checkout.session.completed",
            "payment_intent.succeeded",
            "payment_intent.payment_failed",
    ]:
        return HttpResponse(status=200)

    if Event.objects.filter(stripe_event_id=stripe_event.id).exists():
        return HttpResponse(status=200)

    event = Event(stripe_event_id=stripe_event.id, type=stripe_event.type)

    if stripe_event.type == "checkout.session.completed":
        stripe_session = stripe_event.data.object
        assert isinstance(stripe_session, stripe.checkout.Session)
        try:
            session = Session.objects.get(stripe_session_id=stripe_session.id)
        except Session.DoesNotExist:
            return HttpResponse(status=200)
        event.content_type = ContentType.objects.get_for_model(Session)
        event.object_id = session.id
        event.save()
        handle_checkout_session_completed_event(stripe_session, event)
    elif stripe_event.type == "payment_intent.succeeded":
        stripe_payment_intent = stripe_event.data.object
        assert isinstance(stripe_payment_intent, stripe.PaymentIntent)
        try:
            payment_intent = PaymentIntent.objects.get(
                stripe_payment_intent_id=stripe_payment_intent.id)
        except PaymentIntent.DoesNotExist:
            # PaymentIntent that was not manually created from the billing system.
            # Could be an Invoice getting paid which is is not an event we are interested in.
            return HttpResponse(status=200)
        event.content_type = ContentType.objects.get_for_model(PaymentIntent)
        event.object_id = payment_intent.id
        event.save()
        handle_payment_intent_succeeded_event(stripe_payment_intent, event)
    elif stripe_event.type == "payment_intent.payment_failed":
        stripe_payment_intent = stripe_event.data.object
        try:
            assert isinstance(stripe_payment_intent, stripe.PaymentIntent)
            payment_intent = PaymentIntent.objects.get(
                stripe_payment_intent_id=stripe_payment_intent.id)
        except PaymentIntent.DoesNotExist:
            # PaymentIntent that was not manually created from the billing system.
            # Could be an Invoice getting paid which is is not an event we are interested in.
            return HttpResponse(status=200)
        event.content_type = ContentType.objects.get_for_model(PaymentIntent)
        event.object_id = payment_intent.id
        event.save()
        handle_payment_intent_payment_failed_event(stripe_payment_intent,
                                                   event)
    return HttpResponse(status=200)
Пример #3
0
    def wrapper(stripe_object: Union[stripe.checkout.Session,
                                     stripe.PaymentIntent],
                event: Event) -> None:
        event.status = Event.EVENT_HANDLER_STARTED
        event.save(update_fields=["status"])

        try:
            func(stripe_object, event.content_object)
        except BillingError as e:
            billing_logger.warning(
                "BillingError in %s event handler: %s. stripe_object_id=%s, customer_id=%s metadata=%s",
                event.type,
                e.error_description,
                stripe_object.id,
                stripe_object.customer,
                stripe_object.metadata,
            )
            event.status = Event.EVENT_HANDLER_FAILED
            event.handler_error = {
                "message": e.msg,
                "description": e.error_description,
            }
            event.save(update_fields=["status", "handler_error"])
        except Exception:
            billing_logger.exception(
                "Uncaught exception in %s event handler:",
                event.type,
                stack_info=True,
            )
            event.status = Event.EVENT_HANDLER_FAILED
            event.handler_error = {
                "description":
                f"uncaught exception in {event.type} event handler",
                "message":
                BillingError.CONTACT_SUPPORT.format(
                    email=settings.ZULIP_ADMINISTRATOR),
            }
            event.save(update_fields=["status", "handler_error"])
        else:
            event.status = Event.EVENT_HANDLER_SUCCEEDED
            event.save()