Skip to content

Commit 142a95f

Browse files
committed
Merge pull request thephpleague#23 from judgej/issue_19
Issue 19/16/22
2 parents 463cdc7 + e2c25ff commit 142a95f

11 files changed

+233
-151
lines changed

src/Message/AbstractRequest.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@
55
/**
66
* Authorize.Net Abstract Request
77
*/
8-
abstract class AbstractRequest extends \Omnipay\Common\Message\AbstractRequest
8+
9+
use Omnipay\Common\Message\AbstractRequest as CommonAbstractRequest;
10+
11+
abstract class AbstractRequest extends CommonAbstractRequest
912
{
13+
/**
14+
* Custom field name to send the transaction ID to the notify handler.
15+
*/
16+
const TRANSACTION_ID_PARAM = 'omnipay_transaction_id';
17+
1018
public function getApiLoginId()
1119
{
1220
return $this->getParameter('apiLoginId');
@@ -99,7 +107,13 @@ protected function getBillingData()
99107
{
100108
$data = array();
101109
$data['x_amount'] = $this->getAmount();
110+
111+
// This is deprecated. The invoice number field is reserved for the invoice number.
102112
$data['x_invoice_num'] = $this->getTransactionId();
113+
114+
// A custom field can be used to pass over the merchant site transaction ID.
115+
$data[static::TRANSACTION_ID_PARAM] = $this->getTransactionId();
116+
103117
$data['x_description'] = $this->getDescription();
104118

105119
if ($card = $this->getCard()) {

src/Message/DPMCompleteRequest.php

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,62 +9,6 @@
99
*/
1010
class DPMCompleteRequest extends SIMCompleteAuthorizeRequest
1111
{
12-
public function getData()
13-
{
14-
// The hash sent in the callback from the Authorize.Net gateway.
15-
$hash_posted = strtolower($this->httpRequest->request->get('x_MD5_Hash'));
16-
17-
// The transaction reference generated by the Authorize.Net gateway and sent in the callback.
18-
$posted_transaction_reference = $this->httpRequest->request->get('x_trans_id');
19-
20-
// The amount that the callback has authorized.
21-
$posted_amount = $this->httpRequest->request->get('x_amount');
22-
23-
// Calculate the hash locally, using the shared "hash secret" and login ID.
24-
$hash_calculated = $this->getDpmHash($posted_transaction_reference, $posted_amount);
25-
26-
if ($hash_posted !== $hash_calculated) {
27-
// If the hash is incorrect, then we can't trust the source nor anything sent.
28-
// Throwing exceptions here is probably a bad idea. We are trying to get the data,
29-
// and if it is invalid, then we need to be able to log that data for analysis.
30-
// Except we can't, baceuse the exception means we can't get to the data.
31-
// For now, this is consistent with other OmniPay gateway drivers.
32-
33-
throw new InvalidRequestException('Incorrect hash');
34-
}
35-
36-
// The hashes have passed, but the amount should also be validated against the
37-
// amount in the stored and retrieved transaction. If the application has the
38-
// ability to retrieve the transaction (using the transaction_id sent as a custom
39-
// form field, or perhaps in an otherwise unused field such as x_invoice_id.
40-
41-
$amount = $this->getAmount();
42-
43-
if (isset($amount) && $amount != $posted_amount) {
44-
// The amounts don't match. Someone may have been playing with the
45-
// transaction references.
46-
47-
throw new InvalidRequestException('Incorrect amount');
48-
}
49-
50-
return $this->httpRequest->request->all();
51-
}
52-
53-
/**
54-
* This hash confirms the ransaction has come from the Authorize.Net gateway.
55-
* It confirms the sender knows ther shared hash secret and that the amount and
56-
* transaction reference has not been changed in transit.
57-
*/
58-
public function getDpmHash($transaction_reference, $amount)
59-
{
60-
$key = $this->getHashSecret()
61-
. $this->getApiLoginId()
62-
. $transaction_reference
63-
. $amount;
64-
65-
return md5($key);
66-
}
67-
6812
public function sendData($data)
6913
{
7014
return $this->response = new DPMCompleteResponse($this, $data);

src/Message/DPMCompleteResponse.php

Lines changed: 2 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,79 +2,9 @@
22

33
namespace Omnipay\AuthorizeNet\Message;
44

5-
use Omnipay\Common\Message\AbstractResponse;
6-
use Omnipay\Common\Message\RedirectResponseInterface;
7-
85
/**
9-
* Authorize.Net DPM Complete Authorize Response
10-
* This is the result of handling the callback.
11-
* The result will always be a HTML redirect snippet. This gets
12-
* returned to the gateway, displayed in the user's browser, and a
13-
* redirect is performed using JavaScript and meta refresh (for backup).
14-
* We may want to return to the success page, the failed page or the retry
15-
* page (so the user can correct the form to try again).
6+
* SIM and DPM both have identical needs when handling the notify request.
167
*/
17-
class DPMCompleteResponse extends SIMCompleteAuthorizeResponse implements RedirectResponseInterface
8+
class DPMCompleteResponse extends SIMCompleteAuthorizeResponse
189
{
19-
const RESPONSE_CODE_APPROVED = '1';
20-
const RESPONSE_CODE_DECLINED = '2';
21-
const RESPONSE_CODE_ERROR = '3';
22-
const RESPONSE_CODE_REVIEW = '4';
23-
24-
public function isSuccessful()
25-
{
26-
return isset($this->data['x_response_code'])
27-
&& static::RESPONSE_CODE_APPROVED === $this->data['x_response_code'];
28-
}
29-
30-
/**
31-
* If there is an error in the form, then the user should be able to go back
32-
* to the form and give it another shot.
33-
*/
34-
public function isError()
35-
{
36-
return isset($this->data['x_response_code'])
37-
&& static::RESPONSE_CODE_ERROR === $this->data['x_response_code'];
38-
}
39-
40-
/**
41-
* We are in the callback, and we MUST return a HTML fragment to do a redirect.
42-
* All headers we may return are discarded by the gateway, so we cannot use
43-
* the "Location:" header.
44-
*/
45-
public function isRedirect()
46-
{
47-
return true;
48-
}
49-
50-
/**
51-
* We set POST because the default redirect mechanism in Omnipay Common only
52-
* generates a HTML snippet for POST and not for the GET method.
53-
* The redirect method is actually "HTML", where a HTML page is supplied
54-
* to do a redirect using any method it likes.
55-
*/
56-
public function getRedirectMethod()
57-
{
58-
return 'POST';
59-
}
60-
61-
/**
62-
* We probably do not require any redirect data, if the incomplete transaction
63-
* is still in the user's session and we can inspect the results from the saved
64-
* transaction in the database. We cannot send the result through the redirect
65-
* unless it is hashed so the authorisation result cannot be faked.
66-
*/
67-
public function getRedirectData()
68-
{
69-
return array();
70-
}
71-
72-
/**
73-
* The cancel URL is never handled here - that is a direct link from the gateway.
74-
*/
75-
public function getRedirectUrl()
76-
{
77-
// Leave it for the applicatino to decide where to sent the user.
78-
return;
79-
}
8010
}

src/Message/SIMAuthorizeRequest.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ class SIMAuthorizeRequest extends AbstractRequest
1111

1212
public function getData()
1313
{
14-
$this->validate('amount', 'returnUrl');
14+
$this->validate('amount');
15+
16+
// Either the nodifyUrl or the returnUrl can be provided.
17+
// The returnUrl is deprecated, as strictly this is a notifyUrl.
18+
if (!$this->getNotifyUrl()) {
19+
$this->validate('returnUrl');
20+
}
1521

1622
$data = array();
1723
$data['x_login'] = $this->getApiLoginId();
@@ -28,9 +34,11 @@ public function getData()
2834
$data['x_customer_ip'] = $this->getClientIp();
2935
}
3036

31-
// The returnUrl MUST be set in Authorize.net admin panel under
37+
// The returnUrl MUST be whitelisted in Authorize.net admin panel under
3238
// "Response/Receipt URLs".
33-
$data['x_relay_url'] = $this->getReturnUrl();
39+
// Use the notifyUrl if available, as that is strictly what this is.
40+
// Fall back to returnUrl for BC support.
41+
$data['x_relay_url'] = $this->getNotifyUrl() ?: $this->getReturnUrl();
3442
$data['x_cancel_url'] = $this->getCancelUrl();
3543

3644
if ($this->getCustomerId() !== null) {

src/Message/SIMCompleteAuthorizeRequest.php

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,53 @@
99
*/
1010
class SIMCompleteAuthorizeRequest extends AbstractRequest
1111
{
12+
/**
13+
* Get the transaction ID passed in through the custom field.
14+
* This is used to look up the transaction in storage.
15+
*/
16+
public function getTransactionId()
17+
{
18+
return $this->httpRequest->request->get(static::TRANSACTION_ID_PARAM);
19+
}
20+
1221
public function getData()
1322
{
14-
if (strtolower($this->httpRequest->request->get('x_MD5_Hash')) !== $this->getHash()) {
23+
// The hash sent in the callback from the Authorize.Net gateway.
24+
$hash_posted = strtolower($this->httpRequest->request->get('x_MD5_Hash'));
25+
26+
// The transaction reference generated by the Authorize.Net gateway and sent in the callback.
27+
$posted_transaction_reference = $this->httpRequest->request->get('x_trans_id');
28+
29+
// The amount that the callback has authorized.
30+
$posted_amount = $this->httpRequest->request->get('x_amount');
31+
32+
// Calculate the hash locally, using the shared "hash secret" and login ID.
33+
$hash_calculated = $this->getHash($posted_transaction_reference, $posted_amount);
34+
35+
if ($hash_posted !== $hash_calculated) {
36+
// If the hash is incorrect, then we can't trust the source nor anything sent.
37+
// Throwing exceptions here is probably a bad idea. We are trying to get the data,
38+
// and if it is invalid, then we need to be able to log that data for analysis.
39+
// Except we can't, baceuse the exception means we can't get to the data.
40+
// For now, this is consistent with other OmniPay gateway drivers.
41+
1542
throw new InvalidRequestException('Incorrect hash');
1643
}
1744

45+
// The hashes have passed, but the amount should also be validated against the
46+
// amount in the stored and retrieved transaction. If the application has the
47+
// ability to retrieve the transaction (using the transaction_id sent as a custom
48+
// form field, or perhaps in an otherwise unused field such as x_invoice_id.
49+
50+
$amount = $this->getAmount();
51+
52+
if (isset($amount) && $amount != $posted_amount) {
53+
// The amounts don't match. Someone may have been playing with the
54+
// transaction references.
55+
56+
throw new InvalidRequestException('Incorrect amount');
57+
}
58+
1859
return $this->httpRequest->request->all();
1960
}
2061

@@ -23,9 +64,16 @@ public function getData()
2364
* The transaction reference and the amount are both sent by the remote gateway (x_trans_id
2465
* and x_amount) and it is those that should be checked against.
2566
*/
26-
public function getHash()
67+
public function getHash($transaction_reference, $amount)
2768
{
28-
return md5($this->getHashSecret().$this->getApiLoginId().$this->getTransactionId().$this->getAmount());
69+
$key = array(
70+
$this->getHashSecret(),
71+
$this->getApiLoginId(),
72+
$transaction_reference,
73+
$amount,
74+
);
75+
76+
return md5(implode('', $key));
2977
}
3078

3179
public function sendData($data)

src/Message/SIMCompleteAuthorizeResponse.php

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,33 @@
33
namespace Omnipay\AuthorizeNet\Message;
44

55
use Omnipay\Common\Message\AbstractResponse;
6+
use Omnipay\Common\Message\RedirectResponseInterface;
7+
use Symfony\Component\HttpFoundation\Response as HttpResponse;
68

79
/**
810
* Authorize.Net SIM Complete Authorize Response
911
*/
10-
class SIMCompleteAuthorizeResponse extends AbstractResponse
12+
class SIMCompleteAuthorizeResponse extends AbstractResponse implements RedirectResponseInterface
1113
{
14+
// Response codes returned by Authorize.Net
15+
16+
const RESPONSE_CODE_APPROVED = '1';
17+
const RESPONSE_CODE_DECLINED = '2';
18+
const RESPONSE_CODE_ERROR = '3';
19+
const RESPONSE_CODE_REVIEW = '4';
20+
1221
public function isSuccessful()
1322
{
14-
return isset($this->data['x_response_code']) && '1' === $this->data['x_response_code'];
23+
return static::RESPONSE_CODE_APPROVED === $this->getCode();
24+
}
25+
26+
/**
27+
* If there is an error in the form, then the user should be able to go back
28+
* to the form and give it another shot.
29+
*/
30+
public function isError()
31+
{
32+
return static::RESPONSE_CODE_ERROR === $this->getCode();
1533
}
1634

1735
public function getTransactionReference()
@@ -33,4 +51,68 @@ public function getCode()
3351
{
3452
return isset($this->data['x_response_code']) ? $this->data['x_response_code'] : null;
3553
}
54+
55+
/**
56+
* This message is handled in a notify, where a HTML redirect must be performed.
57+
*/
58+
public function isRedirect()
59+
{
60+
return true;
61+
}
62+
63+
/**
64+
* The merchant site notify handler needs to set the returnUrl in the complete request.
65+
*/
66+
public function getRedirectUrl()
67+
{
68+
return $this->request->getReturnUrl();
69+
}
70+
71+
public function getRedirectMethod()
72+
{
73+
return 'GET';
74+
}
75+
76+
/**
77+
* There is no redirect data to send; the aim is just to get the user to a URL
78+
* by delivering a HTML page.
79+
*/
80+
public function getRedirectData()
81+
{
82+
return array();
83+
}
84+
85+
/**
86+
* Authorize.Net requires a redirect in a HTML page.
87+
* The OmniPay redirect helper will only provide a HTML page for the POST method
88+
* and then implements that through a self-submitting form, which will generate
89+
* browser warnings if returning to a non-SSL page. This JavScript and meta refresh
90+
* page avoids the security warning. No data is sent in this redirect, as that will
91+
* have all been saved with the transaction in storage.
92+
*/
93+
public function getRedirectResponse()
94+
{
95+
$output = <<<ENDHTML
96+
<!DOCTYPE html>
97+
<html>
98+
<head>
99+
<title>Redirecting...</title>
100+
<meta http-equiv="refresh" content="0;url=%1\$s" />
101+
</head>
102+
<body>
103+
<p>Redirecting to <a href="%1\$s">payment complete page</a>...</p>
104+
<script type="text/javascript" charset="utf-8">
105+
window.location="%1\$s";
106+
</script>
107+
</body>
108+
</html>
109+
ENDHTML;
110+
111+
$output = sprintf(
112+
$output,
113+
htmlentities($this->getRedirectUrl(), ENT_QUOTES, 'UTF-8', false)
114+
);
115+
116+
return HttpResponse::create($output);
117+
}
36118
}

0 commit comments

Comments
 (0)