Skip to content

Commit aac7a5a

Browse files
Andy Coatesamacneil
authored andcommitted
Add First Data Connect gateway. Closes #139
1 parent 3c957cb commit aac7a5a

File tree

10 files changed

+424
-0
lines changed

10 files changed

+424
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ The following gateways are already implemented:
9595
* CardSave
9696
* Dummy
9797
* eWAY Rapid 3.0
98+
* First Data Connect
9899
* GoCardless
99100
* Manual
100101
* Migs 2-Party

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"egate",
1616
"eway",
1717
"express",
18+
"first data",
19+
"firstdata",
1820
"gateway",
1921
"gocardless",
2022
"ideal",
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Omnipay\FirstData;
4+
5+
use Omnipay\Common\AbstractGateway;
6+
7+
class ConnectGateway extends AbstractGateway
8+
{
9+
public function getName()
10+
{
11+
return 'First Data Connect';
12+
}
13+
14+
public function getDefaultParameters()
15+
{
16+
return array(
17+
'storeId' => '',
18+
'sharedSecret' => '',
19+
'testMode' => false,
20+
);
21+
}
22+
23+
public function setStoreId($value)
24+
{
25+
return $this->setParameter('storeId', $value);
26+
}
27+
28+
public function getStoreId()
29+
{
30+
return $this->getParameter('storeId');
31+
}
32+
33+
public function setSharedSecret($value)
34+
{
35+
return $this->setParameter('sharedSecret', $value);
36+
}
37+
38+
public function getSharedSecret()
39+
{
40+
return $this->getParameter('sharedSecret');
41+
}
42+
43+
public function purchase(array $parameters = array())
44+
{
45+
return $this->createRequest('\Omnipay\FirstData\Message\PurchaseRequest', $parameters);
46+
}
47+
48+
public function completePurchase(array $parameters = array())
49+
{
50+
return $this->createRequest('\Omnipay\FirstData\Message\CompletePurchaseRequest', $parameters);
51+
}
52+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Omnipay\FirstData\Message;
4+
5+
use Omnipay\Common\Exception\InvalidResponseException;
6+
7+
/**
8+
* First Data Connect Complete Authorize Request
9+
*/
10+
class CompletePurchaseRequest extends PurchaseRequest
11+
{
12+
public function getData()
13+
{
14+
$theirHash = (string) $this->httpRequest->request->get('response_hash');
15+
$dateTime = (string) $this->httpRequest->request->get('txndatetime');
16+
$amount = (string) $this->httpRequest->request->get('chargetotal');
17+
$code = (string) $this->httpRequest->request->get('approval_code');
18+
$ourHash = $this->createResponseHash($amount, $dateTime, $code);
19+
if ($theirHash !== $ourHash) {
20+
throw new InvalidResponseException("Callback hash does not match expected value");
21+
}
22+
23+
return $this->httpRequest->request->all();
24+
}
25+
26+
public function send()
27+
{
28+
return $this->response = new CompletePurchaseResponse($this, $this->getData());
29+
}
30+
31+
/**
32+
* Generate a hash string that matches the format of the one returned by the payment gateway
33+
* @param string $amount
34+
* @param string $dateTime
35+
* @param string $code
36+
* @return string
37+
*/
38+
public function createResponseHash($amount, $dateTime, $code)
39+
{
40+
$this->validate('storeId', 'sharedSecret', 'currency');
41+
42+
$storeId = $this->getStoreId();
43+
$sharedSecret = $this->getSharedSecret();
44+
$currency = $this->getCurrencyNumeric();
45+
46+
$stringToHash = $sharedSecret . $code . $amount . $currency . $dateTime . $storeId;
47+
$ascii = bin2hex($stringToHash);
48+
49+
return sha1($ascii);
50+
}
51+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Omnipay\FirstData\Message;
4+
5+
use Omnipay\Common\Message\AbstractResponse;
6+
7+
/**
8+
* FirstDataConnect Response
9+
*/
10+
class CompletePurchaseResponse extends AbstractResponse
11+
{
12+
public function isSuccessful()
13+
{
14+
return isset($this->data['status']) && $this->data['status'] == 'APPROVED';
15+
}
16+
17+
public function getTransactionReference()
18+
{
19+
return isset($this->data['oid']) ? $this->data['oid'] : null;
20+
}
21+
22+
public function getMessage()
23+
{
24+
return isset($this->data['status']) ? $this->data['status'] : null;
25+
}
26+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
namespace Omnipay\FirstData\Message;
4+
5+
use Omnipay\Common\Message\AbstractRequest;
6+
7+
/**
8+
* FirstDataConnect Authorize Request
9+
*/
10+
class PurchaseRequest extends AbstractRequest
11+
{
12+
protected $liveEndpoint = 'https://www.ipg-online.com/connect/gateway/processing';
13+
protected $testEndpoint = 'https://test.ipg-online.com/connect/gateway/processing';
14+
15+
protected function getDateTime()
16+
{
17+
return date("Y:m:d-H:i:s");
18+
}
19+
20+
public function getStoreId()
21+
{
22+
return $this->getParameter('storeId');
23+
}
24+
25+
public function setStoreId($value)
26+
{
27+
return $this->setParameter('storeId', $value);
28+
}
29+
30+
public function setSharedSecret($value)
31+
{
32+
return $this->setParameter('sharedSecret', $value);
33+
}
34+
35+
public function getSharedSecret()
36+
{
37+
return $this->getParameter('sharedSecret');
38+
}
39+
40+
public function getData()
41+
{
42+
$this->validate('amount', 'card');
43+
44+
$data = array();
45+
$data['storename'] = $this->getStoreId();
46+
$data['txntype'] = 'sale';
47+
$data['timezone'] = 'GMT';
48+
$data['chargetotal'] = $this->getAmount();
49+
$data['txndatetime'] = $this->getDateTime();
50+
$data['hash'] = $this->createHash($data['txndatetime'], $data['chargetotal']);
51+
$data['currency'] = $this->getCurrencyNumeric();
52+
$data['mode'] = 'payonly';
53+
$data['full_bypass'] = 'true';
54+
$data['oid'] = $this->getParameter('transactionId');
55+
56+
$this->getCard()->validate();
57+
58+
$data['cardnumber'] = $this->getCard()->getNumber();
59+
$data['cvm'] = $this->getCard()->getCvv();
60+
$data['expmonth'] = $this->getCard()->getExpiryDate('m');
61+
$data['expyear'] = $this->getCard()->getExpiryDate('y');
62+
63+
$data['responseSuccessURL'] = $this->getParameter('returnUrl');
64+
$data['responseFailURL'] = $this->getParameter('returnUrl');
65+
66+
return $data;
67+
}
68+
69+
public function createHash($dateTime, $amount)
70+
{
71+
$storeId = $this->getStoreId();
72+
$sharedSecret = $this->getSharedSecret();
73+
$currency = $this->getCurrencyNumeric();
74+
$stringToHash = $storeId . $dateTime . $amount . $currency . $sharedSecret;
75+
$ascii = bin2hex($stringToHash);
76+
77+
return sha1($ascii);
78+
}
79+
80+
public function send()
81+
{
82+
return $this->response = new PurchaseResponse($this, $this->getData());
83+
}
84+
85+
public function getEndpoint()
86+
{
87+
return $this->getTestMode() ? $this->testEndpoint : $this->liveEndpoint;
88+
}
89+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Omnipay\FirstData\Message;
4+
5+
use Omnipay\Common\Message\AbstractResponse;
6+
use Omnipay\Common\Message\RedirectResponseInterface;
7+
8+
/**
9+
* First Data Connect Purchase Response
10+
*/
11+
class PurchaseResponse extends AbstractResponse implements RedirectResponseInterface
12+
{
13+
public function isSuccessful()
14+
{
15+
return false;
16+
}
17+
18+
public function isRedirect()
19+
{
20+
return true;
21+
}
22+
23+
public function getRedirectUrl()
24+
{
25+
return $this->getRequest()->getEndpoint();
26+
}
27+
28+
public function getRedirectMethod()
29+
{
30+
return 'POST';
31+
}
32+
33+
public function getRedirectData()
34+
{
35+
return $this->data;
36+
}
37+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
namespace Omnipay\FirstData;
4+
5+
use Omnipay\GatewayTestCase;
6+
7+
class GatewayTest extends GatewayTestCase
8+
{
9+
public function setUp()
10+
{
11+
parent::setUp();
12+
13+
$this->gateway = new ConnectGateway($this->getHttpClient(), $this->getHttpRequest());
14+
$this->gateway->setSharedSecret('96MbdNvxTa');
15+
$this->gateway->setStoreId('1120540155');
16+
17+
$this->options = array(
18+
'amount' => '13.00',
19+
'returnUrl' => 'https://www.example.com/return',
20+
'card' => $this->getValidCard(),
21+
'transactionId' => 'abc123',
22+
'currency' => 'GBP'
23+
);
24+
}
25+
26+
public function testPurchase()
27+
{
28+
$response = $this->gateway->purchase($this->options)->send();
29+
30+
$this->assertFalse($response->isSuccessful());
31+
$this->assertTrue($response->isRedirect());
32+
$this->assertNull($response->getTransactionReference());
33+
$this->assertContains('ipg-online.com/connect/gateway/processing', $response->getRedirectUrl());
34+
}
35+
36+
public function testCompletePurchaseSuccess()
37+
{
38+
$this->getHttpRequest()->request->replace(
39+
array(
40+
'chargetotal' => '110.00',
41+
'response_hash' => '796d7ca236576256236e92900dedfd55be08567a',
42+
'status' => 'APPROVED',
43+
'oid' => 'abc123456',
44+
'txndatetime' => '2013:09:27-16:06:26',
45+
'approval_code' => 'Y:136432:0013649958:PPXM:0015'
46+
)
47+
);
48+
49+
$response = $this->gateway->completePurchase($this->options)->send();
50+
51+
$this->assertTrue($response->isSuccessful());
52+
$this->assertFalse($response->isRedirect());
53+
$this->assertEquals('abc123456', $response->getTransactionReference());
54+
$this->assertSame('APPROVED', $response->getMessage());
55+
}
56+
57+
/**
58+
* @expectedException \Omnipay\Common\Exception\InvalidResponseException
59+
*/
60+
public function testCompletePurchaseInvalidCallbackPassword()
61+
{
62+
$this->getHttpRequest()->request->replace(
63+
array(
64+
'chargetotal' => '110.00',
65+
'response_hash' => 'FAKE',
66+
'status' => 'APPROVED',
67+
'oid' => 'abc123456',
68+
'txndatetime' => '2013:09:27-16:06:26',
69+
'approval_code' => 'Y:136432:0013649958:PPXM:0015'
70+
)
71+
);
72+
73+
$response = $this->gateway->completePurchase($this->options)->send();
74+
}
75+
76+
public function testCompletePurchaseError()
77+
{
78+
$this->getHttpRequest()->request->replace(
79+
array(
80+
'chargetotal' => '110.00',
81+
'response_hash' => '0dfe9e4b3c6306343926207a8814a48f72087cc7',
82+
'status' => 'DECLINED',
83+
'oid' => 'abc1234',
84+
'txndatetime' => '2013:09:27-16:00:19',
85+
'approval_code' => 'N:05:DECLINED'
86+
)
87+
);
88+
89+
$response = $this->gateway->completePurchase($this->options)->send();
90+
91+
$this->assertFalse($response->isSuccessful());
92+
$this->assertFalse($response->isRedirect());
93+
$this->assertEquals('abc1234', $response->getTransactionReference());
94+
$this->assertSame('DECLINED', $response->getMessage());
95+
}
96+
}

0 commit comments

Comments
 (0)