Skip to content

Commit 41b9f16

Browse files
dchestertonamacneil
authored andcommitted
Add support for SagePay ERROR and INVALID responses. Closes thephpleague#120
1 parent 4e39a37 commit 41b9f16

File tree

2 files changed

+115
-6
lines changed

2 files changed

+115
-6
lines changed

src/Omnipay/SagePay/Message/ServerCompleteAuthorizeResponse.php

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,91 @@ public function getTransactionReference()
2929
/**
3030
* Confirm (Sage Pay Server only)
3131
*
32+
* Notify Sage Pay you received the payment details and wish to confirm the payment, and
33+
* provide a URL to forward the customer to.
34+
*
35+
* @param string URL to forward the customer to. Note this is different to your standard
36+
* return controller action URL.
37+
* @param string Optional human readable reasons for accepting the transaction.
38+
*/
39+
public function confirm($nextUrl, $detail = null)
40+
{
41+
$this->sendResponse('OK', $nextUrl, $detail);
42+
}
43+
44+
/**
45+
* Error (Sage Pay Server only)
46+
*
47+
* Notify Sage Pay you received the payment details but there was an error and the payment
48+
* cannot be completed. Error should be called rarely, and only when something unforseen
49+
* has happened on your server or database.
50+
*
51+
* @param string URL to foward the customer to. Note this is different to your standard
52+
* return controller action URL.
53+
* @param string Optional human readable reasons for not accepting the transaction.
54+
*/
55+
public function error($nextUrl, $detail = null)
56+
{
57+
$this->sendResponse('ERROR', $nextUrl, $detail);
58+
}
59+
60+
/**
61+
* Invalid (Sage Pay Server only)
62+
*
63+
* Notify Sage Pay you received the payment details but they were invalid and the payment
64+
* cannot be completed. Invalid should be called if you are not happy with the contents
65+
* of the POST, such as the MD5 hash signatures did not match or you do not wish to proceed
66+
* with the order.
67+
*
68+
* @param string URL to foward the customer to. Note this is different to your standard
69+
* return controller action URL.
70+
* @param string Optional human readable reasons for not accepting the transaction.
71+
*/
72+
public function invalid($nextUrl, $detail = null)
73+
{
74+
$this->sendResponse('INVALID', $nextUrl, $detail);
75+
}
76+
77+
/**
78+
* Respond to SagePay confirming or rejecting the payment.
79+
*
3280
* Sage Pay Server does things backwards compared to every other gateway (including Sage Pay
3381
* Direct). The return URL is called by their server, and they expect you to confirm receipt
3482
* and then pass a URL for them to forward the customer to.
3583
*
3684
* Because of this, an extra step is required. In your return controller, after calling
37-
* $gateway->completePurchase(), you should update your database with details of the
38-
* successful payment. You must then call $response->confirm() to notify Sage Pay you
39-
* received the payment details, and provide a URL to forward the customer to.
85+
* $gateway->completePurchase(), you should attempt to process the payment. You must then call
86+
* either $response->confirm(), $response->error() or $response->invalid() to notify Sage Pay
87+
* whether to complete the payment or not, and provide a URL to forward the customer to.
4088
*
4189
* Keep in mind your original confirmPurchase() script is being called by Sage Pay, not
4290
* the customer.
4391
*
44-
* @param string URL to foward the customer to. Note this is different to your standard
92+
* @param string The status to send to Sage Pay, either OK, INVALID or ERROR.
93+
* @param string URL to forward the customer to. Note this is different to your standard
4594
* return controller action URL.
95+
* @param string Optional human readable reasons for accepting the transaction.
96+
*/
97+
public function sendResponse($status, $nextUrl, $detail = null)
98+
{
99+
$message = "Status=$status\r\nRedirectUrl=$nextUrl";
100+
101+
if (null !== $detail) {
102+
$message .= "\r\nStatusDetail=".$detail;
103+
}
104+
105+
$this->exitWith($message);
106+
}
107+
108+
/**
109+
* Exit to ensure no other HTML, headers, comments, or text are included.
110+
*
111+
* @access private
112+
* @codeCoverageIgnore
46113
*/
47-
public function confirm($nextUrl)
114+
public function exitWith($message)
48115
{
49-
exit("Status=OK\r\nRedirectUrl=".$nextUrl);
116+
echo $message;
117+
exit;
50118
}
51119
}

tests/Omnipay/SagePay/Message/ServerCompleteAuthorizeResponseTest.php

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

55
use Omnipay\TestCase;
6+
use Mockery as m;
67

78
class ServerCompleteAuthorizeResponseTest extends TestCase
89
{
@@ -45,4 +46,44 @@ public function testServerCompleteAuthorizeResponseFailure()
4546
$this->assertNull($response->getTransactionReference());
4647
$this->assertNull($response->getMessage());
4748
}
49+
50+
public function testConfirm()
51+
{
52+
$response = m::mock('\Omnipay\SagePay\Message\ServerCompleteAuthorizeResponse[sendResponse]');
53+
$response->shouldReceive('sendResponse')->once()->with('OK', 'https://www.example.com/', 'detail');
54+
55+
$response->confirm('https://www.example.com/', 'detail');
56+
}
57+
58+
public function testError()
59+
{
60+
$response = m::mock('\Omnipay\SagePay\Message\ServerCompleteAuthorizeResponse[sendResponse]');
61+
$response->shouldReceive('sendResponse')->once()->with('ERROR', 'https://www.example.com/', 'detail');
62+
63+
$response->error('https://www.example.com/', 'detail');
64+
}
65+
66+
public function testInvalid()
67+
{
68+
$response = m::mock('\Omnipay\SagePay\Message\ServerCompleteAuthorizeResponse[sendResponse]');
69+
$response->shouldReceive('sendResponse')->once()->with('INVALID', 'https://www.example.com/', 'detail');
70+
71+
$response->invalid('https://www.example.com/', 'detail');
72+
}
73+
74+
public function testSendResponse()
75+
{
76+
$response = m::mock('\Omnipay\SagePay\Message\ServerCompleteAuthorizeResponse[exitWith]');
77+
$response->shouldReceive('exitWith')->once()->with("Status=FOO\r\nRedirectUrl=https://www.example.com/");
78+
79+
$response->sendResponse('FOO', 'https://www.example.com/');
80+
}
81+
82+
public function testSendResponseDetail()
83+
{
84+
$response = m::mock('\Omnipay\SagePay\Message\ServerCompleteAuthorizeResponse[exitWith]');
85+
$response->shouldReceive('exitWith')->once()->with("Status=FOO\r\nRedirectUrl=https://www.example.com/\r\nStatusDetail=Bar");
86+
87+
$response->sendResponse('FOO', 'https://www.example.com/', 'Bar');
88+
}
4889
}

0 commit comments

Comments
 (0)