Skip to content

Commit 9684e61

Browse files
authored
[CC-2868] Add chargeback webhook handling. (#218)
1 parent 0a26d70 commit 9684e61

File tree

3 files changed

+86
-3
lines changed

3 files changed

+86
-3
lines changed

src/Services/ResourceService.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,16 @@ public function fetchResourceByUrl($url)
209209
}
210210
$resource = $unzer->fetchReversal($paymentId, $resourceId);
211211
break;
212+
case $resourceType === IdStrings::CHARGEBACK:
213+
$paymentId = IdService::getResourceIdFromUrl($url, IdStrings::PAYMENT);
214+
$chargeId = IdService::getResourceIdOrNullFromUrl($url, IdStrings::CHARGE);
215+
216+
$resource = $this->fetchChargebackById(
217+
$paymentId,
218+
$resourceId,
219+
$chargeId
220+
);
221+
break;
212222
case $resourceType === IdStrings::PAYOUT:
213223
$resource = $unzer->fetchPayout(IdService::getResourceIdFromUrl($url, IdStrings::PAYMENT));
214224
break;

test/integration/WebhookTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use UnzerSDK\Constants\ApiResponseCodes;
1515
use UnzerSDK\Constants\WebhookEvents;
1616
use UnzerSDK\Exceptions\UnzerApiException;
17+
use UnzerSDK\Resources\TransactionTypes\Chargeback;
1718
use UnzerSDK\Resources\Webhook;
1819
use UnzerSDK\test\BaseIntegrationTest;
1920
use function count;
@@ -22,6 +23,7 @@
2223
class WebhookTest extends BaseIntegrationTest
2324
{
2425
//<editor-fold desc="Webhook tests">
26+
const CHARGEDBACK_PAYMENT_ID = 's-pay-341196';
2527

2628
/**
2729
* Verify Webhook resource can be registered and fetched.
@@ -221,6 +223,27 @@ public function bulkSettingOnlyOneWebhookShouldBePossible(): void
221223
$this->assertEquals($url, $webhook->getUrl());
222224
}
223225

226+
/**
227+
* @test
228+
*/
229+
public function testFetchResourceFromEvent(): void
230+
{
231+
$webhookNotification = [
232+
'event' => 'chargebacks',
233+
'publicKey' => 's-pub-xyz',
234+
'retrieveUrl' => 'https://sbx-api.unzer.com/v1/payments/' . self::CHARGEDBACK_PAYMENT_ID . '/charges/s-chg-1/chargebacks/s-cbk-1',
235+
'paymentId' => self::CHARGEDBACK_PAYMENT_ID
236+
];
237+
238+
// when
239+
$chargeback = $this->unzer->fetchResourceFromEvent(json_encode($webhookNotification));
240+
241+
// then
242+
$this->assertInstanceOf(Chargeback::class, $chargeback);
243+
$this->assertNotNull($chargeback);
244+
$this->assertEquals('s-cbk-1', $chargeback->getId());
245+
}
246+
224247
//</editor-fold>
225248

226249
//<editor-fold desc="Helpers">

test/unit/Services/WebhooksServiceTest.php

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,18 @@
1111

1212
namespace UnzerSDK\test\unit\Services;
1313

14-
use UnzerSDK\Unzer;
14+
use RuntimeException;
15+
use stdClass;
1516
use UnzerSDK\Interfaces\ResourceServiceInterface;
17+
use UnzerSDK\Resources\TransactionTypes\Charge;
18+
use UnzerSDK\Resources\TransactionTypes\Chargeback;
1619
use UnzerSDK\Resources\Webhook;
1720
use UnzerSDK\Resources\Webhooks;
1821
use UnzerSDK\Services\ResourceService;
1922
use UnzerSDK\Services\WebhookService;
2023
use UnzerSDK\test\BasePaymentTest;
2124
use UnzerSDK\test\unit\DummyResource;
22-
use RuntimeException;
23-
use stdClass;
25+
use UnzerSDK\Unzer;
2426

2527
class WebhooksServiceTest extends BasePaymentTest
2628
{
@@ -328,6 +330,54 @@ public function fetchResourceByEventWithEmptyRetrieveUrlShouldThrowException():
328330
$webhookService->fetchResourceFromEvent();
329331
}
330332

333+
/**
334+
* Verify that a chargeback is returned from a webhook event.
335+
*
336+
* @test
337+
*/
338+
public function fetchChargebackByEventShouldReturnChargeback(): void
339+
{
340+
// given
341+
$unzer = new Unzer('s-priv-1234');
342+
$webhookService = new WebhookService($unzer);
343+
344+
$paymentId = 's-pay-42';
345+
$chargeId = 's-chg-1';
346+
$chargebackId = 's-cbk-1';
347+
$retrieveUrl = "https://api.unzer.com/v1/payments/{$paymentId}/charges/{$chargeId}/chargebacks/{$chargebackId}";
348+
349+
// Partial mock: keep original behavior except fetchChargebackById which we want to intercept
350+
$resourceServiceMock = $this->getMockBuilder(ResourceService::class)
351+
->setConstructorArgs([$unzer])
352+
->setMethods(['fetchChargebackById'])
353+
->getMock();
354+
355+
$chargeback = (new Chargeback(10.0))
356+
->setId($chargebackId)
357+
->setParentResource((new Charge())->setId($chargeId));
358+
359+
$resourceServiceMock->expects($this->once())
360+
->method('fetchChargebackById')
361+
->with($paymentId, $chargebackId, $chargeId)
362+
->willReturn($chargeback);
363+
364+
$webhookService->setResourceService($resourceServiceMock);
365+
366+
$eventJson = json_encode([
367+
'event' => 'chargeback',
368+
'publicKey' => 's-pub-xyz',
369+
'retrieveUrl' => $retrieveUrl,
370+
'paymentId' => $paymentId
371+
]);
372+
373+
// when
374+
$fetchedChargeback = $webhookService->fetchResourceFromEvent($eventJson);
375+
376+
// then
377+
$this->assertSame($chargeback, $fetchedChargeback);
378+
$this->assertNotNull($fetchedChargeback);
379+
}
380+
331381
/**
332382
* Verify exception is thrown if the retrieveURL is empty.
333383
*

0 commit comments

Comments
 (0)