|
13 | 13 | use Adyen\Model\ReportWebhooks\ReportNotificationRequest;
|
14 | 14 | use Adyen\Model\TransactionWebhooks\TransactionNotificationRequestV4;
|
15 | 15 | use Adyen\Model\TransferWebhooks\TransferNotificationRequest;
|
16 |
| -use Exception; |
17 |
| -use PhpParser\Error; |
| 16 | +use JsonException; |
18 | 17 |
|
19 | 18 | class BankingWebhookParser
|
20 | 19 | {
|
21 | 20 | private $payload;
|
22 | 21 |
|
| 22 | + private const WEBHOOK_CLASSES = [ |
| 23 | + AuthenticationNotificationRequest::class, |
| 24 | + RelayedAuthenticationRequest::class, |
| 25 | + BalanceAccountBalanceNotificationRequest::class, |
| 26 | + AccountHolderNotificationRequest::class, |
| 27 | + BalanceAccountNotificationRequest::class, |
| 28 | + PaymentNotificationRequest::class, |
| 29 | + SweepConfigurationNotificationRequest::class, |
| 30 | + ReportNotificationRequest::class, |
| 31 | + TransferNotificationRequest::class, |
| 32 | + TransactionNotificationRequestV4::class |
| 33 | + ]; |
| 34 | + |
23 | 35 | public function __construct(string $payload)
|
24 | 36 | {
|
25 | 37 | $this->payload = $payload;
|
26 | 38 | }
|
27 | 39 |
|
28 |
| - public function getGenericWebhook() |
| 40 | + /** |
| 41 | + * Parse payload into the appropriate webhook model based on the `type` field. |
| 42 | + * |
| 43 | + * @return object The deserialized webhook model. |
| 44 | + * @throws WebhookParseException |
| 45 | + */ |
| 46 | + public function getGenericWebhook(): object |
29 | 47 | {
|
30 |
| - $jsonPayload = (array)json_decode($this->payload, true); |
31 |
| - |
32 |
| - // custom check for RelayedAuthenticationRequest as it doesn't include the attribute 'type' |
33 |
| - if (is_array($jsonPayload) && |
34 |
| - array_key_exists('id', $jsonPayload) && |
35 |
| - array_key_exists('paymentInstrumentId', $jsonPayload)) { |
36 |
| - $clazz = new RelayedAuthenticationRequest(); |
37 |
| - return (object)$this->deserializewebhook($clazz); |
38 |
| - } |
39 |
| - |
40 |
| - // handle other webhook events using `type attribute |
41 | 48 | try {
|
42 |
| - $type = $jsonPayload['type']; |
43 |
| - } catch (Exception $ex) { |
44 |
| - throw new Error("'type' attribute not found in payload: " . $this->payload); |
45 |
| - } |
46 |
| - |
47 |
| - if (in_array($type, ($clazz = new AuthenticationNotificationRequest())->getTypeAllowableValues())) { |
48 |
| - return (object)$this->deserializewebhook($clazz); |
49 |
| - } |
50 |
| - |
51 |
| - if (in_array($type, ($clazz = new BalanceAccountBalanceNotificationRequest())->getTypeAllowableValues())) { |
52 |
| - return (object)$this->deserializewebhook($clazz); |
53 |
| - } |
54 |
| - |
55 |
| - if (in_array($type, ($clazz = new AccountHolderNotificationRequest)->getTypeAllowableValues())) { |
56 |
| - return (object)self::deserializewebhook($clazz); |
57 |
| - } |
58 |
| - |
59 |
| - if (in_array($type, ($clazz = new BalanceAccountNotificationRequest())->getTypeAllowableValues())) { |
60 |
| - return (object)self::deserializeWebhook($clazz); |
| 49 | + $jsonPayload = json_decode($this->payload, true, 512, JSON_THROW_ON_ERROR); |
| 50 | + } catch (JsonException $e) { |
| 51 | + throw new WebhookParseException("Invalid JSON payload: " . $e->getMessage(), 0, $e); |
61 | 52 | }
|
62 | 53 |
|
63 |
| - if (in_array($type, ($clazz = new PaymentNotificationRequest())->getTypeAllowableValues())) { |
64 |
| - return (object)self::deserializeWebhook($clazz); |
| 54 | + if (!isset($jsonPayload['type'])) { |
| 55 | + throw new WebhookParseException("'type' attribute not found in payload: " . $this->payload); |
65 | 56 | }
|
66 | 57 |
|
67 |
| - if (in_array($type, ($clazz = new SweepConfigurationNotificationRequest())->getTypeAllowableValues())) { |
68 |
| - return (object)self::deserializeWebhook($clazz); |
69 |
| - } |
70 |
| - |
71 |
| - if (in_array($type, ($clazz = new ReportNotificationRequest())->getTypeAllowableValues())) { |
72 |
| - return (object)self::deserializeWebhook($clazz); |
73 |
| - } |
| 58 | + $type = $jsonPayload['type']; |
74 | 59 |
|
75 |
| - if (in_array($type, ($clazz = new TransferNotificationRequest())->getTypeAllowableValues())) { |
76 |
| - return(object)self::deserializeWebhook($clazz); |
| 60 | + foreach (self::WEBHOOK_CLASSES as $class) { |
| 61 | + $instance = new $class(); |
| 62 | + if (in_array($type, $instance->getTypeAllowableValues(), true)) { |
| 63 | + return $this->deserializeWebhook($instance); |
| 64 | + } |
77 | 65 | }
|
78 | 66 |
|
79 |
| - if (in_array($type, ($clazz = new TransactionNotificationRequestV4())->getTypeAllowableValues())) { |
80 |
| - return(object)self::deserializeWebhook($clazz); |
81 |
| - } |
82 |
| - |
83 |
| - // throw error in case the webhook can not be parsed |
84 |
| - throw new \Error("Could not parse the payload: " . $this->payload); |
| 67 | + throw new WebhookParseException("Could not parse the payload: " . $this->payload); |
85 | 68 | }
|
86 | 69 |
|
87 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
| 70 | + /** |
| 71 | + * Type-safe getters for specific webhook classes. |
| 72 | + */ |
88 | 73 | public function getAuthenticationNotificationRequest(): AuthenticationNotificationRequest
|
89 | 74 | {
|
90 |
| - return $this->getGenericWebhook(); |
| 75 | + return $this->getWebhookByClass(AuthenticationNotificationRequest::class); |
91 | 76 | }
|
92 | 77 |
|
93 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
94 | 78 | public function getRelayedAuthenticationRequest(): RelayedAuthenticationRequest
|
95 | 79 | {
|
96 |
| - return $this->getGenericWebhook(); |
| 80 | + return $this->getWebhookByClass(RelayedAuthenticationRequest::class); |
97 | 81 | }
|
98 | 82 |
|
99 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
100 | 83 | public function getBalanceAccountBalanceNotificationRequest(): BalanceAccountBalanceNotificationRequest
|
101 | 84 | {
|
102 |
| - return $this->getGenericWebhook(); |
| 85 | + return $this->getWebhookByClass(BalanceAccountBalanceNotificationRequest::class); |
103 | 86 | }
|
104 | 87 |
|
105 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
106 | 88 | public function getAccountHolderNotificationRequest(): AccountHolderNotificationRequest
|
107 | 89 | {
|
108 |
| - return $this->getGenericWebhook(); |
| 90 | + return $this->getWebhookByClass(AccountHolderNotificationRequest::class); |
109 | 91 | }
|
110 | 92 |
|
111 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
112 | 93 | public function getBalanceAccountNotificationRequest(): BalanceAccountNotificationRequest
|
113 | 94 | {
|
114 |
| - return $this->getGenericWebhook(); |
| 95 | + return $this->getWebhookByClass(BalanceAccountNotificationRequest::class); |
115 | 96 | }
|
116 | 97 |
|
117 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
118 | 98 | public function getPaymentNotificationRequest(): PaymentNotificationRequest
|
119 | 99 | {
|
120 |
| - return $this->getGenericWebhook(); |
| 100 | + return $this->getWebhookByClass(PaymentNotificationRequest::class); |
121 | 101 | }
|
122 | 102 |
|
123 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
124 | 103 | public function getSweepConfigurationNotificationRequest(): SweepConfigurationNotificationRequest
|
125 | 104 | {
|
126 |
| - return $this->getGenericWebhook(); |
| 105 | + return $this->getWebhookByClass(SweepConfigurationNotificationRequest::class); |
127 | 106 | }
|
128 | 107 |
|
129 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
130 | 108 | public function getReportNotificationRequest(): ReportNotificationRequest
|
131 | 109 | {
|
132 |
| - return $this->getGenericWebhook(); |
| 110 | + return $this->getWebhookByClass(ReportNotificationRequest::class); |
133 | 111 | }
|
134 | 112 |
|
135 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
136 | 113 | public function getTransferNotificationRequest(): TransferNotificationRequest
|
137 | 114 | {
|
138 |
| - return $this->getGenericWebhook(); |
| 115 | + return $this->getWebhookByClass(TransferNotificationRequest::class); |
139 | 116 | }
|
140 | 117 |
|
141 |
| - /** @noinspection PhpIncompatibleReturnTypeInspection */ |
142 | 118 | public function getTransactionNotificationRequestV4(): TransactionNotificationRequestV4
|
143 | 119 | {
|
144 |
| - return $this->getGenericWebhook(); |
| 120 | + return $this->getWebhookByClass(TransactionNotificationRequestV4::class); |
| 121 | + } |
| 122 | + |
| 123 | + private function getWebhookByClass(string $expectedClass): object |
| 124 | + { |
| 125 | + $webhook = $this->getGenericWebhook(); |
| 126 | + |
| 127 | + if (!$webhook instanceof $expectedClass) { |
| 128 | + throw new WebhookParseException("Expected $expectedClass but got " . get_class($webhook)); |
| 129 | + } |
| 130 | + |
| 131 | + return $webhook; |
145 | 132 | }
|
146 | 133 |
|
147 |
| - private function deserializeWebhook($clazz) |
| 134 | + private function deserializeWebhook(object $instance): object |
148 | 135 | {
|
149 |
| - return ObjectSerializer::deserialize($this->payload, get_class($clazz)); |
| 136 | + return ObjectSerializer::deserialize($this->payload, get_class($instance)); |
150 | 137 | }
|
151 | 138 | }
|
0 commit comments