Skip to content

Commit

Permalink
Merge pull request #3 from Strikewood/feature/token-billing
Browse files Browse the repository at this point in the history
Implement tokenized billing
  • Loading branch information
einkoro committed May 4, 2015
2 parents 6d8a266 + 3abefd9 commit 3c51224
Show file tree
Hide file tree
Showing 9 changed files with 463 additions and 15 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ This package implements the following methods:
* ``refund($options)`` – refund an already processed (settled) transaction.
* ``void($options)`` – reverse a previously authorized (unsettled) transaction.
* ``status($options)`` – check the status of a previous transaction.
* ``createCard($options)`` – create a stored card and return the reference token for future transactions.
* ``updateCard($options)`` – update a stored card's expiry or customer reference.

For general usage instructions, please see the [Omnipay documentation](http://omnipay.thephpleague.com/).

Expand Down
69 changes: 69 additions & 0 deletions src/CreditCard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Omnipay\FirstAtlanticCommerce;

use Omnipay\Common\CreditCard as BaseCreditCard;
use Omnipay\Common\Exception\InvalidCreditCardException;

class CreditCard extends BaseCreditCard
{
/**
* Validate this credit card. If the card is invalid, InvalidCreditCardException is thrown.
*
* This method is called internally by gateways to avoid wasting time with an API call
* when the credit card is clearly invalid.
*
* Falls back to validating number, cvv, expiryMonth, expiryYear if no parameters are present.
*
* @param string ... Optional variable length list of required parameters
* @throws InvalidCreditCardException
*/
public function validate()
{
$parameters = func_get_args();

if ( count($parameters) == 0 )
{
$parameters = ['number', 'cvv', 'expiryMonth', 'expiryYear'];
}

foreach ($parameters as $key)
{
$value = $this->parameters->get($key);

if ( empty($value) )
{
throw new InvalidCreditCardException("The $key parameter is required");
}
}

if ( isset($parameters['expiryMonth']) && isset($parameters['expiryYear']) )
{
if ( $this->getExpiryDate('Ym') < gmdate('Ym') )
{
throw new InvalidCreditCardException('Card has expired');
}
}

if ( isset($parameters['number']) )
{
if ( !Helper::validateLuhn( $this->getNumber() ) )
{
throw new InvalidCreditCardException('Card number is invalid');
}

if ( !is_null( $this->getNumber() ) && !preg_match( '/^\d{12,19}$/i', $this->getNumber() ) )
{
throw new InvalidCreditCardException('Card number should have 12 to 19 digits');
}
}

if ( isset($parameters['cvv']) )
{
if ( !is_null( $this->getCvv() ) && !preg_match( '/^\d{3,4}$/i', $this->getCvv() ) )
{
throw new InvalidCreditCardException('Card CVV should have 3 to 4 digits');
}
}
}
}
24 changes: 24 additions & 0 deletions src/Gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,28 @@ public function status(array $parameters = [])
{
return $this->createRequest('\Omnipay\FirstAtlanticCommerce\Message\StatusRequest', $parameters);
}

/**
* Create a stored card and return the reference token for future transactions.
*
* @param array $parameters
*
* @return \Omnipay\FirstAtlanticCommerce\Message\CreateCardRequest
*/
public function createCard(array $parameters = [])
{
return $this->createRequest('\Omnipay\FirstAtlanticCommerce\Message\CreateCardRequest', $parameters);
}

/**
* Update a stored card.
*
* @param array $parameters
*
* @return \Omnipay\FirstAtlanticCommerce\Message\UpdateCardRequest
*/
public function updateCard(array $parameters = [])
{
return $this->createRequest('\Omnipay\FirstAtlanticCommerce\Message\UpdateCardRequest', $parameters);
}
}
18 changes: 18 additions & 0 deletions src/Message/AbstractRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Omnipay\FirstAtlanticCommerce\Message;

use Omnipay\Common\Message\AbstractRequest as BaseAbstractRequest;
use Omnipay\FirstAtlanticCommerce\CreditCard;
use Omnipay\FirstAtlanticCommerce\ParameterTrait;
use SimpleXMLElement;

Expand Down Expand Up @@ -125,4 +126,21 @@ protected function xmlSerialize($data, $xml = null)

return $xml->asXML();
}

/**
* Sets the card.
*
* @param CreditCard $value
*
* @return AbstractRequest Provides a fluent interface
*/
public function setCard($value)
{
if ($value && !$value instanceof CreditCard)
{
$value = new CreditCard($value);
}

return $this->setParameter('card', $value);
}
}
53 changes: 38 additions & 15 deletions src/Message/AuthorizeRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Omnipay\FirstAtlanticCommerce\Message;

use Alcohol\ISO3166;
use Omnipay\Common\Exception\InvalidRequestException;
use Omnipay\FirstAtlanticCommerce\Message\AbstractRequest;

/**
Expand Down Expand Up @@ -48,7 +49,42 @@ protected function generateSignature()
public function getData()
{
$this->validate('merchantId', 'merchantPassword', 'acquirerId', 'transactionId', 'amount', 'currency', 'card');
$this->getCard()->validate();

// Check for AVS and require billingAddress1 and billingPostcode
if ( $this->getRequireAvsCheck() )
{
$this->getCard()->validate('billingAddress1', 'billingPostcode');
}

// Tokenized cards require the CVV and nothing else, token replaces the card number
if ( $this->getCardReference() )
{
$this->validate('cardReference');
$this->getCard()->validate('cvv', 'expiryMonth', 'expiryYear');

$cardDetails = [
'CardCVV2' => $this->getCard()->getCvv(),
'CardExpiryDate' => $this->getCard()->getExpiryDate('my'),
'CardNumber' => $this->getCardReference()
];
}
else
{
$this->getCard()->validate();

$cardDetails = [
'CardCVV2' => $this->getCard()->getCvv(),
'CardExpiryDate' => $this->getCard()->getExpiryDate('my'),
'CardNumber' => $this->getCard()->getNumber(),
'IssueNumber' => $this->getCard()->getIssueNumber()
];
}

// Only pass the StartDate if year/month are set otherwise it returns 1299
if ( $this->getCard()->getStartYear() && $this->getCard()->getStartMonth() )
{
$cardDetails['StartDate'] = $this->getCard()->getStartDate('my');
}

$transactionDetails = [
'AcquirerId' => $this->getAcquirerId(),
Expand All @@ -63,19 +99,6 @@ public function getData()
'TransactionCode' => $this->getTransactionCode()
];

$cardDetails = [
'CardCVV2' => $this->getCard()->getCvv(),
'CardExpiryDate' => $this->getCard()->getExpiryDate('my'),
'CardNumber' => $this->getCard()->getNumber(),
'IssueNumber' => $this->getCard()->getIssueNumber()
];

// Only pass the StartDate if year/month are set otherwise it returns 1299
if ( $this->getCard()->getStartYear() && $this->getCard()->getStartMonth() )
{
$cardDetails['StartDate'] = $this->getCard()->getStartDate('my');
}

$billingDetails = [
'BillToAddress' => $this->getCard()->getAddress1(),
'BillToZipPostCode' => $this->getCard()->getPostcode(),
Expand Down Expand Up @@ -114,7 +137,7 @@ protected function formatCountry()
{
$country = $this->getCard()->getCountry();

if ( !is_numeric($country) )
if ( !is_null($country) && !is_numeric($country) )
{
$iso3166 = new ISO3166;

Expand Down
95 changes: 95 additions & 0 deletions src/Message/CreateCardRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace Omnipay\FirstAtlanticCommerce\Message;

use Omnipay\FirstAtlanticCommerce\Message\AbstractRequest;

/**
* FACPG2 Tokenize Request
*/
class CreateCardRequest extends AbstractRequest
{
/**
* @var string;
*/
protected $requestName = 'TokenizeRequest';

/**
* Returns the signature for the request.
*
* @return string base64 encoded sha1 hash of the merchantPassword, merchantId,
* and acquirerId.
*/
protected function generateSignature()
{
$signature = $this->getMerchantPassword();
$signature .= $this->getMerchantId();
$signature .= $this->getAcquirerId();

return base64_encode( sha1($signature, true) );
}

/**
* Validate and construct the data for the request
*
* @return array
*/
public function getData()
{
$this->validate('merchantId', 'merchantPassword', 'acquirerId', 'customerReference', 'card');
$this->getCard()->validate();

$data = [
'CardNumber' => $this->getCard()->getNumber(),
'CustomerReference' => $this->getCustomerReference(),
'ExpiryDate' => $this->getCard()->getExpiryDate('my'),
'MerchantNumber' => $this->getMerchantId(),
'Signature' => $this->generateSignature()
];

return $data;
}

/**
* Get the customer reference.
*
* @return string
*/
public function getCustomerReference()
{
return $this->getParameter('customerReference');
}

/**
* Set the customer reference.
*
* @param string $value
*/
public function setCustomerReference($value)
{
return $this->setParameter('customerReference', $value);
}

/**
* Returns endpoint for tokenize requests
*
* @return string Endpoint URL
*/
protected function getEndpoint()
{
return parent::getEndpoint() . 'Tokenize';
}

/**
* Return the tokenize response object
*
* @param \SimpleXMLElement $xml Response xml object
*
* @return CreateCardResponse
*/
protected function newResponse($xml)
{
return new CreateCardResponse($this, $xml);
}

}
61 changes: 61 additions & 0 deletions src/Message/CreateCardResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Omnipay\FirstAtlanticCommerce\Message;

use Omnipay\Common\Exception\InvalidResponseException;
use Omnipay\Common\Message\RequestInterface;
use Omnipay\FirstAtlanticCommerce\Message\AbstractResponse;

/**
* FACPG2 XML Tokenize Response
*/
class CreateCardResponse extends AbstractResponse
{
/**
* Constructor
*
* @param RequestInterface $request
* @param string $data
*/
public function __construct(RequestInterface $request, $data)
{
if ( empty($data) )
{
throw new InvalidResponseException();
}

$this->request = $request;
$this->data = $this->xmlDeserialize($data);
}

/**
* Return whether or not the response was successful
*
* @return boolean
*/
public function isSuccessful()
{
return isset($this->data['Success']) && 'true' === $this->data['Success'];
}

/**
* Return the response's reason message
*
* @return string
*/
public function getMessage()
{
return isset($this->data['ErrorMsg']) && !empty($this->data['ErrorMsg']) ? $this->data['ErrorMsg'] : null;
}

/**
* Return card reference
*
* @return string
*/
public function getCardReference()
{
return isset($this->data['Token']) ? $this->data['Token'] : null;
}

}
Loading

0 comments on commit 3c51224

Please sign in to comment.