From 46e583bc7b1d07bbccc1198e9e61a462310e46b2 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 26 Nov 2018 18:13:40 +1300 Subject: [PATCH 01/11] Refine handling of 'card' parameter to support generic integrations When setting up a paypal rest Authorize request there are a few possibilities we have a payment token we have a credit card number & cvv & expiry we have neither of the above and should be redirected to paypal for us to log in and authorize the payment. Currently the code understands the difference as cardReference is present 'card' object is present neither of the above. However, the card object also holds other information - email, address, phone etc. Even if these are not used by Paypal Rest the calling code should not need to know that it can't set these under a specific combination of circumstances. This fix looks more deeply into the passed card parameter for the presence of the actual card fields - ie.card number, expiry - and otherwise concludes this is not a card-present transations and allows the redirect to proceed --- src/Message/RestAuthorizeRequest.php | 31 ++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/Message/RestAuthorizeRequest.php b/src/Message/RestAuthorizeRequest.php index 19762f95..321910e6 100644 --- a/src/Message/RestAuthorizeRequest.php +++ b/src/Message/RestAuthorizeRequest.php @@ -5,6 +5,8 @@ namespace Omnipay\PayPal\Message; +use Omnipay\Common\Exception\InvalidCreditCardException; + /** * PayPal REST Authorize Request * @@ -259,9 +261,8 @@ public function getData() 'credit_card_id' => $this->getCardReference(), ), ); - } elseif ($this->getCard()) { + } elseif ($this->validCardPresent()) { $this->validate('amount', 'card'); - $this->getCard()->validate(); $data['payer']['funding_instruments'][] = array( 'credit_card' => array( @@ -304,6 +305,32 @@ public function getData() return $data; } + /** + * Has a valid card been passed in the Omnipay parameters. + * + * Omnipay supports details other than card details in the card parameter (e.g. + * billing address) so a generic omnipay integration might set the 'card' when + * there is no number present. In which case the Rest integration should fall + * back to the next method. + */ + public function validCardPresent() + { + $card = $this->getCard(); + if (!$card) { + return false; + } + try { + $card->validate(); + } catch (InvalidCreditCardException $e) { + if (stristr($e->getMessage(), 'is required')) { + return false; + } else { + throw $e; + } + } + return true; + } + /** * Get the experience profile id * From 5379a837ebf63c548410b0e149308715a8af21c0 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 6 Aug 2018 14:35:19 +1200 Subject: [PATCH 02/11] Add test for Refine handling of 'card' parameter to support generic integrations. In a generic integration the card parameter might exist to hold billing address details even when the card number is not provided. We don't expect the integration to hold knowledge that 'paypal rest won't work if you pass a 'card' paramter when you are pre-authorizing a token' so we should look more deeply into the passed card parameter for the presence of the actual card fields - ie. card number, expiry ,cvv --- tests/Message/RestAuthorizeRequestTest.php | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/Message/RestAuthorizeRequestTest.php b/tests/Message/RestAuthorizeRequestTest.php index 5f0f962b..ec04f853 100644 --- a/tests/Message/RestAuthorizeRequestTest.php +++ b/tests/Message/RestAuthorizeRequestTest.php @@ -47,6 +47,33 @@ public function testGetDataWithoutCard() $this->assertSame('https://www.example.com/cancel', $data['redirect_urls']['cancel_url']); } + /** + * This tests that having a card object with no card details acts as 'no card'. + * + * We may have a card object holding billing details but no card details. This + * should be treated as a card-not-present rather than as invalid. + */ + public function testGetDataWitLimitedCard() + { + $this->request->setTransactionId('abc123'); + $this->request->setDescription('Sheep'); + $this->request->setCard(new CreditCard(['firstName' => 'Example'])); + + $data = $this->request->getData(); + + $this->assertSame('authorize', $data['intent']); + $this->assertSame('paypal', $data['payer']['payment_method']); + $this->assertSame('10.00', $data['transactions'][0]['amount']['total']); + $this->assertSame('USD', $data['transactions'][0]['amount']['currency']); + $this->assertSame('abc123 : Sheep', $data['transactions'][0]['description']); + + // Funding instruments must not be set, otherwise paypal API will give error 500. + $this->assertArrayNotHasKey('funding_instruments', $data['payer']); + + $this->assertSame('https://www.example.com/return', $data['redirect_urls']['return_url']); + $this->assertSame('https://www.example.com/cancel', $data['redirect_urls']['cancel_url']); + } + public function testGetDataWithCard() { $card = new CreditCard($this->getValidCard()); From df962d19435ea1722d7664b987b61c9caab57268 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 12 Dec 2018 20:06:42 +1300 Subject: [PATCH 03/11] Return token id if other possible values not present in getTransactionReference. When processing a request with no card present a token is retrieved from paypal which is the used by a paypal browser script to open a paypal popup to approve the token. It's a bit arguable whether this is best retrieved via getTransactionReference or getCardReference. cardReference makes sense in that the token is used in place of a card but it's also the only reference we have for the transaction at this point. Related : https://github.com/thephpleague/omnipay-paypal/issues/219 https://github.com/thephpleague/omnipay-paypal/issues/215 --- src/Message/RestResponse.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Message/RestResponse.php b/src/Message/RestResponse.php index 4ffdbe9b..fa391e06 100644 --- a/src/Message/RestResponse.php +++ b/src/Message/RestResponse.php @@ -42,6 +42,13 @@ public function getTransactionReference() return $this->data['id']; } + if (isset($this->data['token_id'])) { + // This would be present when dealing with a case where the user + // authorize's their card within paypal. In this case the transactionReference + // will be used during the redirect. + return $this->data['token_id']; + } + return null; } @@ -63,6 +70,9 @@ public function getCode() return $this->statusCode; } + /** + * Get a string that will represent the stored card in future requests. + */ public function getCardReference() { if (isset($this->data['id'])) { From 98997c3db74e126cc0fbb95c9eb09b1f7783bba4 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 10 Dec 2018 15:53:55 +1300 Subject: [PATCH 04/11] =?UTF-8?q?Add=20handling=20for=20creating=20a=20tok?= =?UTF-8?q?en=20to=20rebill=20a=20card=20when=20card=20not=E2=80=A6=20pres?= =?UTF-8?q?ent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This scenario occurs when the pay with paypal button is clicked and authentication is done offsite --- src/Message/RestCreateCardRequest.php | 86 +++++++++++++++++++-------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/src/Message/RestCreateCardRequest.php b/src/Message/RestCreateCardRequest.php index f36fa6d3..0870bc29 100644 --- a/src/Message/RestCreateCardRequest.php +++ b/src/Message/RestCreateCardRequest.php @@ -72,39 +72,73 @@ class RestCreateCardRequest extends AbstractRestRequest { public function getData() { - $this->validate('card'); - $this->getCard()->validate(); + if ($this->isCardPresent()) { + $this->validate('card'); + $this->getCard()->validate(); - $data = array( - 'number' => $this->getCard()->getNumber(), - 'type' => $this->getCard()->getBrand(), - 'expire_month' => $this->getCard()->getExpiryMonth(), - 'expire_year' => $this->getCard()->getExpiryYear(), - 'cvv2' => $this->getCard()->getCvv(), - 'first_name' => $this->getCard()->getFirstName(), - 'last_name' => $this->getCard()->getLastName(), - 'billing_address' => array( - 'line1' => $this->getCard()->getAddress1(), - //'line2' => $this->getCard()->getAddress2(), - 'city' => $this->getCard()->getCity(), - 'state' => $this->getCard()->getState(), - 'postal_code' => $this->getCard()->getPostcode(), - 'country_code' => strtoupper($this->getCard()->getCountry()), - ) - ); + $data = array( + 'number' => $this->getCard()->getNumber(), + 'type' => $this->getCard()->getBrand(), + 'expire_month' => $this->getCard()->getExpiryMonth(), + 'expire_year' => $this->getCard()->getExpiryYear(), + 'cvv2' => $this->getCard()->getCvv(), + 'first_name' => $this->getCard()->getFirstName(), + 'last_name' => $this->getCard()->getLastName(), + 'billing_address' => array( + 'line1' => $this->getCard()->getAddress1(), + //'line2' => $this->getCard()->getAddress2(), + 'city' => $this->getCard()->getCity(), + 'state' => $this->getCard()->getState(), + 'postal_code' => $this->getCard()->getPostcode(), + 'country_code' => strtoupper($this->getCard()->getCountry()), + ) + ); - // There's currently a quirk with the REST API that requires line2 to be - // non-empty if it's present. Jul 14, 2014 - $line2 = $this->getCard()->getAddress2(); - if (!empty($line2)) { - $data['billing_address']['line2'] = $line2; + // There's currently a quirk with the REST API that requires line2 to be + // non-empty if it's present. Jul 14, 2014 + $line2 = $this->getCard()->getAddress2(); + if (!empty($line2)) { + $data['billing_address']['line2'] = $line2; + } + } else { + // We are creating a token to rebill a paypal account. + // this equates to the meaning of 'createCard' in other processors + // such as Stripe. + // https://developer.paypal.com/docs/limited-release/reference-transactions/#create-billing-agreement + $data = ["description" => $this->getDescription(), + "payer" => ["payment_method" => "PAYPAL"], + "plan" => + [ + "type" => "MERCHANT_INITIATED_BILLING", + "merchant_preferences" => + [ + "return_url" => $this->getReturnUrl(), + "cancel_url" => $this->getCancelUrl(), + "notify_url" => $this->getNotifyUrl(), + "accepted_pymt_type" => "INSTANT", + "skip_shipping_address" => true, + "immutable_shipping_address" => false, + ] + ] + ]; } - return $data; } protected function getEndpoint() { - return parent::getEndpoint() . '/vault/credit-cards'; + if ($this->isCardPresent()) { + return parent::getEndpoint() . '/vault/credit-cards'; + } else { + return parent::getEndpoint() . '/billing-agreements/agreement-tokens'; + } + } + + /** + * Is the card present (or are we dealing with a paypal account transaction) + */ + protected function isCardPresent() + { + return $this->getCard()->getNumber(); } } From 2c64a95563cea1d581ecb8101bf751397234bf79 Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 12 Dec 2018 22:58:48 +1300 Subject: [PATCH 05/11] Add completeCard actions to complete the authorization process on a new paypal agreement This submits the required paypal requests to complete the authorization of a stored token representing a paypal authorization - ie. like creating a re-usable card but when the card is not presented via the php layer --- src/Message/RestCompleteCreateCardRequest.php | 117 ++++++++++++++++++ src/RestGateway.php | 14 +++ 2 files changed, 131 insertions(+) create mode 100644 src/Message/RestCompleteCreateCardRequest.php diff --git a/src/Message/RestCompleteCreateCardRequest.php b/src/Message/RestCompleteCreateCardRequest.php new file mode 100644 index 00000000..b693d5ea --- /dev/null +++ b/src/Message/RestCompleteCreateCardRequest.php @@ -0,0 +1,117 @@ +getRedirectUrl(). Once + * the customer has approved the agreement and be returned to the returnUrl + * in the call. The returnUrl can contain the following code to complete + * the agreement: + * + * + * // Create a gateway for the PayPal REST Gateway + * // (routes to GatewayFactory::create) + * $gateway = Omnipay::create('PayPal_Rest'); + * + * // Initialise the gateway + * $gateway->initialize(array( + * 'clientId' => 'MyPayPalClientId', + * 'secret' => 'MyPayPalSecret', + * 'testMode' => true, // Or false when you are ready for live transactions + * )); + * + * // Crate a card + * $transaction = $gateway->completeCreateCard(array( + * 'transactionReference' => $subscription_id, + * )); + * $response = $transaction->send(); + * if ($response->isSuccessful()) { + * echo "Complete Subscription transaction was successful!\n"; + * $subscription_id = $response->getTransactionReference(); + * echo "Subscription reference = " . $subscription_id; + * } + * + * + * Note that the subscription_id that you get from calling the response's + * getTransactionReference() method at the end of the completeSubscription + * call will be different to the one that you got after calling the response's + * getTransactionReference() method at the end of the createSubscription + * call. The one that you get from completeSubscription is the correct + * one to use going forwards (e.g. for cancelling or updating the subscription). + * + * ### Request Sample + * + * This is from the PayPal web site: + * + * + * curl -v POST https://api.sandbox.paypal.com/v1/payments/billing-agreements/EC-0JP008296V451950C/agreement-execute \ + * -H 'Content-Type:application/json' \ + * -H 'Authorization: Bearer ' \ + * -d '{}' + * + * + * ### Response Sample + * + * This is from the PayPal web site: + * + * + * { + * "id": "I-0LN988D3JACS", + * "links": [ + * { + * "href": "https://api.sandbox.paypal.com/v1/payments/billing-agreements/I-0LN988D3JACS", + * "rel": "self", + * "method": "GET" + * } + * ] + * } + * + * + * @link https://developer.paypal.com/docs/api/#execute-an-agreement + * @see RestCreateSubscriptionRequest + * @see Omnipay\PayPal\RestGateway + */ +class RestCompleteCreateCardRequest extends AbstractRestRequest +{ + public function getData() + { + $this->validate('transactionReference'); + + return ['token_id' => $this->getTransactionReference()]; + } + + /** + * Get transaction endpoint. + * + * Subscriptions are executed using the /billing-agreements resource. + * + * @return string + */ + protected function getEndpoint() + { + return parent::getEndpoint() . '/billing-agreements/agreements'; + } +} diff --git a/src/RestGateway.php b/src/RestGateway.php index d6ae4f3f..62e63625 100644 --- a/src/RestGateway.php +++ b/src/RestGateway.php @@ -560,6 +560,20 @@ public function createCard(array $parameters = array()) return $this->createRequest('\Omnipay\PayPal\Message\RestCreateCardRequest', $parameters); } + /** + * Complete a card creation when using paypal account method. + * + * Use this call to create a billing agreement after the buyer approves it. + * + * @link https://developer.paypal.com/docs/limited-release/reference-transactions/#create-billing-agreement-token + * @param array $parameters + * @return \Omnipay\PayPal\Message\RestCompleteCreateCardRequest + */ + public function completeCreateCard(array $parameters = array()) + { + return $this->createRequest('\Omnipay\PayPal\Message\RestCompleteCreateCardRequest', $parameters); + } + /** * Delete a credit card from the vault. * From 0a209488d6b68e82490b8d5cf57df9e978f9f80a Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 13 Dec 2018 00:42:02 +1300 Subject: [PATCH 06/11] Add authorize handling for using a stored billing agreement --- src/Message/RestAuthorizeRequest.php | 15 ++++++++++++++- src/Message/RestCompleteCreateCardRequest.php | 2 +- src/Message/RestResponse.php | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Message/RestAuthorizeRequest.php b/src/Message/RestAuthorizeRequest.php index 321910e6..f193996d 100644 --- a/src/Message/RestAuthorizeRequest.php +++ b/src/Message/RestAuthorizeRequest.php @@ -253,7 +253,20 @@ public function getData() $data['transactions'][0]['item_list']["items"] = $itemList; } - if ($this->getCardReference()) { + if ($this->getCardReference() && substr($this->getCardReference(), 0, 2) === 'B-') { + // Card references can be for a billing agreement (format B-....) or a stored card format CARD-... + // here billing agreement + // https://developer.paypal.com/docs/limited-release/reference-transactions/#use-a-reference-transaction-to-make-a-payment + $this->validate('amount'); + $data['payer']['funding_instruments'][] = array( + 'billing' => array( + 'billing_agreement_id' => $this->getCardReference(), + ), + ); + $data['payer']['payment_method'] = 'paypal'; + } elseif ($this->getCardReference()) { + // stored card + //https://developer.paypal.com/docs/integration/direct/vault/#pay-with-vaulted-card $this->validate('amount'); $data['payer']['funding_instruments'][] = array( diff --git a/src/Message/RestCompleteCreateCardRequest.php b/src/Message/RestCompleteCreateCardRequest.php index b693d5ea..1368b364 100644 --- a/src/Message/RestCompleteCreateCardRequest.php +++ b/src/Message/RestCompleteCreateCardRequest.php @@ -6,7 +6,7 @@ namespace Omnipay\PayPal\Message; /** - * PayPal REST Complete Subscription Request + * PayPal REST Complete Create Card Request * * Use this call to create a billing agreement after the buyer approves it. * diff --git a/src/Message/RestResponse.php b/src/Message/RestResponse.php index fa391e06..e344a60e 100644 --- a/src/Message/RestResponse.php +++ b/src/Message/RestResponse.php @@ -72,11 +72,26 @@ public function getCode() /** * Get a string that will represent the stored card in future requests. + * + * If they have authorised the payment through submitting a card through Omnipay + * this will be a reference to the card in the vault. If they have authorised the payment + * through paypal this will be a reference to an approved billing agreement. */ public function getCardReference() { + if ($this->isPaypalApproval()) { + return $this->data['payer']['funding_instruments'][0]['billing']['billing_agreement_id']; + } if (isset($this->data['id'])) { return $this->data['id']; } } + + public function isPaypalApproval() + { + if (!isset($this->data['payer']['payment_method'])) { + return false; + } + return ($this->data['payer']['payment_method'] === 'paypal'); + } } From e6357965937d9a60c293ace9e7707597b54a973e Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 17 Dec 2018 14:24:50 +1300 Subject: [PATCH 07/11] E-notice fix --- src/Message/RestResponse.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Message/RestResponse.php b/src/Message/RestResponse.php index e344a60e..7faef9ec 100644 --- a/src/Message/RestResponse.php +++ b/src/Message/RestResponse.php @@ -80,7 +80,10 @@ public function getCode() public function getCardReference() { if ($this->isPaypalApproval()) { - return $this->data['payer']['funding_instruments'][0]['billing']['billing_agreement_id']; + if (isset($this->data['funding_instruments'])) { + return $this->data['payer']['funding_instruments'][0]['billing']['billing_agreement_id']; + } + return false; } if (isset($this->data['id'])) { return $this->data['id']; From 8a6f38a9a4665ce024b719293da7a273c39a5882 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 25 Mar 2019 18:17:22 +1300 Subject: [PATCH 08/11] Hack for smartbuttons per https://github.com/thephpleague/omnipay-paypal/issues/228 --- src/Message/RestAuthorizeResponse.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Message/RestAuthorizeResponse.php b/src/Message/RestAuthorizeResponse.php index 9359db0b..004be778 100644 --- a/src/Message/RestAuthorizeResponse.php +++ b/src/Message/RestAuthorizeResponse.php @@ -61,7 +61,11 @@ public function getTransactionReference() // The transaction reference for a paypal purchase request or for a // paypal create subscription request ends up in the execute URL // in the links section of the response. - $completeUrl = $this->getCompleteUrl(); + // this has been HACKED. Paypal changed it's product offering & introduced + // smart buttons instead of checkout.js and it needs something different returned. + // notes here https://github.com/thephpleague/omnipay-paypal/issues/228 but I couldn't see a + // good answer here so I just hacked. + $completeUrl = $this->getRedirectUrl(); if (empty($completeUrl)) { return parent::getTransactionReference(); } @@ -70,6 +74,8 @@ public function getTransactionReference() // The last element of the URL should be "execute" $execute = end($urlParts); + // part of hack above. + return (str_replace('webscr?cmd=_express-checkout&token=', '', $execute)); if (!in_array($execute, array('execute', 'agreement-execute'))) { return parent::getTransactionReference(); } From 707c811738b0f817461f0c7dd84562c161f90aa8 Mon Sep 17 00:00:00 2001 From: eileen Date: Mon, 21 Dec 2020 21:55:07 +1300 Subject: [PATCH 09/11] Update grumphp to a version compatibile with composer 2 --- composer.json | 2 +- grumphp.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 1377e600..f702b881 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "require-dev": { "omnipay/tests": "^3", "squizlabs/php_codesniffer": "^3", - "phpro/grumphp": "^0.14" + "phpro/grumphp": "1.3.0" }, "extra": { "branch-alias": { diff --git a/grumphp.yml b/grumphp.yml index 4b767a09..03db70d7 100644 --- a/grumphp.yml +++ b/grumphp.yml @@ -1,4 +1,4 @@ -parameters: +grumphp: git_dir: . bin_dir: vendor/bin tasks: From 05c7a38fee422bb60728b4cbddcfd2f158b993b4 Mon Sep 17 00:00:00 2001 From: DDEV-Local User Date: Wed, 16 Dec 2020 06:44:04 +0000 Subject: [PATCH 10/11] Add getProcessorFeeAmount and update tests/sample data. --- src/Message/RestResponse.php | 27 +++++++++++++++++++++++++++ tests/Message/RestResponseTest.php | 4 ++++ tests/Mock/RestPurchaseSuccess.txt | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Message/RestResponse.php b/src/Message/RestResponse.php index 7faef9ec..676ce1f5 100644 --- a/src/Message/RestResponse.php +++ b/src/Message/RestResponse.php @@ -52,6 +52,33 @@ public function getTransactionReference() return null; } + /* + * The fee taken by PayPal for the transaction. Available from the Sale + * object in RelatedResources only once a transaction has been completed + * and funds have been received by merchant. + * + * https://developer.paypal.com/docs/api/payments/v1/#definition-sale + * + * + * There may be a 'fee' field associated with amount details object but + * this seems to pertain to a handling_fee charged to client rather than + * the PayPal processing fee. + * + * https://developer.paypal.com/docs/api/payments/v1/#definition-details + */ + public function getProcessorFeeAmount() + { + if (!empty($this->data['transactions']) && + !empty($this->data['transactions'][0]['related_resources']) && + !empty($this->data['transactions'][0]['related_resources'][0]['sale']) && + !empty($this->data['transactions'][0]['related_resources'][0]['sale']['transaction_fee'])) + { + return $this->data['transactions'][0]['related_resources'][0]['sale']['transaction_fee']['value']; + } + + return null; + } + public function getMessage() { if (isset($this->data['error_description'])) { diff --git a/tests/Message/RestResponseTest.php b/tests/Message/RestResponseTest.php index ce852bb8..f583d9a8 100644 --- a/tests/Message/RestResponseTest.php +++ b/tests/Message/RestResponseTest.php @@ -14,6 +14,7 @@ public function testPurchaseSuccess() $this->assertTrue($response->isSuccessful()); $this->assertSame('44E89981F8714392Y', $response->getTransactionReference()); + $this->assertSame("0.50", $response->getProcessorFeeAmount()); $this->assertNull($response->getMessage()); } @@ -25,6 +26,7 @@ public function testPurchaseFailure() $this->assertFalse($response->isSuccessful()); $this->assertNull($response->getTransactionReference()); + $this->assertNull($response->getProcessorFeeAmount()); $this->assertSame('Invalid request - see details', $response->getMessage()); } @@ -37,6 +39,7 @@ public function testCompletePurchaseSuccess() $this->assertTrue($response->isSuccessful()); $this->assertSame('9EA05739TH369572R', $response->getTransactionReference()); + $this->assertNull($response->getProcessorFeeAmount()); $this->assertNull($response->getMessage()); } @@ -72,6 +75,7 @@ public function testAuthorizeSuccess() $this->assertTrue($response->isSuccessful()); $this->assertSame('58N7596879166930B', $response->getTransactionReference()); + $this->assertNull($response->getProcessorFeeAmount()); $this->assertNull($response->getMessage()); } diff --git a/tests/Mock/RestPurchaseSuccess.txt b/tests/Mock/RestPurchaseSuccess.txt index 0e43e2f7..d12703c8 100644 --- a/tests/Mock/RestPurchaseSuccess.txt +++ b/tests/Mock/RestPurchaseSuccess.txt @@ -9,4 +9,4 @@ Date: Thu, 03 Jul 2014 14:11:10 GMT Content-Type: application/json Content-Length: 1243 -{"id":"PAY-6RT04683U7444573DKO2WI6A","create_time":"2014-07-03T14:11:04Z","update_time":"2014-07-03T14:11:10Z","state":"approved","intent":"sale","payer":{"payment_method":"credit_card","funding_instruments":[{"credit_card":{"type":"mastercard","number":"xxxxxxxxxxxx5559","expire_month":"12","expire_year":"2018","first_name":"Betsy","last_name":"Buyer"}}]},"transactions":[{"amount":{"total":"7.47","currency":"USD","details":{"subtotal":"7.47"}},"description":"This is the payment transaction description.","related_resources":[{"sale":{"id":"44E89981F8714392Y","create_time":"2014-07-03T14:11:04Z","update_time":"2014-07-03T14:11:10Z","state":"completed","amount":{"total":"7.47","currency":"USD"},"parent_payment":"PAY-6RT04683U7444573DKO2WI6A","links":[{"href":"https://api.sandbox.paypal.com/v1/payments/sale/44E89981F8714392Y","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/payments/sale/44E89981F8714392Y/refund","rel":"refund","method":"POST"},{"href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-6RT04683U7444573DKO2WI6A","rel":"parent_payment","method":"GET"}]}}]}],"links":[{"href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-6RT04683U7444573DKO2WI6A","rel":"self","method":"GET"}]} +{"id":"PAY-6RT04683U7444573DKO2WI6A","create_time":"2014-07-03T14:11:04Z","update_time":"2014-07-03T14:11:10Z","state":"approved","intent":"sale","payer":{"payment_method":"credit_card","funding_instruments":[{"credit_card":{"type":"mastercard","number":"xxxxxxxxxxxx5559","expire_month":"12","expire_year":"2018","first_name":"Betsy","last_name":"Buyer"}}]},"transactions":[{"amount":{"total":"7.47","currency":"USD","details":{"subtotal":"7.47"}},"description":"This is the payment transaction description.","related_resources":[{"sale":{"id":"44E89981F8714392Y","create_time":"2014-07-03T14:11:04Z","update_time":"2014-07-03T14:11:10Z","state":"completed","amount":{"total":"7.47","currency":"USD"},"transaction_fee":{"value":"0.50","currency":"USD"},"parent_payment":"PAY-6RT04683U7444573DKO2WI6A","links":[{"href":"https://api.sandbox.paypal.com/v1/payments/sale/44E89981F8714392Y","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/payments/sale/44E89981F8714392Y/refund","rel":"refund","method":"POST"},{"href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-6RT04683U7444573DKO2WI6A","rel":"parent_payment","method":"GET"}]}}]}],"links":[{"href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-6RT04683U7444573DKO2WI6A","rel":"self","method":"GET"}]} From a3ab73e8abe6b925f52e50e87c7a0cb55bffaeb1 Mon Sep 17 00:00:00 2001 From: Ana Nelson Date: Tue, 15 Dec 2020 23:00:22 -0800 Subject: [PATCH 11/11] Fix formatting. --- src/Message/RestResponse.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Message/RestResponse.php b/src/Message/RestResponse.php index 676ce1f5..2806ef33 100644 --- a/src/Message/RestResponse.php +++ b/src/Message/RestResponse.php @@ -71,8 +71,7 @@ public function getProcessorFeeAmount() if (!empty($this->data['transactions']) && !empty($this->data['transactions'][0]['related_resources']) && !empty($this->data['transactions'][0]['related_resources'][0]['sale']) && - !empty($this->data['transactions'][0]['related_resources'][0]['sale']['transaction_fee'])) - { + !empty($this->data['transactions'][0]['related_resources'][0]['sale']['transaction_fee'])) { return $this->data['transactions'][0]['related_resources'][0]['sale']['transaction_fee']['value']; }