Skip to content
Merged
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
4 changes: 4 additions & 0 deletions docs/Webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Every delivery carries a [`WebhookEvent`](../src/API/Resources/WebhookEvent.php)
| `entityType` | `string` | Type of the related resource (e.g. `order`, `refund`, `subscription`). |
| `entityId` | `string` | ID of the related resource (e.g. `order_Hn5xWqVfKm8RjTgYbUcP`). |
| `object` | `object\|null` | The full resource payload at the time of the event. Shape depends on `entityType`. |
| `createdAt` | `string` | When the event occurred (ISO 8601). |
| `testmode` | `bool` | Whether this event was generated in test mode. |
| `links` | `object` | HATEOAS links — `links.self.href` points to this webhook event. |

### Example payload
Expand All @@ -54,6 +56,8 @@ Every delivery carries a [`WebhookEvent`](../src/API/Resources/WebhookEvent.php)
"status": "paid",
"total": { "value": "29.99", "currency": "EUR" }
},
"createdAt": "2026-01-11T10:50:50+02:00",
"testmode": true,
"links": {
"self": {
"href": "https://api.vatly.com/v1/webhook-events/webhook_event_Qk8pRtSvWm2NjLhYcZaE",
Expand Down
7 changes: 7 additions & 0 deletions src/API/Resources/WebhookEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,12 @@ class WebhookEvent extends BaseResource
*/
public $object = null;

/**
* @example 2023-08-11T10:48:51+02:00
*/
public string $createdAt;

public bool $testmode;

public WebhookEventLinks $links;
}
4 changes: 3 additions & 1 deletion src/API/Webhooks/Webhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static function parse(string $payload, string $signature, string $secret)
);
}

foreach (['id', 'resource', 'eventName', 'entityType', 'entityId'] as $field) {
foreach (['id', 'resource', 'eventName', 'entityType', 'entityId', 'createdAt', 'testmode'] as $field) {
if (! isset($decoded->{$field})) {
throw new \InvalidArgumentException(
"Webhook payload is missing required field: {$field}"
Expand All @@ -56,6 +56,8 @@ public static function parse(string $payload, string $signature, string $secret)
$decoded->eventName,
$decoded->entityType,
$decoded->entityId,
$decoded->testmode,
$decoded->createdAt,
$object,
);
}
Expand Down
11 changes: 11 additions & 0 deletions src/API/Webhooks/WebhookPayload.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,21 @@ class WebhookPayload
*/
public ?object $object;

/**
* @example 2023-08-11T10:48:51+02:00
*/
public string $createdAt;

public bool $testmode;

public function __construct(
string $id,
string $resource,
string $eventName,
string $entityType,
string $entityId,
bool $testmode,
string $createdAt,
?object $object = null,
) {
$this->id = $id;
Expand All @@ -68,5 +77,7 @@ public function __construct(
$this->entityType = $entityType;
$this->entityId = $entityId;
$this->object = $object;
$this->testmode = $testmode;
$this->createdAt = $createdAt;
}
}
7 changes: 7 additions & 0 deletions tests/Webhooks/WebhookTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ private function makePayload(array $overrides = []): string
'entityType' => 'order',
'entityId' => 'order_Hn5xWqVfKm8RjTgYbUcP',
'object' => ['id' => 'order_Hn5xWqVfKm8RjTgYbUcP', 'resource' => 'order'],
'createdAt' => '2023-01-11T10:50:50+02:00',
'testmode' => true,
], $overrides);

return json_encode($data);
Expand Down Expand Up @@ -52,6 +54,9 @@ public function test_it_parses_a_valid_webhook(): void
$this->assertSame('order_Hn5xWqVfKm8RjTgYbUcP', $event->entityId);
$this->assertIsObject($event->object);
$this->assertSame('order_Hn5xWqVfKm8RjTgYbUcP', $event->object->id);
$this->assertSame('order', $event->object->resource);
$this->assertTrue($event->testmode);
$this->assertSame('2023-01-11T10:50:50+02:00', $event->createdAt);
}

public function test_it_parses_a_webhook_without_object(): void
Expand All @@ -62,6 +67,8 @@ public function test_it_parses_a_webhook_without_object(): void
'eventName' => 'order.paid',
'entityType' => 'order',
'entityId' => 'order_Hn5xWqVfKm8RjTgYbUcP',
'createdAt' => '2023-01-11T10:50:50+02:00',
'testmode' => true,
];
$payload = json_encode($data);
$signature = $this->sign($payload);
Expand Down