Skip to content

Commit 463cdc7

Browse files
committed
Merge pull request thephpleague#18 from academe/dpm_support
DPM support for Authorize.Net
2 parents 7b51669 + f83008e commit 463cdc7

20 files changed

+933
-8
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ The following gateways are provided by this package:
3333

3434
* AuthorizeNet_AIM
3535
* AuthorizeNet_SIM
36+
* AuthorizeNet_DPM
3637

3738
For general usage instructions, please see the main [Omnipay](https://github.com/thephpleague/omnipay)
3839
repository.

src/AIMGateway.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ public function getName()
1717
public function getDefaultParameters()
1818
{
1919
return array(
20-
'apiLoginId' => '',
21-
'transactionKey' => '',
22-
'testMode' => false,
23-
'developerMode' => false,
20+
'apiLoginId' => '',
21+
'transactionKey' => '',
22+
'testMode' => false,
23+
'developerMode' => false,
24+
'liveEndpoint' => 'https://secure.authorize.net/gateway/transact.dll',
25+
'developerEndpoint' => 'https://test.authorize.net/gateway/transact.dll',
2426
);
2527
}
2628

@@ -54,6 +56,32 @@ public function setDeveloperMode($value)
5456
return $this->setParameter('developerMode', $value);
5557
}
5658

59+
public function setEndpoints($endpoints)
60+
{
61+
$this->setParameter('liveEndpoint', $endpoints['live']);
62+
return $this->setParameter('developerEndpoint', $endpoints['developer']);
63+
}
64+
65+
public function getLiveEndpoint()
66+
{
67+
return $this->getParameter('liveEndpoint');
68+
}
69+
70+
public function setLiveEndpoint($value)
71+
{
72+
return $this->setParameter('liveEndpoint', $value);
73+
}
74+
75+
public function getDeveloperEndpoint()
76+
{
77+
return $this->getParameter('developerEndpoint');
78+
}
79+
80+
public function setDeveloperEndpoint($value)
81+
{
82+
return $this->setParameter('developerEndpoint', $value);
83+
}
84+
5785
public function authorize(array $parameters = array())
5886
{
5987
return $this->createRequest('\Omnipay\AuthorizeNet\Message\AIMAuthorizeRequest', $parameters);

src/DPMGateway.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Omnipay\AuthorizeNet;
4+
5+
/**
6+
* Authorize.Net DPM (Direct Post Method) Class
7+
*/
8+
class DPMGateway extends SIMGateway
9+
{
10+
public function getName()
11+
{
12+
return 'Authorize.Net DPM';
13+
}
14+
15+
/**
16+
* Helper to generate the authorize direct-post form.
17+
*/
18+
public function authorize(array $parameters = array())
19+
{
20+
return $this->createRequest('\Omnipay\AuthorizeNet\Message\DPMAuthorizeRequest', $parameters);
21+
}
22+
23+
/**
24+
* Get, validate, interpret and respond to the Authorize.Net callback.
25+
*/
26+
public function completeAuthorize(array $parameters = array())
27+
{
28+
return $this->createRequest('\Omnipay\AuthorizeNet\Message\DPMCompleteRequest', $parameters);
29+
}
30+
31+
/**
32+
* Helper to generate the purchase direct-post form.
33+
*/
34+
public function purchase(array $parameters = array())
35+
{
36+
return $this->createRequest('\Omnipay\AuthorizeNet\Message\DPMPurchaseRequest', $parameters);
37+
}
38+
39+
/**
40+
* Get, validate, interpret and respond to the Authorize.Net callback.
41+
*/
42+
public function completePurchase(array $parameters = array())
43+
{
44+
return $this->createRequest('\Omnipay\AuthorizeNet\Message\DPMCompleteRequest', $parameters);
45+
}
46+
}

src/Message/AbstractRequest.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
*/
88
abstract class AbstractRequest extends \Omnipay\Common\Message\AbstractRequest
99
{
10-
protected $liveEndpoint = 'https://secure.authorize.net/gateway/transact.dll';
11-
protected $developerEndpoint = 'https://test.authorize.net/gateway/transact.dll';
12-
1310
public function getApiLoginId()
1411
{
1512
return $this->getParameter('apiLoginId');
@@ -60,6 +57,29 @@ public function setHashSecret($value)
6057
return $this->setParameter('hashSecret', $value);
6158
}
6259

60+
public function getLiveEndpoint()
61+
{
62+
return $this->getParameter('liveEndpoint');
63+
}
64+
65+
public function setLiveEndpoint($value)
66+
{
67+
return $this->setParameter('liveEndpoint', $value);
68+
}
69+
70+
public function setDeveloperEndpoint($value)
71+
{
72+
return $this->setParameter('developerEndpoint', $value);
73+
}
74+
75+
public function getDeveloperEndpoint()
76+
{
77+
return $this->getParameter('developerEndpoint');
78+
}
79+
80+
/**
81+
* Base data used only for the AIM API.
82+
*/
6383
protected function getBaseData()
6484
{
6585
$data = array();
@@ -124,6 +144,10 @@ public function sendData($data)
124144

125145
public function getEndpoint()
126146
{
127-
return $this->getDeveloperMode() ? $this->developerEndpoint : $this->liveEndpoint;
147+
if ($this->getDeveloperMode()) {
148+
return $this->getParameter('developerEndpoint');
149+
} else {
150+
return $this->getParameter('liveEndpoint');
151+
}
128152
}
129153
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace Omnipay\AuthorizeNet\Message;
4+
5+
/**
6+
* Authorize.Net DPM Authorize Request.
7+
* Takes the data that will be used to create the direct-post form.
8+
*/
9+
class DPMAuthorizeRequest extends SIMAuthorizeRequest
10+
{
11+
protected $action = 'AUTH_ONLY';
12+
13+
public function getData()
14+
{
15+
$data = parent::getData();
16+
17+
// If x_show_form is set, then the form will be displayed on the Authorize.Net
18+
// gateway, in a similar way to the SIM gateway. The DPM documentation does NOT
19+
// make this clear at all.
20+
// Since x_show_form is set in the SIM gateway, make sure we unset it here.
21+
22+
unset($data['x_show_form']);
23+
24+
// Must be set for DPM.
25+
// This directs all errors to the relay response.
26+
27+
$data['x_relay_always'] = 'TRUE';
28+
29+
// The card details are optional.
30+
// They will most likely only be used for development and testing.
31+
// The card fields are still needed in the direct-post form regardless.
32+
33+
if ($this->getCard()) {
34+
$data['x_card_num'] = $this->getCard()->getNumber();
35+
36+
// Workaround for https://github.com/thephpleague/omnipay-common/issues/29
37+
$expiry_date = $this->getCard()->getExpiryDate('my');
38+
$data['x_exp_date'] = ($expiry_date === '1299' ? '' : $expiry_date);
39+
40+
$data['x_card_code'] = $this->getCard()->getCvv();
41+
} else {
42+
$data['x_card_num'] = '';
43+
$data['x_exp_date'] = '';
44+
$data['x_card_code'] = '';
45+
}
46+
47+
return $data;
48+
}
49+
50+
51+
/**
52+
* Given the DPM data, we want to turn it into a form for the user to submit to Authorize.net
53+
* The form may have most of the fields hidden, or may allow the user to change some details -
54+
* that depends on the use-case.
55+
* So this method will provide us with an object used to build the form.
56+
*/
57+
public function sendData($data)
58+
{
59+
return $this->response = new DPMResponse($this, $data, $this->getEndpoint());
60+
}
61+
}

src/Message/DPMCompleteRequest.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace Omnipay\AuthorizeNet\Message;
4+
5+
use Omnipay\Common\Exception\InvalidRequestException;
6+
7+
/**
8+
* Authorize.Net DPM Complete Authorize Request
9+
*/
10+
class DPMCompleteRequest extends SIMCompleteAuthorizeRequest
11+
{
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+
68+
public function sendData($data)
69+
{
70+
return $this->response = new DPMCompleteResponse($this, $data);
71+
}
72+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace Omnipay\AuthorizeNet\Message;
4+
5+
use Omnipay\Common\Message\AbstractResponse;
6+
use Omnipay\Common\Message\RedirectResponseInterface;
7+
8+
/**
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).
16+
*/
17+
class DPMCompleteResponse extends SIMCompleteAuthorizeResponse implements RedirectResponseInterface
18+
{
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+
}
80+
}

src/Message/DPMPurchaseRequest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Omnipay\AuthorizeNet\Message;
4+
5+
/**
6+
* Authorize.Net DPM Purchase Request (aka "Authorize and Capture")
7+
*/
8+
class DPMPurchaseRequest extends DPMAuthorizeRequest
9+
{
10+
protected $action = 'AUTH_CAPTURE';
11+
}

0 commit comments

Comments
 (0)