Skip to content

Commit

Permalink
Merge branch 'master' of github.com:thephpleague/omnipay-authorizenet…
Browse files Browse the repository at this point in the history
… into issue123

Conflicts:
	README.md
  • Loading branch information
judgej committed Jan 20, 2019
2 parents 7e118ad + 23458a4 commit bbf1985
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 23 deletions.
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The following gateways are provided by this package:
* AuthorizeNet_SIM
* AuthorizeNet_DPM

In addition, `Accept.JS` is supported by the AIM driver. More details are provided below.
In addition, `Accept.JS` is supported by the AIM driver and CIM (create card). More details are provided below.

For general usage instructions, please see the main [Omnipay](https://github.com/thephpleague/omnipay)
repository.
Expand All @@ -45,15 +45,15 @@ The card is tokenized into two values returned in `opaqueData` object from Accep

These two values must be POSTed back to the merchant application, usually as a part of the payment form.
Make sure the raw credit card details are NOT posted back to your site.
How this is handled is beyond this short note, but examples are always welcome in the documentation.
How this is handled is beyond this short note, but examples are always welcomed in the documentation.

On the server, the tokenized detailt are passed into the `payment` or `authorize` request object.
On the server, the tokenized details are passed into the `payment` or `authorize` request object.
You will still need to pass in the `CreditCard` object, as that contains details of the payee and
recipient, but just leave the credit card details of that object blank. For example:

```php
// $gateway is an instantiation of the AIM driver.
// $dataDescriptor and $dataValue come from the paymentr form at the front end.
// $dataDescriptor and $dataValue come from the payment form at the front end.

$request = $gateway->purchase(
[
Expand All @@ -66,6 +66,55 @@ $request = $gateway->purchase(
);
```

CIM Create Card feature usage:
Accept.js must be implemented on your frontend payment form, once Accept.js 'tokenizes' the customer's
card, just send the two opaque fields and remove the Card's (Number, Expiration and CVV) from your post request.

Accept.js goal is to remove the need of Card information from ever going into your server so be sure to remove that data
before posting to your server.

The create card feature on CIM will automatically create a Customer Profile and a Payment Profile with the
'tokenized' card for each customer you request it for on your authorize.net account, you can use these Payment Profiles
later to request payments from your customers.

In order to create a Customer & Payment Profile pass the opaque fields and the card array with the billing information
to the createCard method on the CIM driver:

```php
// $gateway is an instantiation of the CIM driver. //Omnipay::create( 'AuthorizeNet_CIM' )
// $dataDescriptor and $dataValue come from the payment form at the front end.

$request = $gateway->createCard(
[
'opaqueDataDescriptor' => $dataDescriptor,
'opaqueDataValue' => $dataValue,
'name' => $name,
'email' => $email, //Authorize.net will use the email to identify the CustomerProfile
'customerType' => 'individual',
'customerId' => $user_customer_id,//a customer ID generated by your system or send null
'description' => 'MEMBER',//whichever description you wish to send
'forceCardUpdate' => true
'card' => [
'billingFirstName' => $name,
'billingLastName' => $last_name,
'billingAddress1' => $address,
'billingCity' => $city,
'billingState' => $state,
'billingPostcode' => $zipcode,
'billingPhone' => '',
//... may include shipping info but do not include card (number, cvv or expiration)
],
]
);
$response = $request->send();
$data = $response->getData();

$data['paymentProfile']['customerProfileId'];
$data['paymentProfile']['customerPaymentProfileId'];
//Now you can use these 2 fields to reference this customer and this payment profile for later use with
//the rest of the CIM driver features as usual.
```

## DPM and SIM Signatures

DPM and SIM used to sign their requests with the `transactionKey` using the mdh HMAC algorithm.
Expand Down
5 changes: 5 additions & 0 deletions src/CIMGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public function createCard(array $parameters = array())
return $this->createRequest('\Omnipay\AuthorizeNet\Message\CIMCreateCardRequest', $parameters);
}

public function updateCard(array $parameters = array())
{
return $this->createRequest('\Omnipay\AuthorizeNet\Message\CIMUpdatePaymentProfileRequest', $parameters);
}

public function getPaymentProfile(array $parameters = array())
{
return $this->createRequest('\Omnipay\AuthorizeNet\Message\CIMGetPaymentProfileRequest', $parameters);
Expand Down
45 changes: 34 additions & 11 deletions src/Message/CIMCreateCardRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,33 @@ class CIMCreateCardRequest extends CIMAbstractRequest

public function getData()
{
$this->validate('card');

/** @var CreditCard $card */
$card = $this->getCard();
$card->validate();

$this->validate('card');
$this->cardValidate();
$data = $this->getBaseData();
$this->addProfileData($data);
$this->addTransactionSettings($data);

return $data;
}

/**
* Validate card or skip if opaque data is available
*
* @param \SimpleXMLElement $data
*/
protected function cardValidate()
{

if ($this->getOpaqueDataDescriptor() && $this->getOpaqueDataValue()) {
return;
}

/** @var CreditCard $card */
$card = $this->getCard();
$card->validate();
}

/**
* Add customer profile data to the specified xml element
*
Expand Down Expand Up @@ -97,12 +111,18 @@ protected function addBillingData(\SimpleXMLElement $data)
}

$req = $data->addChild('payment');
$req->creditCard->cardNumber = $card->getNumber();
$req->creditCard->expirationDate = $card->getExpiryDate('Y-m');
if ($card->getCvv()) {
$req->creditCard->cardCode = $card->getCvv();
if ($this->getOpaqueDataDescriptor() && $this->getOpaqueDataValue()) {
//Use opaqueData if available instead of card data
$req->opaqueData->dataDescriptor = $this->getOpaqueDataDescriptor();
$req->opaqueData->dataValue = $this->getOpaqueDataValue();
} else {
$this->setValidationMode(self::VALIDATION_MODE_NONE);
$req->creditCard->cardNumber = $card->getNumber();
$req->creditCard->expirationDate = $card->getExpiryDate('Y-m');
if ($card->getCvv()) {
$req->creditCard->cardCode = $card->getCvv();
} else {
$this->setValidationMode(self::VALIDATION_MODE_NONE);
}
}
}
}
Expand Down Expand Up @@ -194,8 +214,11 @@ public function createPaymentProfile(CIMCreateCardResponse $createCardResponse)
$createPaymentProfileResponse = $this->makeCreatePaymentProfileRequest($parameters);
if ($createPaymentProfileResponse->isSuccessful()) {
$parameters['customerPaymentProfileId'] = $createPaymentProfileResponse->getCustomerPaymentProfileId();
} elseif ($this->getForceCardUpdate() !== true) {
} elseif ($this->getForceCardUpdate() !== true ||
($this->getOpaqueDataDescriptor() && $this->getOpaqueDataValue())
) {
// force card update flag turned off. No need to further process.
// also if opaque data is being used we are not able to update existing payment profiles
return $createCardResponse;
}

Expand Down
6 changes: 1 addition & 5 deletions src/Message/CIMCreatePaymentProfileRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ class CIMCreatePaymentProfileRequest extends CIMCreateCardRequest
public function getData()
{
$this->validate('card', 'customerProfileId');

/** @var CreditCard $card */
$card = $this->getCard();
$card->validate();

$this->cardValidate();
$data = $this->getBaseData();
$data->customerProfileId = $this->getCustomerProfileId();
$this->addPaymentProfileData($data);
Expand Down
4 changes: 1 addition & 3 deletions src/Message/CIMUpdatePaymentProfileRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ public function getData()
{
$this->validate('card', 'customerProfileId', 'customerPaymentProfileId');

/** @var CreditCard $card */
$card = $this->getCard();
$card->validate();
$this->cardValidate();

$data = $this->getBaseData();
$data->customerProfileId = $this->getCustomerProfileId();
Expand Down
52 changes: 52 additions & 0 deletions tests/CIMGatewayTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ public function setUp()
'forceCardUpdate' => true
);

$validCard = $this->getValidCard();
unset($validCard['number'],$validCard['expiryMonth'],$validCard['expiryYear'],$validCard['cvv']);
//remove the actual card data since we are setting opaque values
$this->createCardFromOpaqueDataOptions = array(
'email' => "[email protected]",
'card' => $validCard,
'opaqueDataDescriptor' => 'COMMON.ACCEPT.INAPP.PAYMENT',
'opaqueDataValue' => 'jb2RlIjoiNTB',
'testMode' => true,
'forceCardUpdate' => true
);

$this->authorizeOptions = array(
'cardReference' => '{"customerProfileId":"28972084","customerPaymentProfileId":"26317840","customerShippingAddressId":"27057149"}',
'amount' => 10.00,
Expand Down Expand Up @@ -89,6 +101,20 @@ public function testCreateCardSuccess()
$this->assertSame('Successful.', $response->getMessage());
}

public function testCreateCardFromOpaqueDataSuccess()
{
$this->setMockHttpResponse(array('CIMCreateCardSuccess.txt','CIMGetPaymentProfileSuccess.txt'));

$response = $this->gateway->createCard($this->createCardFromOpaqueDataOptions)->send();

$this->assertTrue($response->isSuccessful());
$this->assertSame(
'{"customerProfileId":"28972084","customerPaymentProfileId":"26485433"}',
$response->getCardReference()
);
$this->assertSame('Successful.', $response->getMessage());
}

public function testShouldCreateCardIfDuplicateCustomerProfileExists()
{
$this->setMockHttpResponse(array('CIMCreateCardFailureWithDuplicate.txt', 'CIMCreatePaymentProfileSuccess.txt',
Expand All @@ -104,6 +130,21 @@ public function testShouldCreateCardIfDuplicateCustomerProfileExists()
$this->assertSame('Successful.', $response->getMessage());
}

public function testShouldCreateCardFromOpaqueDataIfDuplicateCustomerProfileExists()
{
$this->setMockHttpResponse(array('CIMCreateCardFailureWithDuplicate.txt', 'CIMCreatePaymentProfileSuccess.txt',
'CIMGetProfileSuccess.txt', 'CIMGetPaymentProfileSuccess.txt'));

$response = $this->gateway->createCard($this->createCardFromOpaqueDataOptions)->send();

$this->assertTrue($response->isSuccessful());
$this->assertSame(
'{"customerProfileId":"28775801","customerPaymentProfileId":"26485433"}',
$response->getCardReference()
);
$this->assertSame('Successful.', $response->getMessage());
}

public function testShouldUpdateExistingPaymentProfileIfDuplicateExistsAndForceCardUpdateIsSet()
{
// Duplicate **payment** profile
Expand All @@ -120,6 +161,17 @@ public function testShouldUpdateExistingPaymentProfileIfDuplicateExistsAndForceC
$this->assertSame('Successful.', $response->getMessage());
}

public function testDoesntUpdateExistingPaymentProfileFromOpaqueData()
{
// Duplicate **payment** profile
$this->setMockHttpResponse(array('CIMCreateCardFailureWithDuplicate.txt', 'CIMCreatePaymentProfileFailure.txt',
'CIMGetProfileSuccess.txt', 'CIMUpdatePaymentProfileSuccess.txt', 'CIMGetPaymentProfileSuccess.txt'));

$response = $this->gateway->createCard($this->createCardFromOpaqueDataOptions)->send();

$this->assertFalse($response->isSuccessful());
}

public function testShouldUpdateExistingPaymentProfileIfDuplicateExistsAndMaxPaymentProfileLimitIsMet()
{
$this->setMockHttpResponse(array('CIMCreateCardFailureWithDuplicate.txt',
Expand Down
21 changes: 21 additions & 0 deletions tests/Message/CIMCreateCardRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,25 @@ public function testGetDataShouldSetValidationModeToNoneIfNoCvvProvided()
$this->assertFalse(isset($data->profile->paymentProfiles->payment->creditCard->cardCode));
$this->assertEquals(CIMCreatePaymentProfileRequest::VALIDATION_MODE_NONE, $this->request->getValidationMode());
}

public function testGetDataOpaqueData()
{

$validCard = $this->getValidCard();
unset($validCard['number'],$validCard['expiryMonth'],$validCard['expiryYear'],$validCard['cvv']);
//remove the actual card data since we are setting opaque values
$this->params = array(
'email' => "[email protected]",
'card' => $validCard,
'opaqueDataDescriptor' => 'COMMON.ACCEPT.INAPP.PAYMENT',
'opaqueDataValue' => 'jb2RlIjoiNTB',
'developerMode' => true
);
$this->request->initialize($this->params);

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

$this->assertEquals('COMMON.ACCEPT.INAPP.PAYMENT', $data->profile->paymentProfiles->payment->opaqueData->dataDescriptor);
$this->assertEquals('jb2RlIjoiNTB', $data->profile->paymentProfiles->payment->opaqueData->dataValue);
}
}
21 changes: 21 additions & 0 deletions tests/Message/CIMCreatePaymentProfileRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,25 @@ public function testGetData()
$this->assertEquals($card['number'], $data->paymentProfile->payment->creditCard->cardNumber);
$this->assertEquals('testMode', $data->validationMode);
}

public function testGetDataOpaqueData()
{
$validCard = $this->getValidCard();
unset($validCard['number'],$validCard['expiryMonth'],$validCard['expiryYear'],$validCard['cvv']);
//remove the actual card data since we are setting opaque values
$this->request->initialize(
array(
'customerProfileId' => '28775801',
'email' => "[email protected]",
'card' => $validCard,
'opaqueDataDescriptor' => 'COMMON.ACCEPT.INAPP.PAYMENT',
'opaqueDataValue' => 'jb2RlIjoiNTB',
'developerMode' => true
)
);

$data = $this->request->getData();
$this->assertEquals('COMMON.ACCEPT.INAPP.PAYMENT', $data->paymentProfile->payment->opaqueData->dataDescriptor);
$this->assertEquals('jb2RlIjoiNTB', $data->paymentProfile->payment->opaqueData->dataValue);
}
}

0 comments on commit bbf1985

Please sign in to comment.