#kenny_webhooks

1 messages ยท Page 1 of 1 (latest)

prime pulsarBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.

๐Ÿ”— This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1245733139054202976

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

lilac agate
#

Hello

desert turret
#

Hi, here's my webhook code

#
import stripe
from fastapi import APIRouter, Header, HTTPException, Request

from src.lib.config import get_config

router = APIRouter(prefix="/stripe/webhooks")


@router.post("")
async def handle_webhook(
    request: Request,
    site_id: str,
    stripe_signature: str = Header(None),
):
    config = get_config()
    signing_secret = config.STRIPE_SIGNING_SECRET

    data = await request.body()

    try:
        event = stripe.Webhook.construct_event(
            payload=data, sig_header=stripe_signature, secret=signing_secret
        )
    except ValueError as e:
        print(f"Invalid signature: {e}")
        raise HTTPException(status_code=400, detail="Invalid signature")
    except stripe.SignatureVerificationError as e:
        print(f"Signature verification failed: {e}")
        raise HTTPException(
            status_code=400, detail="Signature verification failed"
        )
    except Exception as e:
        print(f"Error in stripe webhook: {e}")
        raise HTTPException(status_code=400, detail=str(e))

    event_type = event["type"]
    if event_type == "invoice.paid":
        checkout_session = event["data"]["object"]
        subscription_id = checkout_session.get("subscription")

        if not subscription_id:
            return {"error": "subscription_id not found in event data"}

        currency = checkout_session["currency"].upper()
        amt_paid = checkout_session["amount_paid"]

        metadata = checkout_session["metadata"]
        salestracking_id = metadata.get("stId")

        print(
            f"Salestracking ID: "
            f"{salestracking_id}, "
            f"Amount Paid: {amt_paid}, "
            f"Currency: {currency}"
            f"Site ID: {site_id}"
        )

    # TODO: Insert the data into the clickhouse DB as an event.
lilac agate
#

Yeah the issue is you likely don't have the raw body here from data = await request.body()

#

What framework are you using here?

desert turret
#

fastapi

#

I pretty much copy and pasted this code from how I handle standard webhook events directly from my stripe account with the key different being the signing key used

lilac agate
#

What is the exact error message?

desert turret
#
INFO:     127.0.0.1:50749 - "POST /v0/stripe/webhooks?site_id=69 HTTP/1.1" 400 Bad Request
Signature verification failed: No signatures found matching the expected signature for payload
#

This is the error I would always face when I put the wrong webhook signing key in

#

in my production app

#

In this case, I put my app's signing key. I don't know if that's right but I don't know any other way i'd do it

lilac agate
#

Okay one sec, let me grab a colleague more familiar with this flow in Stripe Apps

desert turret
#

great, thanks

sand reef
#

Hello jumping in to help out here

Do you know if the framework you're using (fastapi) converts any incoming requests payload to JSON automatically?

desert turret
#

I don't, but if doing that would break it, I'll show you my exact webhook fastapi code I have in production that has worked with over 1k transactions so far. Note that this isn't app related:

sand reef
#

Oh also, Stripe CLI prints out its own webhook secret. Is that the one you're using in your code?

#

When you run stripe listen it should print out the secret

desert turret
#

No, I've been using the app's signing secret

#

I'm trying to make it so that people can connect my app to their stripe and then my server can listen to their webhooks

sand reef
#

I think there is a misunderstanding about the flow here. Are you following any docs for this?

desert turret
#

but it still doesn't fit perfectly for what I thought would be simple to implement

sand reef
#

I see. Let me elaborate. The webhook secret value is unique per endpoint. When you run Stripe CLI, it generates its own secret which would then be used to verify signature of the event that Stripe sends.

You shouldn't be using app's signing secret in this case.

desert turret
#

Then how does the promotekit and slack's stripe app listen to events without me having to provide them with my webhook signing key?

sand reef
#

The guide you linked here shows you two flows: https://docs.stripe.com/stripe-apps/build-backend?public-private=private#receive-events

One for when you're testing the app on your own account versus when you publish the app on the marketplace and someone installs it on their own stripe account.

If you look at "Public listing on App Marketplace" portion of the guide, it explains how you'd need to "Listen to events on Connected accounts" and add specific event type permissions to your app.

In which case, Stripe will send any events (matching your permissions) that get generated on the Stripe accounts that installs your app.

desert turret
#

well i tried that too

#

but it never reached my endpoint

#

This was my trigger: stripe trigger --stripe-account acct_1PEt7TKQ4RvXxzCu invoice.paid

#

and my listen: stripe listen --forward-connect-to "localhost:8000/v0/stripe/webhooks?site_id=69"

sand reef
#

I looked most recent invoice.paid event on acct_1PEt7TKQ4RvXxzCu, I do see it was sent to your Stripe CLI webhook.

#

But really your current issue is triggered by you using the wrong webhook secret

desert turret
#

When I run the commands above nothing reaches my server so how could that be a webhook secret problem?

sand reef
#

Not talking about the above issue. I meant to say your original issue with signature verification failing..

desert turret
#

Ah yeah

sand reef
#

Oh wait

desert turret
#

But having an individual webhook key for each wouldnt be viable here because then my client would need to provide me with their webhook secret

sand reef
#

acct_1PEt7TKQ4RvXxzCu isn't connected to any platforms

desert turret
#

I want it to be one-click like promotekit

sand reef
#

But having an individual webhook key for each wouldnt be viable here because then my client would need to provide me with their webhook secret
that won't be the case when you list your app in production

#

In production, you'd only have one webhook endpoint URL where you'd receive the events

desert turret
#

How would I distinguish between the accounts?

#

and where would I put that production webhook endpoint url

sand reef
#

When a Stripe account installs your app from the marketplace, Stripe establishes a connection between that account and your account.

In this scenario, your account becomes a platform and the account installing your app becomes a connected account

prime pulsarBOT
sand reef
desert turret
#

ok that makes sense

#

so the webhooks event will contain the account id it's associated with?

sand reef
#

Correct

desert turret
#

okay, so my permissions are set up correctly and the app as I currently have it set up will properly proxy the webhooks from the ppl who connect their stripe acct to my account right?

#

so all webhooks travel through a unified endpoint

sand reef
#

Yup.. As long as the endpoint you've created is listening to events on the connected account. Like I mentioned earlier: #1245733139054202976 message

desert turret
#

So that's done through permissions?

#

right now my app permissions are as follows:

  "permissions": [
    {
      "permission": "webhook_read",
      "purpose": "Tracks sales and sales revenue into SalesTracking"
    }
  ],
sand reef
#

There are two parts to it.

Configuring permissions lets your customer (Stripe accounts that install your app) know about the type of events your app will have access to.

The second part is that you'd need to create a webhook on your platform account that listens to the events that get generated on connected accounts. You'd configure a connect type webhook endpoint when you create a new one through: https://dashboard.stripe.com/webhooks

desert turret
#

This selection I presume

#

and for testing I'd just use the singular webhook signing key that is generated for me?

woven patrol
#

Hi there

#

Yeah

desert turret
#

thanks for the help