From 2d86dfc084f02d42e7cf9b5753676af7d367fdfd Mon Sep 17 00:00:00 2001
From: "Barry vd. Heuvel" <barryvdh@gmail.com>
Date: Fri, 18 May 2018 12:43:15 +0200
Subject: [PATCH] Accept Customer Object

---
 src/Common/CreditCard.php                     | 16 ++++++++++--
 src/Common/Message/AbstractRequest.php        | 25 +++++++++++++++++++
 tests/Omnipay/Common/CreditCardTest.php       | 17 +++++++++++++
 .../Common/Message/AbstractRequestTest.php    | 19 ++++++++++++++
 4 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/src/Common/CreditCard.php b/src/Common/CreditCard.php
index b0eebe55..3fbbc34d 100644
--- a/src/Common/CreditCard.php
+++ b/src/Common/CreditCard.php
@@ -93,7 +93,9 @@
  */
 class CreditCard
 {
-    use ParametersTrait;
+    use ParametersTrait {
+        validate as traitValidate;
+    }
 
     const BRAND_VISA = 'visa';
     const BRAND_MASTERCARD = 'mastercard';
@@ -230,11 +232,21 @@ protected function setYearParameter($key, $value)
      * Generally if you want to validate the credit card yourself with custom error
      * messages, you should use your framework's validation library, not this method.
      *
+     * @param array $args
      * @return void
      * @throws Exception\InvalidRequestException
      * @throws InvalidCreditCardException
      */
-    public function validate()
+    public function validate(...$args)
+    {
+        if (!empty($args)) {
+            $this->traitValidate(...$args);
+        } else {
+            $this->validateCreditcard();
+        }
+    }
+
+    private function validateCreditcard()
     {
         $requiredParameters = array(
             'number' => 'credit card number',
diff --git a/src/Common/Message/AbstractRequest.php b/src/Common/Message/AbstractRequest.php
index 5da5fd06..fd924090 100644
--- a/src/Common/Message/AbstractRequest.php
+++ b/src/Common/Message/AbstractRequest.php
@@ -206,6 +206,31 @@ public function setCard($value)
         return $this->setParameter('card', $value);
     }
 
+    /**
+     * Get the customer object.
+     *
+     * @return CreditCard
+     */
+    public function getCustomer()
+    {
+        return $this->getParameter('card');
+    }
+
+    /**
+     * Sets the customer object.
+     *
+     * @param CreditCard $value
+     * @return $this
+     */
+    public function setCustomer($value)
+    {
+        if ($value && !$value instanceof CreditCard) {
+            $value = new CreditCard($value);
+        }
+
+        return $this->setParameter('card', $value);
+    }
+
     /**
      * Get the card token.
      *
diff --git a/tests/Omnipay/Common/CreditCardTest.php b/tests/Omnipay/Common/CreditCardTest.php
index fdd4e91e..9130895b 100644
--- a/tests/Omnipay/Common/CreditCardTest.php
+++ b/tests/Omnipay/Common/CreditCardTest.php
@@ -138,6 +138,23 @@ public function testCustomBrandAddedTwiceReturnsFalse()
         $this->assertFalse($this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/'));
     }
 
+    /**
+     * @expectedException  \Omnipay\Common\Exception\InvalidRequestException
+     * @expectedExceptionMessage The title parameter is required
+     */
+    public function testValidateTitle()
+    {
+        $this->card->validate('title');
+    }
+
+    /**
+     * @expectedException  \Omnipay\Common\Exception\InvalidRequestException
+     */
+    public function testValidateMultiple()
+    {
+        $this->card->validate('title', 'gender');
+    }
+
     public function testTitle()
     {
         $this->card->setTitle('Mr.');
diff --git a/tests/Omnipay/Common/Message/AbstractRequestTest.php b/tests/Omnipay/Common/Message/AbstractRequestTest.php
index f4d751b3..1807b414 100644
--- a/tests/Omnipay/Common/Message/AbstractRequestTest.php
+++ b/tests/Omnipay/Common/Message/AbstractRequestTest.php
@@ -78,6 +78,25 @@ public function testSetCardWithArray()
         $this->assertSame('1234', $card->getNumber());
     }
 
+    public function testCustomer()
+    {
+        // no type checking on card parameter
+        $card = new CreditCard;
+        $this->assertSame($this->request, $this->request->setCustomer($card));
+        $this->assertSame($card, $this->request->getCustomer());
+    }
+
+    public function testSetCustomerWithArray()
+    {
+        // passing array should create CreditCard object
+        $this->assertSame($this->request, $this->request->setCustomer(array('first_name' => 'Barry')));
+
+        $customer = $this->request->getCustomer();
+        $customer->validate('billingFirstName');
+        $this->assertInstanceOf('\Omnipay\Common\CreditCard', $customer);
+        $this->assertSame('Barry', $customer->getName());
+    }
+
     public function testToken()
     {
         $this->assertSame($this->request, $this->request->setToken('12345'));