Reputation: 27
The problem is that if I trigger the same event multiple times, I get different results.
I have three terminals open:
listen --forward-to localhost:8888/my-endpoint.php
stripe trigger + event
(eg. stripe trigger payment_intent.succeeded)
Troubleshooting
I can trigger by running through the Stripe checkout too. Checkout works, but results in terminal are similar to described.
I have also run the trigger whilst xdebug is listening and can see the code steps through in the exact fashion expected (ie assigns simple text responses to the body and calls 200/400 codes).
Important notes
The screenshot below shows log (left), listen (top right) and event trigger (bottom right).
(1st and 4th attempts work. 2nd and 3rd do not. They do not include logs or:
2020-04-24 16:17:16 --> payment_intent.succeeded [evt_1GbMikFOnngxtrI1EfZyfaNo]
2020-04-24 16:17:16 <-- [200] POST http://localhost:8888/scripts/stripe/webhook-endpoint.php [evt_1GbMikFOnngxtrI1EfZyfaNo]
Note you can see the timecodes of the different events in the top right.
And the log only gets updated once, on the first trigger.
The other fun thing is that sometimes the responses appear in the Stripe dashboard, sometimes they don't.
Any ideas?
Webhooks Code (signature and api keys are set above but hidden here)
$payload = @file_get_contents('php://input'); //get json POST data stripe sends
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = null;
try {
$event = \Stripe\Webhook::constructEvent(
$payload, $sig_header, $endpoint_secret, //get data
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
printf("Invalid payload");
http_response_code(400);
exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
printf("Invalid signature.");
http_response_code(400);
exit();
}
// Handle the event(s)
switch ($event->type) {
case 'checkout.session.completed':
$session = $event->data->object;
printf("Checkout Session Completed");
http_response_code(200);
break;
case 'payment_intent.succeeded':
$paymentIntent = $event->data->object;
$intent = $event->data->object;
printf("Payment Intent Succeeded: %s", $intent->id);
http_response_code(200);
error_log( json_encode( $intent, JSON_PRETTY_PRINT ) );
break;
case 'payment_intent.payment_failed':
$paymentMethod = $event->data->object;
$intent = $event->data->object;
$error_message = $intent->last_payment_error ? $intent->last_payment_error->message : "";
printf("Failed: %s, %s", $intent->id, $error_message);
http_response_code(200);
break;
case 'payment_intent.processing':
printf("Payment Intent Processing: %s", $intent->id);
http_response_code(200);
break;
case 'payment_intent.canceled':
printf("Payment Intent canceled: %s", $intent->id);
http_response_code(200);
break;
default:
http_response_code(400);
exit();
}
http_response_code(200);
return false;
Upvotes: 1
Views: 1277
Reputation: 1179
I hope I've understood correctly, my apologies if I'm off the point here.
I think that the issue here is that using stripe trigger payment_intent.succeeded
ultimately creates 3 events:
payment_intent.created
charge.succeeded
payment_intent.succeeded
It seems that the case statement doesn't have clauses for the first two event types so they default to 400s.
Depending on what you would like to do, you could extend the case statement to handle those events. Also maybe this is relevant to your findings, but Events do not have a guaranteed ordering, this is normal behaviour and something that you have to keep in mind when building your system [0]. Hope this helps!
[0] https://stripe.com/docs/webhooks/best-practices#event-ordering
Upvotes: 1