Skip to content

Commit

Permalink
Issue thephpleague#89 refactor token usage code.
Browse files Browse the repository at this point in the history
Some additional tests will be needed for this.
Sage Pay Direct can take the CC details or a token/reference,
and all always be able to take the CVV.
Sage Pay Server will optional take  atoken/reference.
This will need some further refactering when PayPal support
is implemented, since that overrides both tokens and CC details.
  • Loading branch information
judgej committed Sep 5, 2017
1 parent a8c05b7 commit 0641085
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 47 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@ as a part of a transaction:
* `$gateway->createCard()` - message used to create a card token explicitly.
* `$request->CreateToken()` - transaction option to generate a token with a transaction.

If created explicitly, then a CVV can be provided, and that will be stored against the token
until the token is first used to make a payment. If reused after the first payment, then
a CVV must be supplied each time (if your rules require the CVV to be checked).
If using Sage Pay Server, then the user will be prompted for a CVV on subsequent uses of
the cardReference.

If creating a token or cardReference with a transaction, then the CVV will never be
stored against the token.

The transaction response (or notification request for Sage Pay Server) will provide
the generated token. This is accessed using:

Expand All @@ -247,7 +256,8 @@ are generated.

## Using a Token or CardRererence

To use a token, you must leave the credit card details blank in the `CreditCard` object.
To use a token with Sage Pay Direct, you must leave the credit card details blank in
the `CreditCard` object. Sage Pay Server does not use the credit card details anyway.
To use the token as a single-use token, add it to the transaction request as a token:

`request->setToken($saved_token);`
Expand Down
3 changes: 3 additions & 0 deletions src/Message/AbstractRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ public function setApply3DSecure($value)
return $this->setParameter('apply3DSecure', $value);
}

/**
* Basic authorisation, rtransaction type and protocol version.
*/
protected function getBaseData()
{
$data = array();
Expand Down
126 changes: 83 additions & 43 deletions src/Message/DirectAuthorizeRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,29 @@ class DirectAuthorizeRequest extends AbstractRequest
'diners_club' => 'dc'
);

/**
* The required fields concerning what is being authorised and who
* it is being authorised for.
*/
protected function getBaseAuthorizeData()
{
$this->validate('amount', 'card', 'transactionId');
$card = $this->getCard();

// Start with the authorisation and API version details.
$data = $this->getBaseData();

$data['Description'] = $this->getDescription();
$data['Amount'] = $this->getAmount();
$data['Currency'] = $this->getCurrency();

$data['VendorData'] = $this->getVendorData();
$data['VendorTxCode'] = $this->getTransactionId();
$data['ClientIPAddress'] = $this->getClientIp();

$data['ApplyAVSCV2'] = $this->getApplyAVSCV2() ?: 0;
$data['Apply3DSecure'] = $this->getApply3DSecure() ?: 0;

$data['CreateToken'] = $this->getCreateToken();

// Creating a token should not be permissible at
// the same time as using a token.
if (! $data['CreateToken'] && ($this->getToken() || $this->getCardReference())) {
// If a token has been supplied, and we are NOT asking to generate
// a new token here, then use this token and optionally store it
// again for further use.

$data['Token'] = $this->getToken() ?: $this->getCardReference();

// If we don't have a StoreToken override, then set it according to
// whether we are dealing with a token or a cardReference.

$storeToken = $this->getStoreToken();

if ($storeToken === null) {
// If we are using the token as a cardReference, then keep it stored
// after this transaction for future use.
$storeToken = $this->getCardReference()
? static::STORE_TOKEN_YES
: static::STORE_TOKEN_NO;
}

$data['StoreToken'] = $storeToken;
}

if ($this->getReferrerId()) {
$data['ReferrerID'] = $this->getReferrerId();
}
Expand Down Expand Up @@ -126,32 +107,91 @@ public function getCardholderName()
return $this->getParameter('cardholderName');
}

/**
* If a token or cardReference is being used, then include the details
* of the token in the data.
*/
public function getTokenData($data = array())
{
// Are there token details to add?
if ($this->getToken() || $this->getCardReference()) {
// A card token or reference has been provided.
$data['Token'] = $this->getToken() ?: $this->getCardReference();

// If we don't have a StoreToken override, then set it according to
// whether we are dealing with a token or a cardReference.
// Overriding the default token storage flag is for legacy support.

$storeToken = $this->getStoreToken();

if ($storeToken === null) {
// If we are using the token as a cardReference, then keep it stored
// after this transaction for future use.

$storeToken = $this->getCardReference()
? static::STORE_TOKEN_YES
: static::STORE_TOKEN_NO;
}

$data['StoreToken'] = $storeToken;
}

return $data;
}

/**
* Add the credit card or token details to the data.
*/
public function getData()
{
$data = $this->getBaseAuthorizeData();
$this->getCard()->validate();

if ($this->getCardholderName()) {
$data['CardHolder'] = $this->getCardholderName();
if ($this->getToken() || $this->getCardReference()) {
// If using a token, then set that data.
$data = $this->getTokenData($data);
} else {
$data['CardHolder'] = $this->getCard()->getName();
}
// Otherwise, a credit card has to have been provided.
$this->getCard()->validate();

// Card number should not be provided if token is being provided instead
if (!$this->getToken()) {
$data['CardNumber'] = $this->getCard()->getNumber();
}
if ($this->getCardholderName()) {
$data['CardHolder'] = $this->getCardholderName();
} else {
$data['CardHolder'] = $this->getCard()->getName();
}

// Card number should not be provided if token is being provided instead
if (! $this->getToken()) {
$data['CardNumber'] = $this->getCard()->getNumber();
}

$data['ExpiryDate'] = $this->getCard()->getExpiryDate('my');
$data['CardType'] = $this->getCardBrand();

if ($this->getCard()->getStartMonth() and $this->getCard()->getStartYear()) {
$data['StartDate'] = $this->getCard()->getStartDate('my');
}

$data['CV2'] = $this->getCard()->getCvv();
$data['ExpiryDate'] = $this->getCard()->getExpiryDate('my');
$data['CardType'] = $this->getCardBrand();
if ($this->getCard()->getIssueNumber()) {
$data['IssueNumber'] = $this->getCard()->getIssueNumber();
}

// If we want the card details to be saved on the gateway as a
// token or card reference, then request for that to be done.
$data['CreateToken'] = $this->getCreateToken();

if ($this->getCard()->getStartMonth() and $this->getCard()->getStartYear()) {
$data['StartDate'] = $this->getCard()->getStartDate('my');
if ($this->getCard()->getCvv() !== null) {
$data['CV2'] = $this->getCard()->getCvv();
}
}

if ($this->getCard()->getIssueNumber()) {
$data['IssueNumber'] = $this->getCard()->getIssueNumber();
// A CVV may be supplied whether using a token or credit card details.
// On *first* use of a token for which a CVV was provided, that CVV will
// be used when making a transaction. The CVV will then be deleted by the
// gateway. For each *resuse* of a cardReference, a new CVV must be provided,
// if the security rules require it.

if ($this->getCard()->getCvv() !== null) {
$data['CV2'] = $this->getCard()->getCvv();
}

return $data;
Expand Down
8 changes: 7 additions & 1 deletion src/Message/ServerAuthorizeRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
class ServerAuthorizeRequest extends DirectAuthorizeRequest
{
/**
* The returnUrl is supported for legacy applications.
* Add the optional token details to the base data.
* The returnUrl is supported for legacy applications not using the notifyUrl.
*/
public function getData()
{
Expand All @@ -18,6 +19,11 @@ public function getData()

$data = $this->getBaseAuthorizeData();

// If a token is being used, then include the token data.
// With a valid token or card reference, the user is just asked
// for the CVV and not any remaining card details.
$data = $this->getTokenData($data);

// ReturnUrl is for legacy usage.
$data['NotificationURL'] = $this->getNotifyUrl() ?: $this->getReturnUrl();

Expand Down
8 changes: 6 additions & 2 deletions tests/Message/DirectAuthorizeRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,15 +323,19 @@ public function testExistingCardReferenceCanBeSet()
$this->assertSame(1, $data['StoreToken']);
}

/**
* This has been turned on its head: if a token is provided, then that
* takes priority and the "createToken" flag is ignored.
*/
public function testExistingTokenCannotBeSetIfCreateTokenIsTrue()
{
$this->request->setCreateToken(true);
$this->request->setToken('{ABCDEF}');

$data = $this->request->getData();

$this->assertArrayNotHasKey('Token', $data);
$this->assertSame(1, $data['CreateToken']);
$this->assertArrayNotHasKey('CreateToken', $data);
$this->assertSame('{ABCDEF}', $data['Token']);
}

public function testStoreTokenCanOnlyBeSetIfExistingTokenIsSetInRequest()
Expand Down

0 comments on commit 0641085

Please sign in to comment.