Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/V2/Core/EventNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public function __construct($json, $client)

/**
* Helper for constructing an Event Notification. Doesn't perform signature validation, so you
* should use \Stripe\BaseStripeClient#parseEventNotification instead for
* should use \Stripe\BaseStripeClient::parseEventNotification instead for
* initial handling. This is useful in unit tests and working with EventNotifications that you've
* already validated the authenticity of.
*
Expand All @@ -78,6 +78,12 @@ public static function fromJson($jsonStr, $client)
{
$json = json_decode($jsonStr, true);

if (isset($json['object']) && 'event' === $json['object']) {
throw new \Stripe\Exception\UnexpectedValueException(
'You passed a webhook payload to StripeClient::parseEventNotification, which expects an event notification. Use Webhook::constructEvent instead.'
);
}

$class = UnknownEventNotification::class;
$eventNotificationTypes = EventNotificationTypes::v2EventMapping;
if (\array_key_exists($json['type'], $eventNotificationTypes)) {
Expand Down
8 changes: 7 additions & 1 deletion lib/Webhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@ public static function constructEvent($payload, $sigHeader, $secret, $tolerance
$jsonError = \json_last_error();
if (null === $data && \JSON_ERROR_NONE !== $jsonError) {
$msg = "Invalid payload: {$payload} "
. "(json_last_error() was {$jsonError})";
. "(json_last_error() was {$jsonError})";

throw new Exception\UnexpectedValueException($msg);
}

if (isset($data['object']) && 'v2.core.event' === $data['object']) {
throw new Exception\UnexpectedValueException(
'You passed an event notification to Webhook::constructEvent, which expects a webhook payload. Use StripeClient::parseEventNotification instead.'
);
}

return Event::constructFrom($data);
}
}
26 changes: 24 additions & 2 deletions tests/Stripe/BaseStripeClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ public function testParseEventNotification()
{
$jsonEvent = [
'id' => 'evt_234',
'object' => 'event',
'object' => 'v2.core.event',
'type' => 'v1.billing.meter.error_report_triggered',
'created' => '2022-02-15T00:27:45.330Z',
'context' => 'acct_123',
Expand Down Expand Up @@ -872,7 +872,7 @@ public function testParseUnknownEventNotification()
{
$jsonEvent = [
'id' => 'evt_234',
'object' => 'event',
'object' => 'v2.core.event',
'type' => 'imaginary',
'livemode' => true,
'created' => '2022-02-15T00:27:45.330Z',
Expand All @@ -896,6 +896,28 @@ public function testParseUnknownEventNotification()
self::assertNull($event->fetchRelatedObject());
}

public function testParseEventNotificationRejectsV1Payload()
{
$jsonEvent = [
'id' => 'evt_234',
'object' => 'event',
'type' => 'charge.succeeded',
'data' => ['object' => ['id' => 'ch_123', 'object' => 'charge']],
];

$eventData = json_encode($jsonEvent);
$client = new BaseStripeClient(['api_key' => 'sk_test_client', 'api_base' => MOCK_URL, 'stripe_account' => 'acc_123']);

$sigHeader = WebhookTest::generateHeader(['payload' => $eventData]);

try {
$client->parseEventNotification($eventData, $sigHeader, WebhookTest::SECRET);
self::fail('Expected UnexpectedValueException was not thrown');
} catch (Exception\UnexpectedValueException $e) {
self::compatAssertStringContainsString('Webhook::constructEvent', $e->getMessage());
}
}

public function testV2OverridesPreviewVersionIfPassedInRawRequestOptions()
{
$this->curlClientStub->method('executeRequestWithRetries')
Expand Down
6 changes: 3 additions & 3 deletions tests/Stripe/EventTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public function testJsonDecodeEventNotificationObject()
{
$eventData = json_encode([
'id' => 'evt_234',
'object' => 'event',
'object' => 'v2.core.event',
'type' => 'v1.billing.meter.error_report_triggered',
'created' => '2022-02-15T00:27:45.330Z',
'related_object' => [
Expand Down Expand Up @@ -226,7 +226,7 @@ public function testJsonDecodeEventNotificationObjectWithNoRelatedObject()
{
$eventData = json_encode([
'id' => 'evt_234',
'object' => 'event',
'object' => 'v2.core.event',
'type' => 'v1.billing.meter.no_meter_found',
'created' => '2022-02-15T00:27:45.330Z',
]);
Expand All @@ -244,7 +244,7 @@ public function testJsonDecodeEventNotificationObjectWithNoReasonObject()
{
$eventData = json_encode([
'id' => 'evt_234',
'object' => 'event',
'object' => 'v2.core.event',
'type' => 'imaginary',
'created' => '2022-02-15T00:27:45.330Z',
]);
Expand Down
17 changes: 17 additions & 0 deletions tests/Stripe/WebhookTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,21 @@ public function testTimestampOffButNoTolerance()
$sigHeader = $this->generateHeader(['timestamp' => 12345]);
self::assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET));
}

public function testConstructEventRejectsV2Payload()
{
$payload = json_encode([
'id' => 'evt_test_webhook',
'object' => 'v2.core.event',
'type' => 'v1.billing.meter.error_report_triggered',
]);
$sigHeader = $this->generateHeader(['payload' => $payload]);

try {
Webhook::constructEvent($payload, $sigHeader, self::SECRET);
self::fail('Expected UnexpectedValueException was not thrown');
} catch (Exception\UnexpectedValueException $e) {
self::compatAssertStringContainsString('StripeClient::parseEventNotification', $e->getMessage());
}
}
}
Loading