Skip to content

Commit 7be1f84

Browse files
committed
Issue #2 Add notification handler.
1 parent cea6b80 commit 7be1f84

File tree

2 files changed

+181
-6
lines changed

2 files changed

+181
-6
lines changed

README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Table of Contents
2020
* [API Fetch Transaction](#api-fetch-transaction)
2121
* [Hosted Payment Page](#hosted-payment-page)
2222
* [Hosted Payment Page Authorize/Purchase](#hosted-payment-page-authorizepurchase)
23+
* [Webhook Notifications](#webhook-notifications)
2324

2425
# Omnipay-AuthorizeNetApi
2526

@@ -387,3 +388,79 @@ or use the `set*()` form to do the same thing:
387388

388389
$request->setPaymentOptionsShowBankAccount(false);
389390

391+
# Webhook Notifications
392+
393+
The Authorize.Net gateway provides a rich set of webhooks to notify the
394+
merchant site (and/or other backend systems) about events related to
395+
customers or payments.
396+
The [current documentation can be found here](https://developer.authorize.net/api/reference/features/webhooks.html).
397+
398+
For some API methods, such as the Hosted Payment Page, the webhooks
399+
are necessary for operation. For other API methods they provide additional
400+
information.
401+
402+
The webhooks can be configured in the Authorize.Net account settings page.
403+
They can also be fully managed through a REST API, so that a merchant
404+
site can register for all the webhooks that it needs.
405+
*Note that the webhook management RESTful API has not yet been implemented here.*
406+
407+
Your notification handler is set up like this at your webhook endpoint:
408+
409+
```php
410+
$gateway = Omnipay::create('AuthorizeNetApi_Api');
411+
412+
$gateway->setAuthName($authName);
413+
$gateway->setTransactionKey($authKey);
414+
$gateway->setTestMode(true); // for false
415+
416+
$notification = $gateway->acceptNotification();
417+
```
418+
419+
This will read and parse the webhook `POST` data.
420+
The raw nested array data can be found at:
421+
422+
$notification->getData();
423+
424+
The parsed `Notification` value object can be found at:
425+
426+
$notification->getParsedData();
427+
428+
Some details that describe the nature of the notification are:
429+
430+
```php
431+
// The main target: payment or customer
432+
$notification->getEventTarget();
433+
434+
// The event subtarget. e.g. capture, fraud, void, subscription
435+
$notification->getEventSubtarget();
436+
437+
// The event action. e.g. created, updated, deleted, held, approved, declined
438+
$notification->getEventMethod();
439+
```
440+
441+
See here for a full list of the target, subtarget and actions:
442+
https://github.com/academe/authorizenet-objects/blob/master/src/ServerRequest/Notification.php#L24
443+
444+
For those notifications that contain the `transactionReference`, this can be
445+
obtained:
446+
447+
$notification->getTransactionReference();
448+
449+
For any notifications that do not involve a transaction, this will be `null`.
450+
Note that the webhook does not include the merchant `transactionId`,
451+
so there is nothing to tie the payment webhook back to a Hosted Page request.
452+
In this case, you can supply the `transactionId` as a query parameter
453+
on the `notifyUrl` when creating the Hosted Payment Page data.
454+
However, do be aware this ID will be visible to end users monitoring
455+
browser traffic, and the ID (being in the URL) will not be included
456+
in the notification signing, so could be faked. It is unlikely, but just
457+
be aware that it is a potential attack vector, so maybe self-sign the URL
458+
too.
459+
460+
For consistency with other Omipay Driers, this driver *may* make an
461+
opinionated decision on how the `transactionId` is passed into the
462+
notification handler, but only after researchign how other people are
463+
handling it.
464+
There is a front-end way to do it through an iframe, but it seems
465+
vulnerable to user manipulation to me.
466+

src/Message/AcceptNotification.php

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace Omnipay\AuthorizeNetApi\Message;
44

55
/**
6-
*
6+
* TODO: validate the server request signature.
77
*/
88

99
use Omnipay\Common\Message\NotificationInterface;
@@ -13,6 +13,7 @@
1313

1414
use Omnipay\AuthorizeNetApi\Traits\HasGatewayParams;
1515
use Academe\AuthorizeNet\ServerRequest\Notification;
16+
use Academe\AuthorizeNet\Response\Model\TransactionResponse;
1617

1718
class AcceptNotification extends AbstractRequest implements NotificationInterface //, RequestInterface
1819
{
@@ -25,8 +26,6 @@ class AcceptNotification extends AbstractRequest implements NotificationInterfac
2526
*/
2627
protected $parsedData;
2728

28-
protected $notification;
29-
3029
public function __construct(ClientInterface $httpClient, HttpRequest $httpRequest)
3130
{
3231
// The request is a \Symfony/Component/HttpFoundation/Request object
@@ -77,7 +76,9 @@ public function getData()
7776
*/
7877
public function getTransactionReference()
7978
{
80-
// TODO.
79+
if ($this->getEventTarget() === $this->getParsedData()::EVENT_TARGET_PAYMENT) {
80+
return $this->getPayload()->getTransId();
81+
}
8182
}
8283

8384
/**
@@ -88,7 +89,15 @@ public function getTransactionReference()
8889
*/
8990
public function getTransactionStatus()
9091
{
91-
// TODO.
92+
$responseCode = $this->getResponseCode();
93+
94+
if ($responseCode === TransactionResponse::RESPONSE_CODE_APPROVED) {
95+
return static::STATUS_COMPLETED;
96+
} elseif ($responseCode === TransactionResponse::RESPONSE_CODE_PENDING) {
97+
return static::STATUS_PENDIND;
98+
} elseif ($responseCode !== null) {
99+
return static::STATUS_FAILED;
100+
}
92101
}
93102

94103
/**
@@ -98,7 +107,8 @@ public function getTransactionStatus()
98107
*/
99108
public function getMessage()
100109
{
101-
// TODO.
110+
// There are actually no messages in the notifications.
111+
return '';
102112
}
103113

104114
/**
@@ -112,4 +122,92 @@ public function sendData($data)
112122
{
113123
return $this;
114124
}
125+
126+
/**
127+
* The main target of the notificaiton: payment or customer.
128+
*/
129+
public function getEventTarget()
130+
{
131+
return $this->getParsedData()->getEventTarget();
132+
}
133+
134+
/**
135+
* The sub-target of the notificaiton.
136+
*/
137+
public function getEventSubtarget()
138+
{
139+
return $this->getParsedData()->getEventSubtarget();
140+
}
141+
142+
/**
143+
* The action against the target of the notificaito.
144+
*/
145+
public function getEventAction()
146+
{
147+
return $this->getParsedData()->getEventAction();
148+
}
149+
150+
/**
151+
* The UUID identifying this specific notification.
152+
*/
153+
public function getNotificationId()
154+
{
155+
return $this->getParsedData()->getNotificationId();
156+
}
157+
158+
/**
159+
* The UUID identifying the webhook being fired.
160+
*/
161+
public function getWebhookId()
162+
{
163+
return $this->getParsedData()->getWebhookId();
164+
}
165+
166+
/**
167+
* Optional notification payload.
168+
*/
169+
public function getPayload()
170+
{
171+
return $this->getParsedData()->getPayload();
172+
}
173+
174+
/**
175+
* @return int Raw response code
176+
*/
177+
public function getResponseCode()
178+
{
179+
if ($this->getEventTarget() === $this->getParsedData()::EVENT_TARGET_PAYMENT) {
180+
return $this->getPayload()->getResponseCode();
181+
}
182+
}
183+
184+
/**
185+
* @return string Raw response code
186+
*/
187+
public function getAuthCode()
188+
{
189+
if ($this->getEventTarget() === $this->getParsedData()::EVENT_TARGET_PAYMENT) {
190+
return $this->getPayload()->getAuthCode();
191+
}
192+
}
193+
194+
/**
195+
* @return string Raw AVS response code
196+
*/
197+
public function getAvsResponse()
198+
{
199+
if ($this->getEventTarget() === $this->getParsedData()::EVENT_TARGET_PAYMENT) {
200+
return $this->getPayload()->getAvsResponse();
201+
}
202+
}
203+
204+
/**
205+
* @return float authAmount, no currency, no stated units
206+
*/
207+
public function getAuthAmount()
208+
{
209+
if ($this->getEventTarget() === $this->getParsedData()::EVENT_TARGET_PAYMENT) {
210+
return $this->getPayload()->getAuthAmount();
211+
}
212+
}
115213
}

0 commit comments

Comments
 (0)