Skip to content

Commit

Permalink
Merge pull request #2 from navihtot/master
Browse files Browse the repository at this point in the history
Card token purchase
  • Loading branch information
beinbm committed Sep 5, 2017
2 parents 8493eca + a28e4a4 commit c9c82bc
Show file tree
Hide file tree
Showing 12 changed files with 437 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
language: php

php:
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
- 7.0

before_script:
- composer install -n --dev --prefer-source
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ repository.

### Checkout.js

Currently this package provides implementation of 2 workflows:

#### 1. Authorize payment and then capture

The Checkout.com integration is fairly straight forward.
Essentially you just pass the order data and receive a payment token, which you
can use in the checkout.js payment form. After your customer has entered his data, you'll receive
Expand Down Expand Up @@ -66,6 +70,23 @@ if ($response->isSuccessful()) {
}
```

#### 2. Payment with card token (card token purchase)

- In this method we first validate card data via form and js provided from Checkout.com, see [https://docs.checkout.com/getting-started/checkoutkit-js](https://docs.checkout.com/getting-started/checkoutkit-js)
- After card is validated, we receive card token ([https://docs.checkout.com/getting-started/checkoutkit-js#example](https://docs.checkout.com/getting-started/checkoutkit-js#example))
- in the final step we complete payment providing order data and a card token:
```php
$response = $gateway->cardTokenPurchase([
'amount' => $amount,
'currency' => $currency,
'email' => '[email protected]',
'cardToken' => 'some_token',
'description' => 'some nice description'
]);
```

Note that `amount`, `currency`, `email` and `cardToken` are required fields here.

## Support

If you are having general issues with Omnipay, we suggest posting on
Expand Down
5 changes: 5 additions & 0 deletions src/Gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,9 @@ public function completePurchase(array $parameters = array())
{
return $this->createRequest('\Omnipay\CheckoutCom\Message\CompletePurchaseRequest', $parameters);
}

public function cardTokenPurchase(array $parameters = array())
{
return $this->createRequest('\Omnipay\CheckoutCom\Message\CardTokenPurchaseRequest', $parameters);
}
}
20 changes: 20 additions & 0 deletions src/Message/AbstractRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ public function setUdf($value)
return $this->setParameter('udf', $value);
}

public function setCardToken($value)
{
return $this->setParameter('cardToken', $value);
}

public function getCardToken()
{
return $this->getParameter('cardToken');
}

public function setEmail($value)
{
return $this->setParameter('email', $value);
}

public function getEmail()
{
return $this->getParameter('email');
}

public function sendRequest($data)
{
// don't throw exceptions for 4xx errors
Expand Down
50 changes: 50 additions & 0 deletions src/Message/CardTokenPurchaseRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* CheckoutCom Purchase Request
*/

namespace Omnipay\CheckoutCom\Message;

/**
* CheckoutCom CardTokenPurchase Request
*
* @link https://docs.checkout.com/reference/merchant-api-reference/charges/charge-with-card-token
*/
class CardTokenPurchaseRequest extends AbstractRequest
{
public function getData()
{
$this->validate('amount', 'currency');

$data = array();
$data['value'] = $this->getAmountInteger();
$data['currency'] = strtoupper($this->getCurrency());
$data['description'] = $this->getDescription();
$data['metadata'] = $this->getMetadata();
$data['cardToken'] = $this->getCardToken();
$data['email'] = $this->getEmail();


if ($udf = $this->getUdfValues()) {
$data['udf1'] = $udf[0];
$data['udf2'] = isset($udf[1]) ? $udf[1] : null;
$data['udf3'] = isset($udf[2]) ? $udf[2] : null;
$data['udf4'] = isset($udf[3]) ? $udf[3] : null;
$data['udf5'] = isset($udf[4]) ? $udf[4] : null;
}

return $data;
}

public function sendData($data)
{
$httpResponse = $this->sendRequest($data);

return $this->response = new CardTokenPurchaseResponse($this, $httpResponse->json());
}

public function getEndpoint()
{
return parent::getEndpoint() . '/charges/token';
}
}
50 changes: 50 additions & 0 deletions src/Message/CardTokenPurchaseResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* CheckoutCom Response
*/

namespace Omnipay\CheckoutCom\Message;

/**
* CardTokenPurchaseResponse Response
*
*
*
* @see \Omnipay\CheckoutCom\Gateway
*/
class CardTokenPurchaseResponse extends AbstractResponse
{

public function isSuccessful()
{
if (!empty($this->data['errorCode'])) {
return false;
}

if (!empty($this->data['status'])) {
return ($this->data['status'] == 'Authorised');
}

return false;
}

/**
* Get the error message from the response.
*
* Returns null if the request was successful.
*
* @return string|null
*/
public function getMessage()
{
if (!$this->isSuccessful() && isset($this->data['errorCode'])) {
return $this->data['errorCode'] . ': ' . $this->data['message'];
}

if (!$this->isSuccessful() && isset($this->data['responseCode'])) {
return $this->data['responseCode'] . ': ' . $this->data['responseMessage'];
}

return null;
}
}
8 changes: 8 additions & 0 deletions tests/GatewayTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,12 @@ public function testCompletePurchase()
$this->assertInstanceOf('Omnipay\CheckoutCom\Message\CompletePurchaseRequest', $request);
$this->assertSame('10.00', $request->getAmount());
}

public function testCardTokenPurchase()
{
$request = $this->gateway->cardTokenPurchase(array('amount' => '10.00'));

$this->assertInstanceOf('Omnipay\CheckoutCom\Message\CardTokenPurchaseRequest', $request);
$this->assertSame('10.00', $request->getAmount());
}
}
73 changes: 73 additions & 0 deletions tests/Message/CardTokenPurchaseRequestTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Omnipay\CheckoutCom\Message;

use Omnipay\Tests\TestCase;

class CardTokenPurchaseRequestTest extends TestCase
{
public function setUp()
{
$this->request = new CardTokenPurchaseRequest($this->getHttpClient(), $this->getHttpRequest());
$this->request->initialize(
array(
'amount' => '12.00',
'currency' => 'uSd',
'description' => 'Order #42',
'email' => '[email protected]',
'metadata' => array(
'foo' => 'bar',
),
'udf' => array(
'first' => 'lorem',
'second' => 'ipsum'
)
)
);
}

public function testGetData()
{
$data = $this->request->getData();

$this->assertSame(1200, $data['value']);
$this->assertSame('USD', $data['currency']);
$this->assertSame('Order #42', $data['description']);
$this->assertSame(array('foo' => 'bar'), $data['metadata']);
$this->assertSame('lorem', $data['udf1']);
$this->assertSame('ipsum', $data['udf2']);
$this->assertSame('[email protected]', $data['email']);

}

public function testSendSuccess()
{
$this->setMockHttpResponse('CardTokenPurchaseSuccess.txt');
$response = $this->request->send();

$this->assertTrue($response->isSuccessful());
$this->assertSame('charge_test_DD0BF9EC548R752B79E2', $response->getTransactionReference());
$this->assertNull($response->getMessage());
}

public function testSendErrorValidation()
{
$this->setMockHttpResponse('CardTokenPurchaseFailureValidation.txt');
$response = $this->request->send();

$this->assertFalse($response->isSuccessful());
$this->assertFalse($response->isRedirect());
$this->assertNull($response->getTransactionReference());
$this->assertSame('70000: Validation error', $response->getMessage());
}

public function testSendError()
{
$this->setMockHttpResponse('CardTokenPurchaseFailure.txt');
$response = $this->request->send();

$this->assertFalse($response->isSuccessful());
$this->assertSame('charge_test_EE0E09FC548L752B6C12', $response->getTransactionReference());
$this->assertSame('20087: Bad Track Data', $response->getMessage());
}
}
38 changes: 38 additions & 0 deletions tests/Message/CardTokenPurchaseResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Omnipay\CheckoutCom\Message;

use Omnipay\Tests\TestCase;

class CardTokenPurchaseResponseTest extends TestCase
{
public function testPurchaseSuccess()
{
$httpResponse = $this->getMockHttpResponse('CardTokenPurchaseSuccess.txt');
$response = new CardTokenPurchaseResponse($this->getMockRequest(), $httpResponse->json());

$this->assertTrue($response->isSuccessful());
$this->assertSame('charge_test_DD0BF9EC548R752B79E2', $response->getTransactionReference());
$this->assertNull($response->getMessage());
}

public function testPurchaseFailure()
{
$httpResponse = $this->getMockHttpResponse('CardTokenPurchaseFailure.txt');
$response = new CardTokenPurchaseResponse($this->getMockRequest(), $httpResponse->json());

$this->assertFalse($response->isSuccessful());
$this->assertSame('charge_test_EE0E09FC548L752B6C12', $response->getTransactionReference());
$this->assertSame('20087: Bad Track Data', $response->getMessage());
}

public function testPurchaseFailureValidation()
{
$httpResponse = $this->getMockHttpResponse('CardTokenPurchaseFailureValidation.txt');
$response = new CardTokenPurchaseResponse($this->getMockRequest(), $httpResponse->json());

$this->assertFalse($response->isSuccessful());
$this->assertNull($response->getTransactionReference());
$this->assertSame('70000: Validation error', $response->getMessage());
}
}
77 changes: 77 additions & 0 deletions tests/Mock/CardTokenPurchaseFailure.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 05 May 2013 08:51:15 GMT
Content-Type: application/json;charset=utf-8
Content-Length: 997
Connection: keep-alive
Cache-Control: no-cache, no-store
Access-Control-Max-Age: 300
Access-Control-Allow-Credentials: true

{
"id": "charge_test_EE0E09FC548L752B6C12",
"liveMode": false,
"created": "2017-09-05T11:47:36Z",
"value": 319,
"currency": "EUR",
"trackId": null,
"description": "Order Key: 20479114264",
"email": "[email protected]",
"chargeMode": 1,
"transactionIndicator": 1,
"customerIp": null,
"responseMessage": "Bad Track Data",
"responseAdvancedInfo": "Bad Track Data",
"responseCode": "20087",
"status": "Declined",
"authCode": "000000",
"isCascaded": false,
"autoCapture": "Y",
"autoCapTime": 0,
"card": {
"customerId": "cust_EC34DF5E-7A0E-43C3-868F-170DA09B0DD6",
"expiryMonth": "06",
"expiryYear": "2025",
"billingDetails": {
"addressLine1": null,
"addressLine2": null,
"postcode": null,
"country": null,
"city": null,
"state": null,
"phone": []
},
"id": "",
"last4": "4242",
"bin": "424242",
"paymentMethod": "Visa",
"fingerprint": "9F3BAD2E48C6C8579F2F5DC0710B7C11A8ACD5072C3363A72579A6FB227D64BE",
"name": null,
"cvvCheck": "D",
"avsCheck": "S"
},
"riskCheck": true,
"customerPaymentPlans": null,
"metadata": {
"ordernumber": "426",
"invoicenumber": "410",
"order_id": "59ae8f05-e750-4922-99c0-5e527f000102",
"customer_id": "57977215-2990-4354-a733-3db4c0a8171f",
"customer_name": "Testvorname Testnachname"
},
"shippingDetails": {
"addressLine1": null,
"addressLine2": null,
"postcode": null,
"country": null,
"city": null,
"state": null,
"phone": []
},
"products": [],
"udf1": "Order-No:426",
"udf2": "Invoice-No:410",
"udf3": "Order-Id:59ae8f05-e750-4922-99c0-5e527f000102",
"udf4": "Customer-Id:57977215-2990-4354-a733-3db4c0a8171f",
"udf5": null
}
Loading

0 comments on commit c9c82bc

Please sign in to comment.