Question for @Patrick About Stripe Webhooks for PHP Based Multi-Tenanted System

Hi there @patrick...

I'm setting up Stripe webhooks for my multi-tenanted PHP based app where each of my clients has their own Stripe account and keys.

I'd like to implement it where in the dmxConnect/webhooks/stripe/index.php file I am still checking the form of the call is correct, but I am skipping the check of the webhook api key itself (so I'm not checking the value of whsec_xyz)

Then in the specific webhook server action file (e.g. charge.succeeded.php) I check whether the call is valid and which payment it refers to through a metadata code I passed into the payment_intent plus the payment_intent id itself.

I therefore need to change the dmxConnect/webhooks/stripe/index.php file to miss out the check for the webhook api key.

I've tried a few ways of doing it that haven't work... could tell me how best to make this change please?

Thanks!
Antony.

dmxConnect/webhooks/stripe/index.php:

<?php

if ($_SERVER['REQUEST_METHOD'] != 'POST') {
    err('Invalid request.');
}

require('../../../dmxConnectLib/dmxConnect.php');
require('../../../dmxConnectLib/stripe/init.php');

\Stripe\Stripe::setApiKey(CONFIG('STRIPE_SECRET_KEY'));
$endpoint_secret = CONFIG('STRIPE_ENDPOINT_SECRET');

$payload = @file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = NULL;

try {
    $event = \Stripe\Webhook::constructEvent(
        $payload, $sig_header, $endpoint_secret
    );
} catch(\UnexpectedValueException $e) {
    // Invalid payload
    http_response_code(400);
    exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
    // Invalid signature
    http_response_code(400);
    exit();
}

if (file_exists($event->type . '.php')) {
    require($event->type . '.php');
    $steps = json_decode($exports);
    $app = new \lib\App();
    $app->exec($steps);
} else {
    err("No action found for $event->type.");
}

function err($msg) {
    echo $msg;
    exit();
}

Iā€™d love your perspective on this @patrick if you have a moment to help me out!

For security I would suggest to also check the webhook secret. If you don't use the endpoint secret then at least do an extra request to the stripe API to retrieve the event from there and do not trust the data posted to the webhook.

That sounds like a plan @patrick...

But since my database will have hundreds of webhook endpoint secrets, I need to be able to let the index.php file above pass that check and then check it once the flow gets to the webhook server actions I will create in Wappler.

Can you show me how to modify the index.php file to bypass that initial check please?

Don't you use Stripe connect? Then you only need 1 webhook secret.

1 Like

A quick and dirty fix will be.

Replace:

$event = \Stripe\Webhook::constructEvent(
  $payload, $sig_header, $endpoint_secret
);

With:

$event = \json_decode($payload, true);

Like I said. don't trust the payload, retrieve the event again using the API with the users API key.

Preferred is to validate the secret also which also doesn't require an extra API call and delay, but the above is relative secure since you verify the event directly using the API.

I thought you were using your own Stripe webhook implementation instead of Wappler's, or did I misinterpret?

(Note I don't know anything about Stripe, I can't help)