From 27f27bc9402deb695335556751e15a7910025ca4 Mon Sep 17 00:00:00 2001 From: Braintree Date: Wed, 9 Nov 2022 21:30:53 +0000 Subject: [PATCH] 6.10.0 --- CHANGELOG.md | 8 ++ lib/Braintree/Customer.php | 8 ++ lib/Braintree/Dispute.php | 31 ++++- lib/Braintree/DisputeSearch.php | 15 ++- lib/Braintree/Error/Codes.php | 4 + lib/Braintree/Gateway.php | 9 ++ lib/Braintree/PaymentInstrumentType.php | 21 ++-- lib/Braintree/PaymentMethodParser.php | 2 + lib/Braintree/SepaDirectDebitAccount.php | 102 ++++++++++++++++ .../SepaDirectDebitAccountGateway.php | 97 +++++++++++++++ lib/Braintree/Test/Nonces.php | 1 + lib/Braintree/Transaction.php | 9 ++ .../SepaDirectDebitAccountDetails.php | 25 ++++ lib/Braintree/TransactionSearch.php | 10 ++ lib/Braintree/Version.php | 4 +- tests/integration/CustomerTest.php | 12 ++ tests/integration/DisputeSearchTest.php | 2 + tests/integration/PaymentMethodNonceTest.php | 11 ++ tests/integration/PaymentMethodTest.php | 26 ++++ .../SepaDirectDebitAccountTest.php | 115 ++++++++++++++++++ .../TransactionAdvancedSearchTest.php | 42 +++++++ tests/integration/TransactionTest.php | 6 + tests/unit/CustomerTest.php | 12 ++ tests/unit/DisputeTest.php | 62 ++++++++++ tests/unit/SepaDirectDebitAccountTest.php | 33 +++++ tests/unit/TransactionTest.php | 22 ++++ 26 files changed, 675 insertions(+), 14 deletions(-) create mode 100644 lib/Braintree/SepaDirectDebitAccount.php create mode 100644 lib/Braintree/SepaDirectDebitAccountGateway.php create mode 100644 lib/Braintree/Transaction/SepaDirectDebitAccountDetails.php create mode 100644 tests/integration/SepaDirectDebitAccountTest.php create mode 100644 tests/unit/SepaDirectDebitAccountTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 293db00f..b10cb423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 6.10.0 +* Add `SEPADirectDebitAccount` payment method +* Add `SEPADirectDebitAccountDetails` to transaction object +* Add `SEPA_DIRECT_DEBIT_ACCOUNT` to payment instrument type +* Add SEPA Direct Debit specific error codes +* Add SEPA Direct Debit array to customer object +* Deprecate `chargebackProtectionLevel` and add `protectionLevel` to `Dispute` and `DisputeSearch` + ## 6.9.1 * Address PHP 8.2 deprecation warnings due to string interpolation patterns. (thanks @Ayesh) diff --git a/lib/Braintree/Customer.php b/lib/Braintree/Customer.php index 925a9644..51238b9a 100644 --- a/lib/Braintree/Customer.php +++ b/lib/Braintree/Customer.php @@ -284,6 +284,14 @@ protected function _initialize($customerAttribs) } $this->_set('samsungPayCards', $samsungPayCardArray); + $sepaDirectDebitArray = []; + if (isset($customerAttribs['sepaDebitAccounts'])) { + foreach ($customerAttribs['sepaDebitAccounts'] as $sepaDirectDebitAccount) { + $sepaDirectDebitArray[] = SepaDirectDebitAccount::factory($sepaDirectDebitAccount); + } + } + $this->_set('sepaDirectDebitAccounts', $sepaDirectDebitArray); + $usBankAccountArray = array(); if (isset($customerAttribs['usBankAccounts'])) { foreach ($customerAttribs['usBankAccounts'] as $usBankAccount) { diff --git a/lib/Braintree/Dispute.php b/lib/Braintree/Dispute.php index 3b8a345e..c203ac51 100644 --- a/lib/Braintree/Dispute.php +++ b/lib/Braintree/Dispute.php @@ -37,6 +37,11 @@ class Dispute extends Base const STANDARD = 'standard'; const NOT_PROTECTED = 'not_protected'; + /* Dispute ProtectionLevel */ + const EFFORTLESS_CBP = 'Effortless Chargeback Protection tool'; + const STANDARD_CBP = 'Chargeback Protection tool'; + const NO_PROTECTION = 'No Protection'; + /* Dispute Kind */ const CHARGEBACK = 'chargeback'; const PRE_ARBITRATION = 'pre_arbitration'; @@ -46,6 +51,13 @@ protected function _initialize($disputeAttribs) { $this->_attributes = $disputeAttribs; + if (isset($disputeAttribs['chargebackProtectionLevel']) && in_array($disputeAttribs['chargebackProtectionLevel'], array(self::EFFORTLESS, self::STANDARD))) { + $protectionLevel = constant('self::' . strtoupper($disputeAttribs['chargebackProtectionLevel']) . '_CBP'); + $this->_set('protectionLevel', $protectionLevel); + } else { + $this->_set('protectionLevel', self::NO_PROTECTION); + } + if (isset($disputeAttribs['transaction'])) { $transactionDetails = new Dispute\TransactionDetails($disputeAttribs['transaction']); $this->_set('transactionDetails', $transactionDetails); @@ -210,9 +222,12 @@ public static function search($query) return Configuration::gateway()->dispute()->search($query); } - /* + // NEXT_MAJOR_VERSION Remove this function + /** * Retrive all types of chargeback protection level types * + * @deprecated Use allProtectionLevelTypes() instead + * * @return array */ public static function allChargebackProtectionLevelTypes() @@ -223,4 +238,18 @@ public static function allChargebackProtectionLevelTypes() Dispute::NOT_PROTECTED ]; } + + /* + * Retrieve all types of protection level types + * + * @return array + */ + public static function allProtectionLevelTypes() + { + return [ + Dispute::EFFORTLESS_CBP, + Dispute::STANDARD_CBP, + Dispute::NO_PROTECTION + ]; + } } diff --git a/lib/Braintree/DisputeSearch.php b/lib/Braintree/DisputeSearch.php index 249a6762..a590485b 100644 --- a/lib/Braintree/DisputeSearch.php +++ b/lib/Braintree/DisputeSearch.php @@ -157,9 +157,12 @@ public static function status() return new MultipleValueNode("status"); } - /* + // NEXT_MAJOR_VERSION Remove this function + /** * Create a new multiple value node for chargeback protection level * + * @deprecated Use protectionLevel() instead + * * @return MultipleValueNode */ public static function chargebackProtectionLevel() @@ -167,6 +170,16 @@ public static function chargebackProtectionLevel() return new MultipleValueNode("chargeback_protection_level", Dispute::allChargebackProtectionLevelTypes()); } + /* + * Create a new multiple value node for protection level + * + * @return MultipleValueNode + */ + public static function protectionLevel() + { + return new MultipleValueNode("protection_level", Dispute::allProtectionLevelTypes()); + } + /* * Create a new text node for transaction id * diff --git a/lib/Braintree/Error/Codes.php b/lib/Braintree/Error/Codes.php index 669b98b5..f8083acb 100644 --- a/lib/Braintree/Error/Codes.php +++ b/lib/Braintree/Error/Codes.php @@ -463,6 +463,10 @@ class Codes const PROCESSOR_DOES_NOT_SUPPORT_INCREMENTAL_AUTH = '915220'; const PROCESSOR_DOES_NOT_SUPPORT_PARTIAL_AUTH_REVERSAL = '915221'; + const SEPA_DEBIT_ACCOUNT_PAYMENT_METHOD_MANDATE_TYPE_IS_NOT_SUPPORTED = '87115'; + const SEPA_DEBIT_ACCOUNT_PAYMENT_METHOD_CUSTOMER_ID_IS_INVALID = '87116'; + const SEPA_DEBIT_ACCOUNT_PAYMENT_METHOD_CUSTOMER_ID_IS_REQUIRED = '87117'; + const SETTLEMENT_BATCH_SUMMARY_SETTLEMENT_DATE_IS_INVALID = '82302'; const SETTLEMENT_BATCH_SUMMARY_SETTLEMENT_DATE_IS_REQUIRED = '82301'; const SETTLEMENT_BATCH_SUMMARY_CUSTOM_FIELD_IS_INVALID = '82303'; diff --git a/lib/Braintree/Gateway.php b/lib/Braintree/Gateway.php index 05f75ce4..9b5e4d5e 100644 --- a/lib/Braintree/Gateway.php +++ b/lib/Braintree/Gateway.php @@ -182,6 +182,15 @@ public function payPalAccount() return new PayPalAccountGateway($this); } + /** + * + * @return SepaDirectDebitAccountGateway + */ + public function sepaDirectDebitAccount() + { + return new SepaDirectDebitAccountGateway($this); + } + /** * * @return PlanGateway diff --git a/lib/Braintree/PaymentInstrumentType.php b/lib/Braintree/PaymentInstrumentType.php index 68b31bcf..76ac079c 100644 --- a/lib/Braintree/PaymentInstrumentType.php +++ b/lib/Braintree/PaymentInstrumentType.php @@ -9,14 +9,15 @@ */ class PaymentInstrumentType { - const GOOGLE_PAY_CARD = 'android_pay_card'; - const APPLE_PAY_CARD = 'apple_pay_card'; - const CREDIT_CARD = 'credit_card'; - const LOCAL_PAYMENT = 'local_payment'; - const PAYPAL_ACCOUNT = 'paypal_account'; - const PAYPAL_HERE = 'paypal_here'; - const SAMSUNG_PAY_CARD = 'samsung_pay_card'; - const US_BANK_ACCOUNT = 'us_bank_account'; - const VENMO_ACCOUNT = 'venmo_account'; - const VISA_CHECKOUT_CARD = 'visa_checkout_card'; + const APPLE_PAY_CARD = 'apple_pay_card'; + const CREDIT_CARD = 'credit_card'; + const GOOGLE_PAY_CARD = 'android_pay_card'; + const LOCAL_PAYMENT = 'local_payment'; + const PAYPAL_ACCOUNT = 'paypal_account'; + const PAYPAL_HERE = 'paypal_here'; + const SAMSUNG_PAY_CARD = 'samsung_pay_card'; + const SEPA_DIRECT_DEBIT_ACCOUNT = 'sepa_debit_account'; + const US_BANK_ACCOUNT = 'us_bank_account'; + const VENMO_ACCOUNT = 'venmo_account'; + const VISA_CHECKOUT_CARD = 'visa_checkout_card'; } diff --git a/lib/Braintree/PaymentMethodParser.php b/lib/Braintree/PaymentMethodParser.php index 45a3a5ba..9b8e5590 100644 --- a/lib/Braintree/PaymentMethodParser.php +++ b/lib/Braintree/PaymentMethodParser.php @@ -32,6 +32,8 @@ public static function parsePaymentMethod($response) return VisaCheckoutCard::factory($response['visaCheckoutCard']); } elseif (isset($response['samsungPayCard'])) { return SamsungPayCard::factory($response['samsungPayCard']); + } elseif (isset($response['sepaDebitAccount'])) { + return SepaDirectDebitAccount::factory($response['sepaDebitAccount']); } elseif (is_array($response)) { return UnknownPaymentMethod::factory($response); } else { diff --git a/lib/Braintree/SepaDirectDebitAccount.php b/lib/Braintree/SepaDirectDebitAccount.php new file mode 100644 index 00000000..a90e2d76 --- /dev/null +++ b/lib/Braintree/SepaDirectDebitAccount.php @@ -0,0 +1,102 @@ +_initialize($attributes); + return $instance; + } + + /* instance methods */ + + /** + * Returns false if default is null or false + * + * @return boolean + */ + public function isDefault() + { + return $this->default; + } + + /** + * Sets instance properties from an array of values + * + * @param array $sepaDirectDebitAttribs array of sepaDirectDebitAccount data + * + * @return void + */ + protected function _initialize($sepaDirectDebitAttribs) + { + $this->_attributes = $sepaDirectDebitAttribs; + } + + // phpcs:ignore PEAR.Commenting.FunctionComment.Missing + public function __toString() + { + return __CLASS__ . '[' . + Util::attributesToString($this->_attributes) . ']'; + } + + /** + * Static methods redirecting to gateway class + * + * @param string $token paypal account unique id + * + * @see SepaDirectDebitAccountGateway::find() + * + * @throws Exception\NotFound + * + * @return SepaDirectDebitAccount + */ + public static function find($token) + { + return Configuration::gateway()->sepaDirectDebitAccount()->find($token); + } + + /** + * Static methods redirecting to gateway class + * + * @param string $token paypal account identifier + * + * @see PayPalGateway::delete() + * + * @return Result + */ + public static function delete($token) + { + return Configuration::gateway()->sepaDirectDebitAccount()->delete($token); + } + + /** + * Static methods redirecting to gateway class + * + * @param string $token paypal account identifier + * @param array $transactionAttribs containing request parameters + * + * @see PayPalGateway::sale() + * + * @return Result\Successful|Result\Error + */ + public static function sale($token, $transactionAttribs) + { + return Configuration::gateway()->sepaDirectDebitAccount()->sale($token, $transactionAttribs); + } +} diff --git a/lib/Braintree/SepaDirectDebitAccountGateway.php b/lib/Braintree/SepaDirectDebitAccountGateway.php new file mode 100644 index 00000000..e9f29db0 --- /dev/null +++ b/lib/Braintree/SepaDirectDebitAccountGateway.php @@ -0,0 +1,97 @@ + + */ +class SepaDirectDebitAccountGateway +{ + private $_gateway; + private $_config; + private $_http; + + // phpcs:ignore PEAR.Commenting.FunctionComment.Missing + public function __construct($gateway) + { + $this->_gateway = $gateway; + $this->_config = $gateway->config; + $this->_config->assertHasAccessTokenOrKeys(); + $this->_http = new Http($gateway->config); + } + + + /** + * Find a sepaDirectDebitAccount by token + * + * @param string $token sepa direct debit account unique id + * + * @throws Exception\NotFound + * + * @return SepaDirectDebitAccount + */ + public function find($token) + { + $this->_validateId($token); + $path = $this->_config->merchantPath() . '/payment_methods/sepa_debit_account/' . $token; + $response = $this->_http->get($path); + return SepaDirectDebitAccount::factory($response['sepaDebitAccount']); + } + + /** + * Delete a Sepa Direct Debit Account record + * + * @param string $token sepa direct debit account identifier + * + * @return Result + */ + public function delete($token) + { + $this->_validateId($token); + $path = $this->_config->merchantPath() . '/payment_methods/sepa_debit_account/' . $token; + $this->_http->delete($path); + return new Result\Successful(); + } + + /** + * Create a new sale for the current Sepa Direct Debit Account + * + * @param string $token sepa direct debit account identifier + * @param array $transactionAttribs containing request parameters + * + * @return Result\Successful|Result\Error + */ + public function sale($token, $transactionAttribs) + { + $this->_validateId($token); + return Transaction::sale( + array_merge( + $transactionAttribs, + ['paymentMethodToken' => $token] + ) + ); + } + + /** + * Verifies that a valid sepa direct debit account identifier is being used + * + * @param string $identifier + * @param Optional string $identifierType type of identifier supplied, default 'token' + * + * @throws InvalidArgumentException + */ + private function _validateId($identifier = null, $identifierType = 'token') + { + if (empty($identifier)) { + throw new InvalidArgumentException( + 'expected SEPA direct debit account id to be set' + ); + } + } +} diff --git a/lib/Braintree/Test/Nonces.php b/lib/Braintree/Test/Nonces.php index 458a1c94..52fe02f3 100644 --- a/lib/Braintree/Test/Nonces.php +++ b/lib/Braintree/Test/Nonces.php @@ -30,6 +30,7 @@ class Nonces public static $googlePayAmEx = "fake-android-pay-amex-nonce"; public static $abstractTransactable = "fake-abstract-transactable-nonce"; public static $europe = "fake-europe-bank-account-nonce"; + public static $sepaDirectDebit = "fake-sepa-direct-debit-nonce"; public static $transactableVisa = "fake-valid-visa-nonce"; public static $transactableAmEx = "fake-valid-amex-nonce"; public static $transactableMasterCard = "fake-valid-mastercard-nonce"; diff --git a/lib/Braintree/Transaction.php b/lib/Braintree/Transaction.php index cae7d907..222a4c1d 100644 --- a/lib/Braintree/Transaction.php +++ b/lib/Braintree/Transaction.php @@ -277,6 +277,15 @@ protected function _initialize($transactionAttribs) ); } + if (isset($transactionAttribs['sepaDebitAccountDetail'])) { + $this->_set( + 'sepaDirectDebitAccountDetails', + new Transaction\SepaDirectDebitAccountDetails( + $transactionAttribs['sepaDebitAccountDetail'] + ) + ); + } + if (isset($transactionAttribs['paypal'])) { $this->_set( 'paypalDetails', diff --git a/lib/Braintree/Transaction/SepaDirectDebitAccountDetails.php b/lib/Braintree/Transaction/SepaDirectDebitAccountDetails.php new file mode 100644 index 00000000..1969398f --- /dev/null +++ b/lib/Braintree/Transaction/SepaDirectDebitAccountDetails.php @@ -0,0 +1,25 @@ + 'invalid']); } + public function testCreate_worksWithSepaDirectDebitNonce() + { + $nonce = Braintree\Test\Nonces::$sepaDirectDebit; + + $result = Braintree\Customer::create([ + 'paymentMethodNonce' => $nonce + ]); + + $this->assertTrue($result->success); + $this->assertCount(1, $result->customer->sepaDirectDebitAccounts); + } + public function testCreate_worksWithFuturePayPalNonce() { $nonce = Braintree\Test\Nonces::$paypalFuturePayment; diff --git a/tests/integration/DisputeSearchTest.php b/tests/integration/DisputeSearchTest.php index 8e4c1767..2ee50470 100644 --- a/tests/integration/DisputeSearchTest.php +++ b/tests/integration/DisputeSearchTest.php @@ -81,7 +81,9 @@ public function testAdvancededSearch_byChargebackProtectionLevel_returnsDispute( $this->assertEquals(1, count($disputes)); $this->assertEquals($disputes[0]->caseNumber, "CASE-CHARGEBACK-PROTECTED"); $this->assertEquals($disputes[0]->reason, Braintree\Dispute::FRAUD); + // NEXT_MAJOR_VERSION Remove this assertion when chargebackProtectionLevel is removed from the SDK $this->assertEquals($disputes[0]->chargebackProtectionLevel, Braintree\Dispute::EFFORTLESS); + $this->assertEquals($disputes[0]->protectionLevel, Braintree\Dispute::EFFORTLESS_CBP); } public function testAdvancedSearch_byReceivedDateRange_returnsDispute() diff --git a/tests/integration/PaymentMethodNonceTest.php b/tests/integration/PaymentMethodNonceTest.php index fad72315..0453eaa3 100644 --- a/tests/integration/PaymentMethodNonceTest.php +++ b/tests/integration/PaymentMethodNonceTest.php @@ -180,6 +180,17 @@ public function testFind_exposesVenmoDetails() $this->assertEquals('1234567891234567891', $details['venmoUserId']); } + public function testFind_exposesSepaDirectDebitAccountDetails() + { + $nonce = Braintree\PaymentMethodNonce::find(Braintree\Test\Nonces::$sepaDirectDebit); + $details = $nonce->details; + + $this->assertEquals('1234', $details['ibanLastChars']); + $this->assertEquals('a-fake-mp-customer-id', $details['merchantOrPartnerCustomerId']); + $this->assertEquals('a-fake-bank-reference-token', $details['bankReferenceToken']); + $this->assertEquals('RECURRENT', $details['mandateType']); + } + public function testFind_exposesThreeDSecureInfo() { $nonce = 'fake-three-d-secure-visa-full-authentication-nonce'; diff --git a/tests/integration/PaymentMethodTest.php b/tests/integration/PaymentMethodTest.php index dfb38c2f..d0a0b85e 100644 --- a/tests/integration/PaymentMethodTest.php +++ b/tests/integration/PaymentMethodTest.php @@ -263,6 +263,32 @@ public function testCreate_fromFakeVenmoAccountNonce() $this->assertSame("1234567891234567891", $venmoAccount->venmoUserId); } + public function testCreate_fromFakeSepaDirectDebitAccountNonce() + { + $customer = Braintree\Customer::createNoValidate(); + $result = Braintree\PaymentMethod::create(array( + 'customerId' => $customer->id, + 'paymentMethodNonce' => Braintree\Test\Nonces::$sepaDirectDebit + )); + + $this->assertTrue($result->success); + $sepaDirectDebitAccount = $result->paymentMethod; + + $this->assertInstanceOf('Braintree\SepaDirectDebitAccount', $sepaDirectDebitAccount); + $this->assertEquals($customer->id, $sepaDirectDebitAccount->customerId); + $this->assertNotNull($sepaDirectDebitAccount->customerGlobalId); + $this->assertNotNull($sepaDirectDebitAccount->globalId); + $this->assertNotNull($sepaDirectDebitAccount->imageUrl); + $this->assertNotNull($sepaDirectDebitAccount->token); + $this->assertEquals('a-fake-mp-customer-id', $sepaDirectDebitAccount->merchantOrPartnerCustomerId); + $this->assertEquals(true, $sepaDirectDebitAccount->default); + $this->assertEquals('1234', $sepaDirectDebitAccount->last4); + $this->assertEquals('a-fake-bank-reference-token', $sepaDirectDebitAccount->bankReferenceToken); + $this->assertEquals('RECURRENT', $sepaDirectDebitAccount->mandateType); + $this->assertEquals('DateTime', get_class($sepaDirectDebitAccount->createdAt)); + $this->assertEquals('DateTime', get_class($sepaDirectDebitAccount->updatedAt)); + } + public function testCreate_fromUnvalidatedCreditCardNonce() { $customer = Braintree\Customer::createNoValidate(); diff --git a/tests/integration/SepaDirectDebitAccountTest.php b/tests/integration/SepaDirectDebitAccountTest.php new file mode 100644 index 00000000..dec04143 --- /dev/null +++ b/tests/integration/SepaDirectDebitAccountTest.php @@ -0,0 +1,115 @@ + $customer->id, + 'paymentMethodNonce' => $nonce + ]); + + $foundSepaDirectDebitAccount = $result->paymentMethod; + + $this->assertInstanceOf('Braintree\SepaDirectDebitAccount', $foundSepaDirectDebitAccount); + $this->assertEquals($customer->id, $foundSepaDirectDebitAccount->customerId); + $this->assertNotNull($foundSepaDirectDebitAccount->customerGlobalId); + $this->assertNotNull($foundSepaDirectDebitAccount->globalId); + $this->assertNotNull($foundSepaDirectDebitAccount->imageUrl); + $this->assertNotNull($foundSepaDirectDebitAccount->token); + $this->assertEquals('a-fake-mp-customer-id', $foundSepaDirectDebitAccount->merchantOrPartnerCustomerId); + $this->assertEquals(true, $foundSepaDirectDebitAccount->default); + $this->assertEquals('1234', $foundSepaDirectDebitAccount->last4); + $this->assertEquals('a-fake-bank-reference-token', $foundSepaDirectDebitAccount->bankReferenceToken); + $this->assertEquals('RECURRENT', $foundSepaDirectDebitAccount->mandateType); + $this->assertEquals('DateTime', get_class($foundSepaDirectDebitAccount->createdAt)); + $this->assertEquals('DateTime', get_class($foundSepaDirectDebitAccount->updatedAt)); + } + + public function testFind() + { + $customer = Braintree\Customer::createNoValidate(); + $http = new HttpClientApi(Braintree\Configuration::$global); + $nonce = Braintree\Test\Nonces::$sepaDirectDebit; + + $result = Braintree\PaymentMethod::create([ + 'customerId' => $customer->id, + 'paymentMethodNonce' => $nonce + ]); + + $foundSepaDirectDebitAccount = Braintree\SepaDirectDebitAccount::find($result->paymentMethod->token); + + $this->assertInstanceOf('Braintree\SepaDirectDebitAccount', $foundSepaDirectDebitAccount); + $this->assertEquals($customer->id, $foundSepaDirectDebitAccount->customerId); + $this->assertNotNull($foundSepaDirectDebitAccount->customerGlobalId); + $this->assertNotNull($foundSepaDirectDebitAccount->globalId); + $this->assertNotNull($foundSepaDirectDebitAccount->imageUrl); + $this->assertNotNull($foundSepaDirectDebitAccount->token); + $this->assertEquals('a-fake-mp-customer-id', $foundSepaDirectDebitAccount->merchantOrPartnerCustomerId); + $this->assertEquals(true, $foundSepaDirectDebitAccount->default); + $this->assertEquals('1234', $foundSepaDirectDebitAccount->last4); + $this->assertEquals('a-fake-bank-reference-token', $foundSepaDirectDebitAccount->bankReferenceToken); + $this->assertEquals('RECURRENT', $foundSepaDirectDebitAccount->mandateType); + $this->assertEquals('DateTime', get_class($foundSepaDirectDebitAccount->createdAt)); + $this->assertEquals('DateTime', get_class($foundSepaDirectDebitAccount->updatedAt)); + } + + public function testSale_createsASaleUsingGivenToken() + { + $customer = Braintree\Customer::createNoValidate(); + $http = new HttpClientApi(Braintree\Configuration::$global); + $nonce = Braintree\Test\Nonces::$sepaDirectDebit; + + $result = Braintree\PaymentMethod::create([ + 'customerId' => $customer->id, + 'paymentMethodNonce' => $nonce, + ]); + + $pmtToken = $result->paymentMethod->token; + $result = Braintree\SepaDirectDebitAccount::sale($pmtToken, [ + 'amount' => '100.00', + 'options' => [ + 'submitForSettlement' => true + ] + ]); + + $this->assertTrue($result->success); + $transaction = $result->transaction; + + $details = $transaction->sepaDirectDebitAccountDetails; + + $this->assertEquals(Braintree\Transaction::SETTLING, $transaction->status); + $this->assertEquals(Braintree\Transaction::SALE, $transaction->type); + $this->assertEquals('100.00', $transaction->amount); + $this->assertEquals('a-fake-mp-customer-id', $details->merchantOrPartnerCustomerId); + $this->assertEquals('1234', $details->last4); + $this->assertEquals('a-fake-bank-reference-token', $details->bankReferenceToken); + $this->assertEquals('RECURRENT', $details->mandateType); + $this->assertEquals('USD', $details->transactionFeeCurrencyIsoCode); + $this->assertEquals('0.01', $details->transactionFeeAmount); + $this->assertEquals($pmtToken, $details->token); + + $this->assertNull($details->debugId); + $this->assertNull($details->refundId); + $this->assertNull($details->correlationId); + $this->assertNull($details->settlementType); + $this->assertNull($details->paypalV2OrderId); + $this->assertNull($details->refundFromTransactionFeeAmount); + $this->assertNull($details->refundFromTransactionFeeCurrencyIsoCode); + + $this->assertNotNull($details->captureId); + $this->assertNotNull($details->globalId); + } +} diff --git a/tests/integration/TransactionAdvancedSearchTest.php b/tests/integration/TransactionAdvancedSearchTest.php index 9faf0b9a..59a2b7e1 100644 --- a/tests/integration/TransactionAdvancedSearchTest.php +++ b/tests/integration/TransactionAdvancedSearchTest.php @@ -358,6 +358,48 @@ public function test_multipleValueNode_paymentInstrumentType_is_paypal() $this->assertEquals($transaction->id, $collection->firstItem()->id); } + public function test_multipleValueNode_paymentInstrumentType_is_sepaDebit() + { + $transaction = Braintree\Transaction::saleNoValidate([ + 'amount' => Braintree\Test\TransactionAmounts::$authorize, + 'paymentMethodNonce' => Braintree\Test\Nonces::$sepaDirectDebit, + 'options' => [ + 'submitForSettlement' => true + ] + ]); + + $collection = Braintree\Transaction::search([ + Braintree\TransactionSearch::id()->is($transaction->id), + Braintree\TransactionSearch::paymentInstrumentType()->is("SEPADebitAccountDetail") + ]); + + + $this->assertEquals($transaction->paymentInstrumentType, Braintree\PaymentInstrumentType::SEPA_DIRECT_DEBIT_ACCOUNT); + $this->assertEquals($transaction->id, $collection->firstItem()->id); + } + + public function test_multipleValueNode_paypalV2OrderId_is_sepaDebit() + { + $transaction = Braintree\Transaction::saleNoValidate([ + 'amount' => Braintree\Test\TransactionAmounts::$authorize, + 'paymentMethodNonce' => Braintree\Test\Nonces::$sepaDirectDebit, + 'options' => [ + 'submitForSettlement' => true + ] + ]); + + $collection = Braintree\Transaction::search([ + Braintree\TransactionSearch::id()->is($transaction->id), + Braintree\TransactionSearch::sepaDebitPaypalV2OrderId()->is( + $transaction->sepaDirectDebitAccountDetails->paypalV2OrderId + ) + ]); + + + $this->assertEquals($transaction->paymentInstrumentType, Braintree\PaymentInstrumentType::SEPA_DIRECT_DEBIT_ACCOUNT); + $this->assertEquals($transaction->id, $collection->firstItem()->id); + } + public function test_multipleValueNode_paymentInstrumentType_is_applepay() { $transaction = Braintree\Transaction::saleNoValidate([ diff --git a/tests/integration/TransactionTest.php b/tests/integration/TransactionTest.php index 9bcb110d..bc357cc2 100644 --- a/tests/integration/TransactionTest.php +++ b/tests/integration/TransactionTest.php @@ -5734,6 +5734,7 @@ public function testSale_withTravelFlightIndustryDataValidation() public function testSale_withAmexRewardsSucceeds() { + $this->markTestSkipped('Skipping until we have a more stable CI env'); $result = Braintree\Transaction::sale([ 'amount' => '47.00', 'merchantAccountId' => Test\Helper::fakeAmexDirectMerchantAccountId(), @@ -5761,6 +5762,7 @@ public function testSale_withAmexRewardsSucceeds() public function testSale_withAmexRewardsSucceedsEvenIfCardIsIneligible() { + $this->markTestSkipped('Skipping until we have a more stable CI env'); $result = Braintree\Transaction::sale([ 'amount' => '47.00', 'merchantAccountId' => Test\Helper::fakeAmexDirectMerchantAccountId(), @@ -5788,6 +5790,7 @@ public function testSale_withAmexRewardsSucceedsEvenIfCardIsIneligible() public function testSale_withAmexRewardsSucceedsEvenIfCardBalanceIsInsufficient() { + $this->markTestSkipped('Skipping until we have a more stable CI env'); $result = Braintree\Transaction::sale([ 'amount' => '47.00', 'merchantAccountId' => Test\Helper::fakeAmexDirectMerchantAccountId(), @@ -5815,6 +5818,7 @@ public function testSale_withAmexRewardsSucceedsEvenIfCardBalanceIsInsufficient( public function testSubmitForSettlement_withAmexRewardsSucceeds() { + $this->markTestSkipped('Skipping until we have a more stable CI env'); $result = Braintree\Transaction::sale([ 'amount' => '47.00', 'merchantAccountId' => Test\Helper::fakeAmexDirectMerchantAccountId(), @@ -5845,6 +5849,7 @@ public function testSubmitForSettlement_withAmexRewardsSucceeds() public function testSubmitForSettlement_withAmexRewardsSucceedsEvenIfCardIsIneligible() { + $this->markTestSkipped('Skipping until we have a more stable CI env'); $result = Braintree\Transaction::sale([ 'amount' => '47.00', 'merchantAccountId' => Test\Helper::fakeAmexDirectMerchantAccountId(), @@ -5875,6 +5880,7 @@ public function testSubmitForSettlement_withAmexRewardsSucceedsEvenIfCardIsIneli public function testSubmitForSettlement_withAmexRewardsSucceedsEvenIfCardBalanceIsInsufficient() { + $this->markTestSkipped('Skipping until we have a more stable CI env'); $result = Braintree\Transaction::sale([ 'amount' => '47.00', 'merchantAccountId' => Test\Helper::fakeAmexDirectMerchantAccountId(), diff --git a/tests/unit/CustomerTest.php b/tests/unit/CustomerTest.php index 5650267c..76e7ecbd 100644 --- a/tests/unit/CustomerTest.php +++ b/tests/unit/CustomerTest.php @@ -60,4 +60,16 @@ public function testFindErrorsOnWhitespaceId() $this->expectException('InvalidArgumentException'); Braintree\Customer::find('\t'); } + + public function testCustomerWithSepaDebitAccount() + { + $sepaDebitAccount = Braintree\SepaDirectDebitAccount::factory([ + 'last4' => '1234', + ]); + $customer = Braintree\Customer::factory([ + 'sepaDebitAccounts' => $sepaDebitAccount, + ]); + $sepaDirectDebitAccount = $customer -> sepaDirectDebitAccounts[0]; + $this->assertEquals("1234", $sepaDirectDebitAccount -> toArray()['last4']); + } } diff --git a/tests/unit/DisputeTest.php b/tests/unit/DisputeTest.php index dda5f659..09e4aca3 100644 --- a/tests/unit/DisputeTest.php +++ b/tests/unit/DisputeTest.php @@ -135,7 +135,9 @@ public function testConstructorPopulatesNewFields() $this->assertEquals("100.00", $dispute->amountDisputed); $this->assertEquals("0.00", $dispute->amountWon); $this->assertEquals("CB123456", $dispute->caseNumber); + // NEXT_MAJOR_VERSION Remove this assertion when chargebackProtectionLevel is removed from the SDK $this->assertEquals("effortless", $dispute->chargebackProtectionLevel); + $this->assertEquals("Effortless Chargeback Protection tool", $dispute->protectionLevel); $this->assertEquals(DateTime::createFromFormat('Ymd-His', '20130410-105039'), $dispute->createdAt); $this->assertEquals("Forwarded comments", $dispute->processorComments); $this->assertEquals("abc123", $dispute->merchantAccountId); @@ -164,6 +166,66 @@ public function testConstructorPopulatesNewFields() $this->assertEquals(DateTime::createFromFormat('Ymd-His', '20130410-105039'), $dispute->statusHistory[0]->timestamp); } + public function testConstructorHandleStandardCBPLevel() + { + $emptyAttributes = [ + 'chargebackProtectionLevel' => 'standard' + ]; + + $attrs = array_merge([], $this->attributes, $emptyAttributes); + + $dispute = Braintree\Dispute::factory($attrs); + + // NEXT_MAJOR_VERSION Remove this assertion when chargebackProtectionLevel is removed from the SDK + $this->assertEquals("standard", $dispute->chargebackProtectionLevel); + $this->assertEquals("Chargeback Protection tool", $dispute->protectionLevel); + } + + public function testConstructorHandleNullCBPLevel() + { + $emptyAttributes = [ + 'chargebackProtectionLevel' => null + ]; + + $attrs = array_merge([], $this->attributes, $emptyAttributes); + + $dispute = Braintree\Dispute::factory($attrs); + + // NEXT_MAJOR_VERSION Remove this assertion when chargebackProtectionLevel is removed from the SDK + $this->assertEquals("", $dispute->chargebackProtectionLevel); + $this->assertEquals("No Protection", $dispute->protectionLevel); + } + + public function testConstructorHandleEmptyCBPLevel() + { + $emptyAttributes = [ + 'chargebackProtectionLevel' => '' + ]; + + $attrs = array_merge([], $this->attributes, $emptyAttributes); + + $dispute = Braintree\Dispute::factory($attrs); + + // NEXT_MAJOR_VERSION Remove this assertion when chargebackProtectionLevel is removed from the SDK + $this->assertEquals("", $dispute->chargebackProtectionLevel); + $this->assertEquals("No Protection", $dispute->protectionLevel); + } + + public function testConstructorHandleNotprotectedCBPLevel() + { + $emptyAttributes = [ + 'chargebackProtectionLevel' => 'not_protected' + ]; + + $attrs = array_merge([], $this->attributes, $emptyAttributes); + + $dispute = Braintree\Dispute::factory($attrs); + + // NEXT_MAJOR_VERSION Remove this assertion when chargebackProtectionLevel is removed from the SDK + $this->assertEquals("not_protected", $dispute->chargebackProtectionLevel); + $this->assertEquals("No Protection", $dispute->protectionLevel); + } + public function testConstructorHandlesNullFields() { $emptyAttributes = [ diff --git a/tests/unit/SepaDirectDebitAccountTest.php b/tests/unit/SepaDirectDebitAccountTest.php new file mode 100644 index 00000000..cc57758d --- /dev/null +++ b/tests/unit/SepaDirectDebitAccountTest.php @@ -0,0 +1,33 @@ +expectError(); + $sepaDirectDebitAccount = Braintree\SepaDirectDebitAccount::factory([]); + $sepaDirectDebitAccount->foo; + } + + public function testIsDefault() + { + $sepaDirectDebitAccount = Braintree\SepaDirectDebitAccount::factory(['default' => true]); + $this->assertTrue($sepaDirectDebitAccount->isDefault()); + + $sepaDirectDebitAccount = Braintree\SepaDirectDebitAccount::factory(['default' => false]); + $this->assertFalse($sepaDirectDebitAccount->isDefault()); + } + + public function testErrorsOnFindWithBlankArgument() + { + $this->expectException('InvalidArgumentException'); + Braintree\SepaDirectDebitAccount::find(''); + } +} diff --git a/tests/unit/TransactionTest.php b/tests/unit/TransactionTest.php index 69fbe370..618cea8d 100644 --- a/tests/unit/TransactionTest.php +++ b/tests/unit/TransactionTest.php @@ -4,6 +4,7 @@ require_once dirname(__DIR__) . '/Setup.php'; +use DateTime; use Test\Setup; use Braintree; @@ -105,6 +106,27 @@ public function testSaleWithoutSkipAdvancedFraudCheckingOption() ]); } + public function testTransactionWithSepaDebitAccountDetail() + { + $transaction = Braintree\Transaction::factory([ + 'id' => '123', + 'type' => 'sale', + 'amount' => '12.34', + 'status' => 'settled', + 'customer' => [], + 'creditCard' => ['expirationMonth' => '05', 'expirationYear' => '2010', 'bin' => '510510', 'last4' => '5100'], + 'createdAt' => DateTime::createFromFormat('Ymd', '20121212'), + 'sepaDebitAccountDetail' => [ + [ + 'last4' => "1234", + ], + ] + ]); + + $details = $transaction -> sepaDirectDebitAccountDetails -> toArray()[0]; + $this->assertEquals("1234", $details["last4"]); + } + private function mockTransactionGatewayDoCreate() { return $this->getMockBuilder('Braintree\TransactionGateway')