Skip to content

Commit 92a6ca9

Browse files
Enable repeat purchases to be done via the purchase (and authorize) methods
This is more compatible with other processors that accept a token or card reference against the purchase and authorize actions in order to process a new payment using information stored on file from previous interactions
1 parent ecc048d commit 92a6ca9

6 files changed

+139
-20
lines changed

README.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -771,8 +771,8 @@ admin panel.
771771
* `refund()`
772772
* `void()` - void a purchase
773773
* `abort()` - abort an authorization before it is captured
774-
* `repeatAuthorize()` - new authorization based on past transaction
775-
* `repeatPurchase()` - new purchase based on past transaction
774+
* `repeatAuthorize()` - new authorization based on past transaction (deprecated, use authorize with cardReference)
775+
* `repeatPurchase()` - new purchase based on past transaction (deprecated, use purchase with cardReference)
776776
* `deleteCard()` - remove a cardReference or token from the account
777777

778778
### Repeat Authorize/Purchase
@@ -782,10 +782,14 @@ You will need the `transactionReference` of the original transaction.
782782
The `transactionReference` will be a JSON string containing the four pieces of
783783
information the gateway needs to reuse the transaction.
784784

785+
Although a separate repeatPurchase/repeatAuthorize request exists it is
786+
recommended you use purchase along with the transactionReference for
787+
interoperability with other Omnipay gateways.
788+
785789
```php
786-
// repeatAuthorize() or repeatPurchase()
790+
// use authorize or purchase to process a repeat payment.
787791

788-
$repeatRequest = $gateway->repeatAuthorize([
792+
$repeatRequest = $gateway->authorize([
789793
'transactionReference' => $originalTransactionReference,
790794
// or
791795
'securityKey' => $originalSecurityKey,

src/Message/AbstractRequest.php

+21
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,25 @@ public function setTransactionReference($value)
648648

649649
return parent::setTransactionReference($value);
650650
}
651+
652+
/**
653+
* Is this a repeat transaction.
654+
*
655+
* For an account to repeat transactions based on an
656+
* initial approval they need to get continuous authorization
657+
* enabled by Sagepay.
658+
*
659+
* https://www.opayo.co.uk/support/12/36/transaction-types
660+
*
661+
* These can be identified because the details from the previous
662+
* transaction are passed in to be re-used. In this instance
663+
* the txn_type is altered and the params sent include the details
664+
* of the previous transaction.
665+
*
666+
* @return bool
667+
*/
668+
protected function isRepeatTransaction()
669+
{
670+
return !empty($this->getRelatedTransactionId());
671+
}
651672
}

src/Message/DirectAuthorizeRequest.php

+27-11
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ class DirectAuthorizeRequest extends AbstractRequest
2121
*/
2222
public function getTxType()
2323
{
24-
if ($this->getUseAuthenticate()) {
24+
if ($this->isRepeatTransaction()) {
25+
return static::TXTYPE_REPEATDEFERRED;
26+
} elseif ($this->getUseAuthenticate()) {
2527
return static::TXTYPE_AUTHENTICATE;
2628
} else {
2729
return static::TXTYPE_DEFERRED;
@@ -41,7 +43,12 @@ public function getService()
4143
*/
4244
protected function getBaseAuthorizeData()
4345
{
44-
$this->validate('amount', 'card', 'transactionId');
46+
$this->validate('amount', 'transactionId');
47+
if (!$this->isRepeatTransaction()) {
48+
// If we have a related transation id the card is not required
49+
// as we are using the cardReference.
50+
$this->validate('card', );
51+
}
4552

4653
// Start with the authorisation and API version details.
4754
$data = $this->getBaseData();
@@ -63,18 +70,27 @@ protected function getBaseAuthorizeData()
6370
$data['ReferrerID'] = $this->getReferrerId();
6471
}
6572

66-
// Billing details
67-
68-
$data = $this->getBillingAddressData($data);
69-
70-
// Shipping details
71-
72-
$data = $this->getDeliveryAddressData($data);
73+
if ($this->getRelatedTransactionId()) {
74+
// The following parameters allow the related transaction ID to be re-used.
75+
$data['RelatedVendorTxCode'] = $this->getRelatedTransactionId();
76+
$data['RelatedVPSTxId'] = $this->getVpsTxId();
77+
$data['RelatedSecurityKey'] = $this->getSecurityKey();
78+
$data['RelatedTxAuthNo'] = $this->getTxAuthNo();
79+
}
7380

7481
$card = $this->getCard();
82+
// Card is optional for repeatPurchase so only process the following
83+
// if it is present.
84+
if ($card) {
85+
// Billing details
86+
$data = $this->getBillingAddressData($data);
87+
88+
// Shipping details
89+
$data = $this->getDeliveryAddressData($data);
7590

76-
if ($card->getEmail()) {
77-
$data['CustomerEMail'] = $card->getEmail();
91+
if ($card->getEmail()) {
92+
$data['CustomerEMail'] = $card->getEmail();
93+
}
7894
}
7995

8096
if ((bool)$this->getUseOldBasketFormat()) {

src/Message/ServerAuthorizeRequest.php

+25-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class ServerAuthorizeRequest extends DirectAuthorizeRequest
99
{
1010
public function getService()
1111
{
12-
return static::SERVICE_SERVER_REGISTER;
12+
return $this->isRepeatTransaction() ? static::SERVICE_REPEAT : static::SERVICE_SERVER_REGISTER;
1313
}
1414

1515
/**
@@ -20,9 +20,7 @@ public function getService()
2020
*/
2121
public function getData()
2222
{
23-
if (! $this->getReturnUrl()) {
24-
$this->validate('notifyUrl');
25-
}
23+
$this->checkRequiredFields();
2624

2725
$data = $this->getBaseAuthorizeData();
2826

@@ -82,4 +80,27 @@ protected function getBaseAuthorizeData()
8280

8381
return $data;
8482
}
83+
84+
/**
85+
* Check necessary fields are passed in.
86+
*
87+
* @throws \Omnipay\Common\Exception\InvalidRequestException
88+
*/
89+
protected function checkRequiredFields()
90+
{
91+
if (!$this->isRepeatTransaction()) {
92+
if (!$this->getReturnUrl()) {
93+
$this->validate('notifyUrl');
94+
}
95+
} else {
96+
$this->validate(
97+
'relatedTransactionId',
98+
'vpsTxId',
99+
'securityKey',
100+
'txAuthNo',
101+
'currency',
102+
'description'
103+
);
104+
}
105+
}
85106
}

src/Message/ServerPurchaseRequest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ public function getData()
1919
*/
2020
public function getTxType()
2121
{
22-
return static::TXTYPE_PAYMENT;
22+
return $this->getRelatedTransactionId() ? static::TXTYPE_REPEAT : static::TXTYPE_PAYMENT;
2323
}
2424
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace Omnipay\SagePay\Message;
4+
5+
use Omnipay\Tests\TestCase;
6+
7+
/**
8+
* Class SharedRepeatPurchaseViaPurchaseRequestTest
9+
*
10+
* Tests for using the purchase action to process repeat
11+
* purchases.
12+
*
13+
* @package Omnipay\SagePay\Message
14+
*/
15+
class ServerRepeatPurchaseViaPurchaseRequestTest extends TestCase
16+
{
17+
/**
18+
* @var \Omnipay\SagePay\Message\SharedPurchaseRequest $request
19+
*/
20+
protected $request;
21+
22+
public function setUp()
23+
{
24+
parent::setUp();
25+
26+
$this->request = new ServerPurchaseRequest($this->getHttpClient(), $this->getHttpRequest());
27+
$this->request->initialize(
28+
array(
29+
'amount' => '12.00',
30+
'currency' => 'EUR',
31+
'transactionId' => '123',
32+
)
33+
);
34+
}
35+
36+
public function testSettingOfRelatedTransaction()
37+
{
38+
$relatedTransactionRef =
39+
'{"SecurityKey":"F6AF4AIB1G","TxAuthNo":"1518884596","VPSTxId":"{9EC5D0BC-A816-E8C3-859A-55C1E476E7C2}","VendorTxCode":"D6429BY7x2217743"}';
40+
$this->request->setTransactionReference($relatedTransactionRef);
41+
$this->request->setDescription('testSettingOfRelatedTransaction');
42+
$data = $this->request->getData();
43+
44+
$this->assertEquals('12.00', $data['Amount'], 'Transaction amount does not match');
45+
$this->assertEquals('EUR', $data['Currency'], 'Currency code does not match');
46+
$this->assertEquals('123', $data['VendorTxCode'], 'Transaction ID does not match');
47+
$this->assertEquals('F6AF4AIB1G', $data['RelatedSecurityKey'], 'Security Key does not match');
48+
$this->assertEquals('{9EC5D0BC-A816-E8C3-859A-55C1E476E7C2}', $data['RelatedVPSTxId'],
49+
'Related VPSTxId does not match');
50+
$this->assertEquals('D6429BY7x2217743', $data['RelatedVendorTxCode'], 'Related VendorTxCode does not match');
51+
$this->assertEquals('1518884596', $data['RelatedTxAuthNo'], 'Related TxAuthNo does not match');
52+
53+
$this->assertEquals('REPEAT', $data['TxType']);
54+
$this->assertEquals('repeat', $this->request->getService());
55+
}
56+
57+
}

0 commit comments

Comments
 (0)