diff --git a/composer.json b/composer.json index fec7713..8d18813 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "lob/lob-php", - "version": "4.2.8", + "version": "4.3.0", "description": "The Lob API is organized around REST. Our API is designed to have predictable, resource-oriented URLs and uses HTTP response codes to indicate any API errors.", "keywords": [ "openapitools", diff --git a/lib/Model/BankAccount.php b/lib/Model/BankAccount.php index eb1badc..26a39f5 100755 --- a/lib/Model/BankAccount.php +++ b/lib/Model/BankAccount.php @@ -73,7 +73,8 @@ class BankAccount implements ModelInterface, ArrayAccess, \JsonSerializable 'date_created' => '\DateTime', 'date_modified' => '\DateTime', 'deleted' => 'bool', - 'object' => 'string' + 'object' => 'string', + 'microdeposit_type' => 'string' ]; /** @@ -97,7 +98,8 @@ class BankAccount implements ModelInterface, ArrayAccess, \JsonSerializable 'date_created' => 'date-time', 'date_modified' => 'date-time', 'deleted' => null, - 'object' => null + 'object' => null, + 'microdeposit_type' => null ]; /** @@ -140,7 +142,8 @@ public static function openAPIFormats() 'date_created' => 'date_created', 'date_modified' => 'date_modified', 'deleted' => 'deleted', - 'object' => 'object' + 'object' => 'object', + 'microdeposit_type' => 'microdeposit_type' ]; /** @@ -162,7 +165,8 @@ public static function openAPIFormats() 'date_created' => 'setDateCreated', 'date_modified' => 'setDateModified', 'deleted' => 'setDeleted', - 'object' => 'setObject' + 'object' => 'setObject', + 'microdeposit_type' => 'setMicrodepositType' ]; /** @@ -184,7 +188,8 @@ public static function openAPIFormats() 'date_created' => 'getDateCreated', 'date_modified' => 'getDateModified', 'deleted' => 'getDeleted', - 'object' => 'getObject' + 'object' => 'getObject', + 'microdeposit_type' => 'getMicrodepositType' ]; /** @@ -286,6 +291,7 @@ public function __construct(array $data = null) $this->container['date_modified'] = $data['date_modified'] ?? null; $this->container['deleted'] = $data['deleted'] ?? null; $this->container['object'] = $data['object'] ?? null; + $this->container['microdeposit_type'] = $data['microdeposit_type'] ?? null; } /** @@ -836,6 +842,32 @@ public function setObject($object) return $this; } + + + /** + * Gets microdeposit_type + * + * @return string|null + */ + public function getMicrodepositType() + { + return $this->container['microdeposit_type']; + } + + /** + * Sets microdeposit_type + * + * @param string|null $microdeposit_type The type of microdeposit verification required. Present when verified is false; null once the account is verified. Use this to determine which field to submit to the verify endpoint: amounts or descriptor_code. + * + * @return self + */ + public function setMicrodepositType($microdeposit_type) + { + $this->container['microdeposit_type'] = $microdeposit_type; + + return $this; + } + /** * Returns true if offset exists. False otherwise. * diff --git a/lib/Model/BankAccountVerify.php b/lib/Model/BankAccountVerify.php index ad6f05b..8670281 100755 --- a/lib/Model/BankAccountVerify.php +++ b/lib/Model/BankAccountVerify.php @@ -60,7 +60,8 @@ class BankAccountVerify implements ModelInterface, ArrayAccess, \JsonSerializabl * @var string[] */ protected static $openAPITypes = [ - 'amounts' => 'int[]' + 'amounts' => 'int[]', + 'descriptor_code' => 'string' ]; /** @@ -71,7 +72,8 @@ class BankAccountVerify implements ModelInterface, ArrayAccess, \JsonSerializabl * @psalm-var array */ protected static $openAPIFormats = [ - 'amounts' => null + 'amounts' => null, + 'descriptor_code' => null ]; /** @@ -101,7 +103,8 @@ public static function openAPIFormats() * @var string[] */ protected static $attributeMap = [ - 'amounts' => 'amounts' + 'amounts' => 'amounts', + 'descriptor_code' => 'descriptor_code' ]; /** @@ -110,7 +113,8 @@ public static function openAPIFormats() * @var string[] */ protected static $setters = [ - 'amounts' => 'setAmounts' + 'amounts' => 'setAmounts', + 'descriptor_code' => 'setDescriptorCode' ]; /** @@ -119,7 +123,8 @@ public static function openAPIFormats() * @var string[] */ protected static $getters = [ - 'amounts' => 'getAmounts' + 'amounts' => 'getAmounts', + 'descriptor_code' => 'getDescriptorCode' ]; /** @@ -180,6 +185,7 @@ public function getModelName() public function __construct(array $data = null) { $this->container['amounts'] = $data['amounts'] ?? null; + $this->container['descriptor_code'] = $data['descriptor_code'] ?? null; } /** @@ -192,19 +198,26 @@ public function listInvalidProperties() $invalidProperties = []; if (!method_exists($this, 'getId') || (!empty($this->getId()) && strpos($this->getId(), "fakeId") === False)) { - if ($this->container['amounts'] === null) { - $invalidProperties[] = "'amounts' can't be null"; - } - } - if (!method_exists($this, 'getId') || (!empty($this->getId()) && strpos($this->getId(), "fakeId") === False)) { - if ((count($this->container['amounts']) > 2)) { - $invalidProperties[] = "invalid value for 'amounts', number of items must be less than or equal to 2."; + $hasAmounts = $this->container['amounts'] !== null; + $hasDescriptorCode = $this->container['descriptor_code'] !== null; + + if (!$hasAmounts && !$hasDescriptorCode) { + $invalidProperties[] = "one of 'amounts' or 'descriptor_code' must be provided"; } - if ((count($this->container['amounts']) < 2)) { - $invalidProperties[] = "invalid value for 'amounts', number of items must be greater than or equal to 2."; + if ($hasAmounts && $hasDescriptorCode) { + $invalidProperties[] = "only one of 'amounts' or 'descriptor_code' may be provided"; } + if ($hasAmounts) { + if ((count($this->container['amounts']) > 2)) { + $invalidProperties[] = "invalid value for 'amounts', number of items must be less than or equal to 2."; + } + + if ((count($this->container['amounts']) < 2)) { + $invalidProperties[] = "invalid value for 'amounts', number of items must be greater than or equal to 2."; + } + } } return $invalidProperties; } @@ -253,7 +266,7 @@ public function setAmounts($amounts) $this->container['amounts'] = []; if ($amounts) { foreach ($amounts as $point) { - + $deserializedData = (int) $point; array_push($this->container['amounts'], $deserializedData); } @@ -261,6 +274,35 @@ public function setAmounts($amounts) return $this; } + + /** + * Gets descriptor_code + * + * @return string|null + */ + public function getDescriptorCode() + { + return $this->container['descriptor_code']; + } + + /** + * Sets descriptor_code + * + * @param string|null $descriptor_code The 6-character code (beginning with SM) from the bank statement descriptor of the single $0.01 microdeposit. Required when microdeposit_type is descriptor_code. + * + * @return self + */ + public function setDescriptorCode($descriptor_code) + { + if (!method_exists($this, 'getId') || (!empty($this->getId()) && strpos($this->getId(), "fakeId") === False)) { + if (!is_null($descriptor_code) && !preg_match("/^SM[a-zA-Z0-9]{4}$/", $descriptor_code)) { + throw new \InvalidArgumentException('invalid value for $descriptor_code when calling BankAccountVerify., must conform to the pattern /^SM[a-zA-Z0-9]{4}$/.'); + } + } + $this->container['descriptor_code'] = $descriptor_code; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/test/Integration/BankAccountsApiSpecTest.php b/test/Integration/BankAccountsApiSpecTest.php index 615e8e2..3e0e330 100644 --- a/test/Integration/BankAccountsApiSpecTest.php +++ b/test/Integration/BankAccountsApiSpecTest.php @@ -360,4 +360,31 @@ public function testDelete404() $this->expectExceptionMessageMatches("/bank account not found/"); $badDeletion = self::$bankApi->delete("bank_NONEXISTENT"); } + + public function testVerifyWithDescriptorCode200() + { + try { + $createdBankAccount = self::$bankApi->create(self::$writableBankAcc); + $descriptorVerify = new BankAccountVerify(); + $descriptorVerify->setDescriptorCode("SM11AA"); + $verifiedBankAccount = self::$bankApi->verify($createdBankAccount->getId(), $descriptorVerify); + $this->assertMatchesRegularExpression("/bank_/", $verifiedBankAccount->getId()); + array_push($this->idsForCleanup, $createdBankAccount->getId()); + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + } + + public function testBankAccountHasMicrodepositType() + { + try { + $createdBankAccount = self::$bankApi->create(self::$writableBankAcc); + $retrievedBankAccount = self::$bankApi->get($createdBankAccount->getId()); + $this->assertNotNull($retrievedBankAccount->getMicrodepositType()); + $this->assertContains($retrievedBankAccount->getMicrodepositType(), ["amounts", "descriptor_code"]); + array_push($this->idsForCleanup, $createdBankAccount->getId()); + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + } } diff --git a/test/Unit/BankAccountsApiUnitTest.php b/test/Unit/BankAccountsApiUnitTest.php index 011784c..8dabdcd 100644 --- a/test/Unit/BankAccountsApiUnitTest.php +++ b/test/Unit/BankAccountsApiUnitTest.php @@ -836,4 +836,85 @@ public function testVerifyFailStatusCode() echo 'Caught exception: ', $instantiationError->getMessage(), "\n"; } } + + /** + * @group unit + * @group bankAccounts + */ + public function testVerifyWithDescriptorCode() + { + $guzzleMock = new MockHandler(); + $handlerStack = HandlerStack::create($guzzleMock); + $client = new Client(['handler' => $handlerStack]); + $config = new Configuration(); + $config->setApiKey('basic', 'Totally Fake Key'); + $bankAccountsApi = new BankAccountsApi($config, $client); + + $guzzleMock->append(new Response(200, [], self::$mockBankAccountResponse)); + try { + $verify = new BankAccountVerify(array("descriptor_code" => "SM11AA")); + $happyPath = $bankAccountsApi->verify(self::$mockBankId, $verify); + $this->assertEquals($happyPath->getId(), self::$mockBankId); + } catch (Exception $retrieveError) { + echo 'Caught exception: ', $retrieveError->getMessage(), "\n"; + } + } + + /** + * @group unit + * @group bankAccounts + */ + public function testVerifyFailBothAmountsAndDescriptorCode() + { + $verify = new BankAccountVerify(array("amounts" => [1, 2], "descriptor_code" => "SM11AA")); + $invalidProps = $verify->listInvalidProperties(); + $this->assertNotEmpty($invalidProps); + $this->assertStringContainsString("only one of", $invalidProps[0]); + $this->assertFalse($verify->valid()); + } + + /** + * @group unit + * @group bankAccounts + */ + public function testVerifyFailNeitherAmountsNorDescriptorCode() + { + $verify = new BankAccountVerify(); + $invalidProps = $verify->listInvalidProperties(); + $this->assertNotEmpty($invalidProps); + $this->assertStringContainsString("one of 'amounts' or 'descriptor_code' must be provided", $invalidProps[0]); + } + + /** + * @group unit + * @group bankAccounts + */ + public function testVerifyFailInvalidDescriptorCodePattern() + { + try { + $this->expectException(\InvalidArgumentException::class); + $verify = new BankAccountVerify(); + $verify->setDescriptorCode("INVALID"); + } catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; + } + } + + /** + * @group unit + * @group bankAccounts + */ + public function testBankAccountHasMicrodepositType() + { + $account = new BankAccount(); + $account->setId(self::$mockBankId); + $account->setMicrodepositType("amounts"); + $this->assertEquals("amounts", $account->getMicrodepositType()); + + $account->setMicrodepositType("descriptor_code"); + $this->assertEquals("descriptor_code", $account->getMicrodepositType()); + + $account->setMicrodepositType(null); + $this->assertNull($account->getMicrodepositType()); + } }