예제 #1
0
def stripe_webhook(request):
    payload = request.body
    sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")

    if not payload or not sig_header:
        return HttpResponse("[invalid payload]", status=400)

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
        )
    except ValueError:
        return HttpResponse("[invalid payload]", status=400)
    except stripe.error.SignatureVerificationError:
        return HttpResponse("[invalid signature]", status=400)

    if event["type"] == "checkout.session.completed":
        session = event["data"]["object"]
        payment = Payment.finish(
            reference=session["id"],
            status=Payment.PAYMENT_STATUS_SUCCESS,
            data=session,
        )
        product = PRODUCTS[payment.product_code]
        product["activator"](product, payment, payment.user)

    return HttpResponse("[ok]", status=200)
예제 #2
0
def stripe_webhook(request):
    payload = request.body
    sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")

    if not payload or not sig_header:
        return HttpResponse("[invalid payload]", status=400)

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
        )
    except ValueError:
        return HttpResponse("[invalid payload]", status=400)
    except stripe.error.SignatureVerificationError:
        return HttpResponse("[invalid signature]", status=400)

    log.info("Stripe webhook event: " + event["type"])

    if event["type"] == "checkout.session.completed":
        session = event["data"]["object"]
        try:
            payment = Payment.finish(
                reference=session["id"],
                status=Payment.STATUS_SUCCESS,
                data=session,
            )
        except PaymentException:
            return HttpResponse("[payment not found]", status=400)

        product = PRODUCTS[payment.product_code]
        product["activator"](product, payment, payment.user)
        return HttpResponse("[ok]", status=200)

    if event["type"] == "invoice.paid":
        invoice = event["data"]["object"]
        if invoice["billing_reason"] == "subscription_create":
            # already processed in "checkout.session.completed" event
            return HttpResponse("[ok]", status=200)

        user = User.objects.filter(stripe_id=invoice["customer"]).first()
        # todo: do we need throw error in case user not found?

        payment = Payment.create(
            reference=invoice["id"],
            user=user,
            product=find_by_stripe_id(invoice["lines"]["data"][0]["plan"]["id"]),
            data=invoice,
            status=Payment.STATUS_SUCCESS,
        )
        product = PRODUCTS[payment.product_code]
        product["activator"](product, payment, user)
        return HttpResponse("[ok]", status=200)

    if event["type"] in {"customer.created", "customer.updated"}:
        customer = event["data"]["object"]
        User.objects.filter(email=customer["email"]).update(stripe_id=customer["id"])
        return HttpResponse("[ok]", status=200)

    return HttpResponse("[unknown event]", status=400)
예제 #3
0
    def test_finish_payment_positive(self):
        result: Payment = Payment.finish(
            reference=self.existed_payment.reference,
            status=Payment.STATUS_SUCCESS,
            data={"some": "data"})

        self.assertIsNotNone(result)
        # check it persistent
        payment = Payment.get(reference=result.reference)
        self.assertIsNotNone(payment)
        self.assertEqual(payment.id, self.existed_payment.id)
        self.assertEqual(payment.status, Payment.STATUS_SUCCESS)
        self.assertEqual(payment.data, '{"some": "data"}')
예제 #4
0
 def test_finis_payment_not_existed_payment(self):
     result = Payment.finish(reference="wrong-not-existed-reference",
                             status=Payment.STATUS_FAILED,
                             data={"some": "data"})
     self.assertIsNone(result)
예제 #5
0
def coinbase_webhook(request):
    payload = request.body
    webhook_signature = request.META.get("HTTP_X_CC_WEBHOOK_SIGNATURE")
    if not payload or not webhook_signature:
        return HttpResponse("[invalid payload]", status=400)

    # verify webhook signature
    payload_signature = hmac.new(
        key=bytes(settings.COINBASE_WEBHOOK_SECRET, "utf-8"),
        msg=payload,  # it's already in bytes
        digestmod=hashlib.sha256
    ).hexdigest()
    if payload_signature.upper() != webhook_signature.upper():
        return HttpResponse("[bad signature]", status=400)

    # load event data
    try:
        data = json.loads(payload)
    except json.JSONDecodeError:
        return HttpResponse("[payload is not json]", status=400)

    event = data.get("event")
    event_type = event.get("type")
    event_data = event.get("data")
    event_code = event_data.get("code")
    if not event or not event_type or not event_data or not event_code:
        return HttpResponse("[bad payload structure]", status=400)

    log.info(f"Coinbase webhook event: {event_type} ({event_code})")

    # find or create the user
    metadata_email = event_data.get("metadata", {}).get("email")
    if not metadata_email:
        return HttpResponse("[no email in payload]", status=400)

    now = datetime.utcnow()
    user, _ = User.objects.get_or_create(
        email=metadata_email,
        defaults=dict(
            membership_platform_type=User.MEMBERSHIP_PLATFORM_CRYPTO,
            full_name=metadata_email[:metadata_email.find("@")],
            membership_started_at=now,
            membership_expires_at=now,
            created_at=now,
            updated_at=now,
            moderation_status=User.MODERATION_STATUS_INTRO,
        ),
    )

    # find product
    checkout_id = event_data.get("checkout", {}).get("id")
    product = find_by_coinbase_id(checkout_id)
    if not checkout_id or not product:
        return HttpResponse("[product not found]", status=404)

    # make actions for event_types
    if event_type == "charge:created":
        Payment.create(
            reference=event_code,
            user=user,
            product=product,
            data=event,
            status=Payment.STATUS_STARTED,
        )
        return HttpResponse("[ok]", status=200)

    elif event_type == "charge:confirmed":
        try:
            payment = Payment.finish(
                reference=event_code,
                status=Payment.STATUS_SUCCESS,
                data=event,
            )
        except PaymentNotFound:
            payment = Payment.create(
                reference=event_code,
                user=user,
                product=product,
                data=event,
                status=Payment.STATUS_SUCCESS,
            )
        except PaymentAlreadyFinalized:
            return HttpResponse("[duplicate payment]", status=400)

        product["activator"](product, payment, user)
        return HttpResponse("[ok]", status=200)

    elif event_type == "charge:failed":
        Payment.finish(
            reference=event_code,
            status=Payment.STATUS_FAILED,
            data=event,
        )
        return HttpResponse("[ok]", status=200)

    elif event_type == "charge:pending":
        return HttpResponse("[ok]", status=200)

    return HttpResponse("[unknown event]", status=400)
예제 #6
0
 def test_finish_non_existent_payment_exception(self):
     with self.assertRaises(PaymentNotFound):
         result = Payment.finish(reference="wrong-not-existed-reference",
                                 status=Payment.STATUS_FAILED,
                                 data={"some": "data"})