diff --git a/app/code/Magento/Authorizenet/Model/Source/Cctype.php b/app/code/Magento/Authorizenet/Model/Source/Cctype.php index 52ef75c3a3136..a6e173474ae86 100644 --- a/app/code/Magento/Authorizenet/Model/Source/Cctype.php +++ b/app/code/Magento/Authorizenet/Model/Source/Cctype.php @@ -17,6 +17,6 @@ class Cctype extends PaymentCctype */ public function getAllowedTypes() { - return ['VI', 'MC', 'AE', 'DI', 'OT', 'JCB', 'DN']; + return ['VI', 'MC', 'AE', 'DI', 'JCB', 'DN']; } } diff --git a/app/code/Magento/Braintree/Block/Customer/PayPal/VaultTokenRenderer.php b/app/code/Magento/Braintree/Block/Customer/PayPal/VaultTokenRenderer.php new file mode 100644 index 0000000000000..0af012352a2b8 --- /dev/null +++ b/app/code/Magento/Braintree/Block/Customer/PayPal/VaultTokenRenderer.php @@ -0,0 +1,76 @@ +config = $config; + } + + /** + * @inheritdoc + */ + public function getIconUrl() + { + return $this->config->getPayPalIcon()['url']; + } + + /** + * @inheritdoc + */ + public function getIconHeight() + { + return $this->config->getPayPalIcon()['height']; + } + + /** + * @inheritdoc + */ + public function getIconWidth() + { + return $this->config->getPayPalIcon()['width']; + } + + /** + * Can render specified token + * + * @param PaymentTokenInterface $token + * @return boolean + */ + public function canRender(PaymentTokenInterface $token) + { + return $token->getPaymentMethodCode() === ConfigProvider::PAYPAL_CODE; + } + + /** + * Get email of PayPal payer + * @return string + */ + public function getPayerEmail() + { + return $this->getTokenDetails()['payerEmail']; + } +} diff --git a/app/code/Magento/Braintree/Gateway/Command/CaptureStrategyCommand.php b/app/code/Magento/Braintree/Gateway/Command/CaptureStrategyCommand.php index 3119042b776e2..a03546b366045 100644 --- a/app/code/Magento/Braintree/Gateway/Command/CaptureStrategyCommand.php +++ b/app/code/Magento/Braintree/Gateway/Command/CaptureStrategyCommand.php @@ -166,16 +166,25 @@ private function isExpiredAuthorization(OrderPaymentInterface $payment) */ private function isExistsCaptureTransaction(OrderPaymentInterface $payment) { - $filters[] = $this->filterBuilder->setField('payment_id') - ->setValue($payment->getId()) - ->create(); + $this->searchCriteriaBuilder->addFilters( + [ + $this->filterBuilder + ->setField('payment_id') + ->setValue($payment->getId()) + ->create(), + ] + ); - $filters[] = $this->filterBuilder->setField('txn_type') - ->setValue(TransactionInterface::TYPE_CAPTURE) - ->create(); + $this->searchCriteriaBuilder->addFilters( + [ + $this->filterBuilder + ->setField('txn_type') + ->setValue(TransactionInterface::TYPE_CAPTURE) + ->create(), + ] + ); - $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters) - ->create(); + $searchCriteria = $this->searchCriteriaBuilder->create(); $count = $this->transactionRepository->getList($searchCriteria)->getTotalCount(); return (boolean) $count; diff --git a/app/code/Magento/Braintree/Gateway/Config/Config.php b/app/code/Magento/Braintree/Gateway/Config/Config.php index 4d612bf5e64f3..774b8e365368f 100644 --- a/app/code/Magento/Braintree/Gateway/Config/Config.php +++ b/app/code/Magento/Braintree/Gateway/Config/Config.php @@ -30,6 +30,14 @@ class Config extends \Magento\Payment\Gateway\Config\Config const KEY_KOUNT_MERCHANT_ID = 'kount_id'; const FRAUD_PROTECTION = 'fraudprotection'; + /** + * Get list of available dynamic descriptors keys + * @var array + */ + private static $dynamicDescriptorKeys = [ + 'name', 'phone', 'url' + ]; + /** * Return the country specific card type config * @@ -170,6 +178,22 @@ public function isActive() return (bool) $this->getValue(self::KEY_ACTIVE); } + /** + * Get list of configured dynamic descriptors + * @return array + */ + public function getDynamicDescriptors() + { + $values = []; + foreach (self::$dynamicDescriptorKeys as $key) { + $value = $this->getValue('descriptor_' . $key); + if (!empty($value)) { + $values[$key] = $value; + } + } + return $values; + } + /** * Get Merchant account ID * diff --git a/app/code/Magento/Braintree/Gateway/Config/PayPal/Config.php b/app/code/Magento/Braintree/Gateway/Config/PayPal/Config.php index 06ff216acd49c..f94c6abfd773b 100644 --- a/app/code/Magento/Braintree/Gateway/Config/PayPal/Config.php +++ b/app/code/Magento/Braintree/Gateway/Config/PayPal/Config.php @@ -5,6 +5,9 @@ */ namespace Magento\Braintree\Gateway\Config\PayPal; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Payment\Model\CcConfig; + /** * Class Config */ @@ -22,6 +25,26 @@ class Config extends \Magento\Payment\Gateway\Config\Config const KEY_REQUIRE_BILLING_ADDRESS = 'require_billing_address'; + /** + * @var CcConfig + */ + private $ccConfig; + + /** + * @var array + */ + private $icon = []; + + public function __construct( + ScopeConfigInterface $scopeConfig, + CcConfig $ccConfig, + $methodCode = null, + $pathPattern = self::DEFAULT_PATH_PATTERN + ) { + parent::__construct($scopeConfig, $methodCode, $pathPattern); + $this->ccConfig = $ccConfig; + } + /** * Get Payment configuration status * @@ -79,4 +102,32 @@ public function getTitle() { return $this->getValue(self::KEY_TITLE); } + + /** + * Is need to skip order review + * @return bool + */ + public function isSkipOrderReview() + { + return (bool) $this->getValue('skip_order_review'); + } + + /** + * Get PayPal icon + * @return array + */ + public function getPayPalIcon() + { + if (empty($this->icon)) { + $asset = $this->ccConfig->createAsset('Magento_Braintree::images/paypal.png'); + list($width, $height) = getimagesize($asset->getSourceFile()); + $this->icon = [ + 'url' => $asset->getUrl(), + 'width' => $width, + 'height' => $height + ]; + } + + return $this->icon; + } } diff --git a/app/code/Magento/Braintree/Gateway/Helper/SubjectReader.php b/app/code/Magento/Braintree/Gateway/Helper/SubjectReader.php index c91abc49b2dd2..e5082a5df6fdd 100644 --- a/app/code/Magento/Braintree/Gateway/Helper/SubjectReader.php +++ b/app/code/Magento/Braintree/Gateway/Helper/SubjectReader.php @@ -83,7 +83,7 @@ public function readAmount(array $subject) */ public function readCustomerId(array $subject) { - if (empty($subject['customer_id'])) { + if (!isset($subject['customer_id'])) { throw new \InvalidArgumentException('The "customerId" field does not exists'); } diff --git a/app/code/Magento/Braintree/Gateway/Request/DescriptorDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/DescriptorDataBuilder.php new file mode 100644 index 0000000000000..f2c5515eac712 --- /dev/null +++ b/app/code/Magento/Braintree/Gateway/Request/DescriptorDataBuilder.php @@ -0,0 +1,44 @@ +config = $config; + } + + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function build(array $buildSubject) + { + $values = $this->config->getDynamicDescriptors(); + return !empty($values) ? [self::$descriptorKey => $values] : []; + } +} diff --git a/app/code/Magento/Braintree/Gateway/Request/PayPal/DeviceDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/PayPal/DeviceDataBuilder.php new file mode 100644 index 0000000000000..bac5dc5fa0e0a --- /dev/null +++ b/app/code/Magento/Braintree/Gateway/Request/PayPal/DeviceDataBuilder.php @@ -0,0 +1,52 @@ +subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject) + { + $result = []; + $paymentDO = $this->subjectReader->readPayment($buildSubject); + + $payment = $paymentDO->getPayment(); + $data = $payment->getAdditionalInformation(); + if (!empty($data[DataAssignObserver::DEVICE_DATA])) { + $result[self::$deviceDataKey] = $data[DataAssignObserver::DEVICE_DATA]; + } + + return $result; + } +} diff --git a/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php new file mode 100644 index 0000000000000..efffdbf5fef27 --- /dev/null +++ b/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php @@ -0,0 +1,60 @@ +subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject) + { + $result = []; + $paymentDO = $this->subjectReader->readPayment($buildSubject); + + $payment = $paymentDO->getPayment(); + $data = $payment->getAdditionalInformation(); + if (!empty($data[VaultConfigProvider::IS_ACTIVE_CODE])) { + $result[self::$optionsKey] = [ + self::$storeInVaultOnSuccess => true + ]; + } + + return $result; + } +} diff --git a/app/code/Magento/Braintree/Gateway/Request/VaultDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/VaultDataBuilder.php index 728089d0cab73..3165b67cc5c49 100644 --- a/app/code/Magento/Braintree/Gateway/Request/VaultDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/VaultDataBuilder.php @@ -18,9 +18,8 @@ class VaultDataBuilder implements BuilderInterface const OPTIONS = 'options'; /** - * The option that determines whether the shipping address information - * provided with the transaction should be associated with the customer ID specified. - * When passed, the payment method will always be stored in the Vault. + * The option that determines whether the payment method associated with + * the successful transaction should be stored in the Vault. */ const STORE_IN_VAULT_ON_SUCCESS = 'storeInVaultOnSuccess'; diff --git a/app/code/Magento/Braintree/Gateway/Response/PayPal/VaultDetailsHandler.php b/app/code/Magento/Braintree/Gateway/Response/PayPal/VaultDetailsHandler.php new file mode 100644 index 0000000000000..c43f68fc608f4 --- /dev/null +++ b/app/code/Magento/Braintree/Gateway/Response/PayPal/VaultDetailsHandler.php @@ -0,0 +1,130 @@ +paymentTokenFactory = $paymentTokenFactory; + $this->paymentExtensionFactory = $paymentExtensionFactory; + $this->subjectReader = $subjectReader; + $this->dateTimeFactory = $dateTimeFactory; + } + + /** + * @inheritdoc + */ + public function handle(array $handlingSubject, array $response) + { + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + $transaction = $this->subjectReader->readTransaction($response); + $payment = $paymentDO->getPayment(); + + // add vault payment token entity to extension attributes + $paymentToken = $this->getVaultPaymentToken($transaction); + if ($paymentToken !== null) { + $extensionAttributes = $this->getExtensionAttributes($payment); + $extensionAttributes->setVaultPaymentToken($paymentToken); + } + } + + /** + * Get vault payment token entity + * + * @param \Braintree\Transaction $transaction + * @return PaymentTokenInterface|null + */ + private function getVaultPaymentToken(Transaction $transaction) + { + // Check token existing in gateway response + $token = $transaction->paypalDetails->token; + if (empty($token)) { + return null; + } + + /** @var PaymentTokenInterface $paymentToken */ + $paymentToken = $this->paymentTokenFactory->create(); + $paymentToken->setGatewayToken($token); + $paymentToken->setExpiresAt($this->getExpirationDate()); + $details = json_encode([ + 'payerEmail' => $transaction->paypalDetails->payerEmail + ]); + $paymentToken->setTokenDetails($details); + + return $paymentToken; + } + + /** + * @return string + */ + private function getExpirationDate() + { + $expDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); + $expDate->add(new \DateInterval('P1Y')); + return $expDate->format('Y-m-d 00:00:00'); + } + + /** + * Get payment extension attributes + * @param InfoInterface $payment + * @return OrderPaymentExtensionInterface + */ + private function getExtensionAttributes(InfoInterface $payment) + { + $extensionAttributes = $payment->getExtensionAttributes(); + if ($extensionAttributes === null) { + $extensionAttributes = $this->paymentExtensionFactory->create(); + $payment->setExtensionAttributes($extensionAttributes); + } + return $extensionAttributes; + } +} diff --git a/app/code/Magento/Braintree/Gateway/Response/RiskDataHandler.php b/app/code/Magento/Braintree/Gateway/Response/RiskDataHandler.php index 46d415c97b7c6..6cadf252e7a3b 100644 --- a/app/code/Magento/Braintree/Gateway/Response/RiskDataHandler.php +++ b/app/code/Magento/Braintree/Gateway/Response/RiskDataHandler.php @@ -24,6 +24,11 @@ class RiskDataHandler implements HandlerInterface */ const RISK_DATA_DECISION = 'riskDataDecision'; + /** + * Risk data Review status + */ + private static $statusReview = 'Review'; + /** * @var SubjectReader */ @@ -62,5 +67,10 @@ public function handle(array $handlingSubject, array $response) $payment->setAdditionalInformation(self::RISK_DATA_ID, $transaction->riskData->id); $payment->setAdditionalInformation(self::RISK_DATA_DECISION, $transaction->riskData->decision); + + // mark payment as fraud + if ($transaction->riskData->decision === self::$statusReview) { + $payment->setIsFraudDetected(true); + } } } diff --git a/app/code/Magento/Braintree/Gateway/Validator/ResponseValidator.php b/app/code/Magento/Braintree/Gateway/Validator/ResponseValidator.php index d2add721c4bcf..346d43f3312e1 100644 --- a/app/code/Magento/Braintree/Gateway/Validator/ResponseValidator.php +++ b/app/code/Magento/Braintree/Gateway/Validator/ResponseValidator.php @@ -25,7 +25,8 @@ protected function getResponseValidators() [ function ($response) { return [ - isset($response->transaction) + $response instanceof Successful + && isset($response->transaction) && in_array( $response->transaction->status, [Transaction::AUTHORIZED, Transaction::SUBMITTED_FOR_SETTLEMENT, Transaction::SETTLING] diff --git a/app/code/Magento/Braintree/Helper/Country.php b/app/code/Magento/Braintree/Helper/Country.php index 3a3116b0d9ac0..e8660b8523b30 100644 --- a/app/code/Magento/Braintree/Helper/Country.php +++ b/app/code/Magento/Braintree/Helper/Country.php @@ -6,6 +6,7 @@ namespace Magento\Braintree\Helper; use Magento\Directory\Model\ResourceModel\Country\CollectionFactory; +use Magento\Braintree\Model\Adminhtml\System\Config\Country as CountryConfig; /** * Class Country @@ -13,12 +14,12 @@ class Country { /** - * @var \Magento\Directory\Model\ResourceModel\Country\CollectionFactory + * @var CollectionFactory */ private $collectionFactory; /** - * @var \Magento\Braintree\Model\Adminhtml\System\Config\Country + * @var CountryConfig */ private $countryConfig; @@ -28,13 +29,11 @@ class Country private $countries; /** - * @param \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $factory - * @param \Magento\Braintree\Model\Adminhtml\System\Config\Country $countryConfig + * @param CollectionFactory $factory + * @param CountryConfig $countryConfig */ - public function __construct( - \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $factory, - \Magento\Braintree\Model\Adminhtml\System\Config\Country $countryConfig - ) { + public function __construct(CollectionFactory $factory, CountryConfig $countryConfig) + { $this->collectionFactory = $factory; $this->countryConfig = $countryConfig; } diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php index ef6bfaccecdb6..4cfbf0f3a833f 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php @@ -5,14 +5,13 @@ */ namespace Magento\Braintree\Model\Paypal\Helper; +use Magento\Braintree\Gateway\Config\PayPal\Config; +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; +use Magento\Braintree\Observer\DataAssignObserver; +use Magento\Framework\Exception\LocalizedException; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address; -use Magento\Quote\Model\Quote\Payment; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Braintree\Model\Ui\ConfigProvider; -use Magento\Framework\Exception\LocalizedException; -use Magento\Braintree\Observer\DataAssignObserver; -use Magento\Braintree\Gateway\Config\PayPal\Config; /** * Class QuoteUpdater diff --git a/app/code/Magento/Braintree/Model/Report/Row/TransactionMap.php b/app/code/Magento/Braintree/Model/Report/Row/TransactionMap.php index 0abae8af89fbc..c914b21893e25 100644 --- a/app/code/Magento/Braintree/Model/Report/Row/TransactionMap.php +++ b/app/code/Magento/Braintree/Model/Report/Row/TransactionMap.php @@ -115,9 +115,13 @@ public function setCustomAttribute($attributeCode, $attributeValue) */ public function getCustomAttributes() { + $shouldBeLocalized = ['paymentInstrumentType', 'type', 'status']; $output = []; foreach ($this->getMappedValues() as $key => $value) { $attribute = $this->attributeValueFactory->create(); + if(in_array($key, $shouldBeLocalized)) { + $value = __($value); + } $output[] = $attribute->setAttributeCode($key)->setValue($value); } return $output; diff --git a/app/code/Magento/Braintree/Model/Report/TransactionsCollection.php b/app/code/Magento/Braintree/Model/Report/TransactionsCollection.php index f52f19c0ab6f0..7b177cff3af1d 100644 --- a/app/code/Magento/Braintree/Model/Report/TransactionsCollection.php +++ b/app/code/Magento/Braintree/Model/Report/TransactionsCollection.php @@ -6,6 +6,7 @@ namespace Magento\Braintree\Model\Report; use Magento\Braintree\Model\Adapter\BraintreeAdapter; +use Magento\Braintree\Model\Report\Row\TransactionMap; use Magento\Framework\Api\Search\SearchResultInterface; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Data\Collection; @@ -26,7 +27,7 @@ class TransactionsCollection extends Collection implements SearchResultInterface * * @var string */ - protected $_itemObjectClass = 'Magento\Braintree\Model\Report\Row\TransactionMap'; + protected $_itemObjectClass = TransactionMap::class; /** * @var array diff --git a/app/code/Magento/Braintree/Model/Ui/Adminhtml/PayPal/TokenUiComponentProvider.php b/app/code/Magento/Braintree/Model/Ui/Adminhtml/PayPal/TokenUiComponentProvider.php new file mode 100644 index 0000000000000..fd94e18e2cd94 --- /dev/null +++ b/app/code/Magento/Braintree/Model/Ui/Adminhtml/PayPal/TokenUiComponentProvider.php @@ -0,0 +1,84 @@ +componentFactory = $componentFactory; + $this->urlBuilder = $urlBuilder; + $this->config = $config; + } + + /** + * @inheritdoc + */ + public function getComponentForToken(PaymentTokenInterface $paymentToken) + { + $data = json_decode($paymentToken->getTokenDetails() ?: '{}', true); + $data['icon'] = $this->config->getPayPalIcon(); + $component = $this->componentFactory->create( + [ + 'config' => [ + 'code' => PayPalConfigProvider::PAYPAL_VAULT_CODE, + 'nonceUrl' => $this->getNonceRetrieveUrl(), + TokenUiComponentProviderInterface::COMPONENT_DETAILS => $data, + TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH => $paymentToken->getPublicHash(), + 'template' => 'Magento_Braintree::form/paypal/vault.phtml' + ], + 'name' => Template::class + ] + ); + + return $component; + } + + /** + * Get url to retrieve payment method nonce + * @return string + */ + private function getNonceRetrieveUrl() + { + return $this->urlBuilder->getUrl(ConfigProvider::CODE . '/payment/getnonce', ['_secure' => true]); + } +} diff --git a/app/code/Magento/Braintree/Model/Ui/Adminhtml/TokenUiComponentProvider.php b/app/code/Magento/Braintree/Model/Ui/Adminhtml/TokenUiComponentProvider.php index 6cfc96ea23d0d..420b8365b3ea4 100644 --- a/app/code/Magento/Braintree/Model/Ui/Adminhtml/TokenUiComponentProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/Adminhtml/TokenUiComponentProvider.php @@ -49,6 +49,7 @@ public function getComponentForToken(PaymentTokenInterface $paymentToken) $component = $this->componentFactory->create( [ 'config' => [ + 'code' => ConfigProvider::CC_VAULT_CODE, 'nonceUrl' => $this->getNonceRetrieveUrl(), TokenUiComponentProviderInterface::COMPONENT_DETAILS => $data, TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH => $paymentToken->getPublicHash(), diff --git a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php index 3a7a773c33c46..9588c197b411a 100644 --- a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php @@ -19,25 +19,18 @@ final class ConfigProvider implements ConfigProviderInterface { const CODE = 'braintree'; + /** + * @deprecated + */ const PAYPAL_CODE = 'braintree_paypal'; const CC_VAULT_CODE = 'braintree_cc_vault'; - /** - * @var ResolverInterface - */ - private $localeResolver; - /** * @var Config */ private $config; - /** - * @var PayPalConfig - */ - private $payPalConfig; - /** * @var BraintreeAdapter */ @@ -52,9 +45,10 @@ final class ConfigProvider implements ConfigProviderInterface * Constructor * * @param Config $config - * @param PayPalConfig $payPalConfig; + * @param PayPalConfig $payPalConfig No longer used by internal code and not recommended. * @param BraintreeAdapter $adapter - * @param ResolverInterface $localeResolver + * @param ResolverInterface $localeResolver No longer used by internal code and not recommended. + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( Config $config, @@ -63,9 +57,7 @@ public function __construct( ResolverInterface $localeResolver ) { $this->config = $config; - $this->payPalConfig = $payPalConfig; $this->adapter = $adapter; - $this->localeResolver = $localeResolver; } /** @@ -75,7 +67,6 @@ public function __construct( */ public function getConfig() { - $isPayPalActive = $this->payPalConfig->isActive(); return [ 'payment' => [ self::CODE => [ @@ -90,22 +81,13 @@ public function getConfig() 'kountMerchantId' => $this->config->getKountMerchantId(), 'hasFraudProtection' => $this->config->hasFraudProtection(), 'merchantId' => $this->config->getMerchantId(), - 'ccVaultCode' => static::CC_VAULT_CODE + 'ccVaultCode' => self::CC_VAULT_CODE ], Config::CODE_3DSECURE => [ 'enabled' => $this->config->isVerify3DSecure(), 'thresholdAmount' => $this->config->getThresholdAmount(), 'specificCountries' => $this->config->get3DSecureSpecificCountries() ], - self::PAYPAL_CODE => [ - 'isActive' => $isPayPalActive, - 'title' => $this->payPalConfig->getTitle(), - 'isAllowShippingAddressOverride' => $this->payPalConfig->isAllowToEditShippingAddress(), - 'merchantName' => $this->payPalConfig->getMerchantName(), - 'locale' => strtolower($this->localeResolver->getLocale()), - 'paymentAcceptanceMarkSrc' => - 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-medium.png', - ] ] ]; } diff --git a/app/code/Magento/Braintree/Model/Ui/PayPal/ConfigProvider.php b/app/code/Magento/Braintree/Model/Ui/PayPal/ConfigProvider.php new file mode 100644 index 0000000000000..65fb5370181db --- /dev/null +++ b/app/code/Magento/Braintree/Model/Ui/PayPal/ConfigProvider.php @@ -0,0 +1,56 @@ +config = $config; + $this->resolver = $resolver; + } + + public function getConfig() + { + return [ + 'payment' => [ + self::PAYPAL_CODE => [ + 'isActive' => $this->config->isActive(), + 'title' => $this->config->getTitle(), + 'isAllowShippingAddressOverride' => $this->config->isAllowToEditShippingAddress(), + 'merchantName' => $this->config->getMerchantName(), + 'locale' => strtolower($this->resolver->getLocale()), + 'paymentAcceptanceMarkSrc' => + 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-medium.png', + 'vaultCode' => self::PAYPAL_VAULT_CODE, + 'skipOrderReview' => $this->config->isSkipOrderReview(), + 'paymentIcon' => $this->config->getPayPalIcon(), + ] + ] + ]; + } +} diff --git a/app/code/Magento/Braintree/Model/Ui/PayPal/TokenUiComponentProvider.php b/app/code/Magento/Braintree/Model/Ui/PayPal/TokenUiComponentProvider.php new file mode 100644 index 0000000000000..2f3e3db75cc34 --- /dev/null +++ b/app/code/Magento/Braintree/Model/Ui/PayPal/TokenUiComponentProvider.php @@ -0,0 +1,73 @@ +componentFactory = $componentFactory; + $this->urlBuilder = $urlBuilder; + } + + /** + * Get UI component for token + * @param PaymentTokenInterface $paymentToken + * @return TokenUiComponentInterface + */ + public function getComponentForToken(PaymentTokenInterface $paymentToken) + { + $jsonDetails = json_decode($paymentToken->getTokenDetails() ?: '{}', true); + $component = $this->componentFactory->create( + [ + 'config' => [ + 'code' => ConfigProvider::PAYPAL_VAULT_CODE, + 'nonceUrl' => $this->getNonceRetrieveUrl(), + TokenUiComponentProviderInterface::COMPONENT_DETAILS => $jsonDetails, + TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH => $paymentToken->getPublicHash() + ], + 'name' => 'Magento_Braintree/js/view/payment/method-renderer/paypal-vault' + ] + ); + + return $component; + } + + /** + * Get url to retrieve payment method nonce + * @return string + */ + private function getNonceRetrieveUrl() + { + return $this->urlBuilder->getUrl(CommonConfigProvider::CODE . '/payment/getnonce', ['_secure' => true]); + } +} diff --git a/app/code/Magento/Braintree/Observer/AddPaypalShortcuts.php b/app/code/Magento/Braintree/Observer/AddPaypalShortcuts.php index bbf4b91ef474f..e5be5c2bab43c 100644 --- a/app/code/Magento/Braintree/Observer/AddPaypalShortcuts.php +++ b/app/code/Magento/Braintree/Observer/AddPaypalShortcuts.php @@ -5,8 +5,9 @@ */ namespace Magento\Braintree\Observer; -use Magento\Framework\Event\Observer; +use Magento\Braintree\Block\Paypal\Button; use Magento\Catalog\Block\ShortcutButtons; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; /** @@ -17,7 +18,7 @@ class AddPaypalShortcuts implements ObserverInterface /** * Block class */ - const PAYPAL_SHORTCUT_BLOCK = 'Magento\Braintree\Block\Paypal\Button'; + const PAYPAL_SHORTCUT_BLOCK = Button::class; /** * Add Braintree PayPal shortcut buttons diff --git a/app/code/Magento/Braintree/Test/Unit/Block/FormTest.php b/app/code/Magento/Braintree/Test/Unit/Block/FormTest.php index f056ba0cefd36..e6c7ce59d0e29 100644 --- a/app/code/Magento/Braintree/Test/Unit/Block/FormTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Block/FormTest.php @@ -16,7 +16,6 @@ use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Vault\Model\VaultPaymentInterface; -use OAuthTest\Mocks\Common\Service\Mock; use PHPUnit_Framework_MockObject_MockObject as MockObject; /** diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php index fda3e85d167c2..9665afa1f3655 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php @@ -374,7 +374,7 @@ private function buildSearchCriteria() ->willReturnSelf(); $searchCriteria = new SearchCriteria(); - $this->searchCriteriaBuilder->expects(static::once()) + $this->searchCriteriaBuilder->expects(static::exactly(2)) ->method('addFilters') ->willReturnSelf(); $this->searchCriteriaBuilder->expects(static::once()) diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.php index 66f6c7dd78e79..e4dd13985fa9a 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.php @@ -271,6 +271,68 @@ public function threeDSecureSpecificCountriesDataProvider() ]; } + /** + * @covers \Magento\Braintree\Gateway\Config\Config::getDynamicDescriptors + * @param $name + * @param $phone + * @param $url + * @param array $expected + * @dataProvider descriptorsDataProvider + */ + public function testGetDynamicDescriptors($name, $phone, $url, array $expected) + { + $this->scopeConfigMock->expects(static::at(0)) + ->method('getValue') + ->with($this->getPath('descriptor_name'), ScopeInterface::SCOPE_STORE, null) + ->willReturn($name); + $this->scopeConfigMock->expects(static::at(1)) + ->method('getValue') + ->with($this->getPath('descriptor_phone'), ScopeInterface::SCOPE_STORE, null) + ->willReturn($phone); + $this->scopeConfigMock->expects(static::at(2)) + ->method('getValue') + ->with($this->getPath('descriptor_url'), ScopeInterface::SCOPE_STORE, null) + ->willReturn($url); + + $actual = $this->model->getDynamicDescriptors(); + static::assertEquals($expected, $actual); + } + + /** + * Get variations to test dynamic descriptors + * @return array + */ + public function descriptorsDataProvider() + { + $name = 'company * product'; + $phone = '333-22-22-333'; + $url = 'https://test.url.mage.com'; + return [ + [ + $name, $phone, $url, + 'expected' => [ + 'name' => $name, 'phone' => $phone, 'url' => $url + ] + ], + [ + $name, null, null, + 'expected' => [ + 'name' => $name + ] + ], + [ + null, null, $url, + 'expected' => [ + 'url' => $url + ] + ], + [ + null, null, null, + 'expected' => [] + ] + ]; + } + /** * Return config path * diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/DescriptorDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/DescriptorDataBuilderTest.php new file mode 100644 index 0000000000000..ffee329491795 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/DescriptorDataBuilderTest.php @@ -0,0 +1,105 @@ +config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->setMethods(['getDynamicDescriptors']) + ->getMock(); + + $this->builder = new DescriptorDataBuilder($this->config); + } + + /** + * @covers \Magento\Braintree\Gateway\Request\DescriptorDataBuilder::build + * @param array $descriptors + * @param array $expected + * @dataProvider buildDataProvider + */ + public function testBuild(array $descriptors, array $expected) + { + $this->config->expects(static::once()) + ->method('getDynamicDescriptors') + ->willReturn($descriptors); + + $actual = $this->builder->build([]); + static::assertEquals($expected, $actual); + } + + /** + * Get variations for build method testing + * @return array + */ + public function buildDataProvider() + { + $name = 'company * product'; + $phone = '333-22-22-333'; + $url = 'https://test.url.mage.com'; + return [ + [ + 'descriptors' => [ + 'name' => $name, + 'phone' => $phone, + 'url' => $url + ], + 'expected' => [ + 'descriptor' => [ + 'name' => $name, + 'phone' => $phone, + 'url' => $url + ] + ] + ], + [ + 'descriptors' => [ + 'name' => $name, + 'phone' => $phone + ], + 'expected' => [ + 'descriptor' => [ + 'name' => $name, + 'phone' => $phone + ] + ] + ], + [ + 'descriptors' => [ + 'name' => $name + ], + 'expected' => [ + 'descriptor' => [ + 'name' => $name + ] + ] + ], + [ + 'descriptors' => [], + 'expected' => [] + ] + ]; + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/KountPaymentDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/KountPaymentDataBuilderTest.php index c695ad24376b3..f1485fe192f23 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/KountPaymentDataBuilderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/KountPaymentDataBuilderTest.php @@ -84,7 +84,7 @@ public function testBuildReadPaymentException() public function testBuild() { $additionalData = [ - DataAssignObserver::DEVICE_DATA => self::DEVICE_DATA + DataAssignObserver::DEVICE_DATA => self::DEVICE_DATA ]; $expectedResult = [ diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PayPal/DeviceDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PayPal/DeviceDataBuilderTest.php new file mode 100644 index 0000000000000..268f1d6251109 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PayPal/DeviceDataBuilderTest.php @@ -0,0 +1,115 @@ +subjectReader = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->setMethods(['readPayment']) + ->getMock(); + + $this->paymentDataObject = $this->getMock(PaymentDataObjectInterface::class); + + $this->paymentInfo = $this->getMock(InfoInterface::class); + + $this->builder = new DeviceDataBuilder($this->subjectReader); + } + + /** + * @covers \Magento\Braintree\Gateway\Request\PayPal\DeviceDataBuilder::build + * @param array $paymentData + * @param array $expected + * @dataProvider buildDataProvider + */ + public function testBuild(array $paymentData, array $expected) + { + $subject = [ + 'payment' => $this->paymentDataObject + ]; + + $this->subjectReader->expects(static::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($this->paymentDataObject); + + $this->paymentDataObject->expects(static::once()) + ->method('getPayment') + ->willReturn($this->paymentInfo); + + $this->paymentInfo->expects(static::once()) + ->method('getAdditionalInformation') + ->willReturn($paymentData); + + $actual = $this->builder->build($subject); + static::assertEquals($expected, $actual); + } + + /** + * Get variations for build method testing + * @return array + */ + public function buildDataProvider() + { + return [ + [ + 'paymentData' => [ + 'device_data' => '{correlation_id: 12s3jf9as}' + ], + 'expected' => [ + 'deviceData' => '{correlation_id: 12s3jf9as}' + ] + ], + [ + 'paymentData' => [ + 'device_data' => null, + ], + 'expected' => [] + ], + [ + 'paymentData' => [ + 'deviceData' => '{correlation_id: 12s3jf9as}', + ], + 'expected' => [] + ], + [ + 'paymentData' => [], + 'expected' => [] + ] + ]; + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PayPal/VaultDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PayPal/VaultDataBuilderTest.php new file mode 100644 index 0000000000000..bee9ae2e06de1 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PayPal/VaultDataBuilderTest.php @@ -0,0 +1,112 @@ +paymentDataObject = $this->getMock(PaymentDataObjectInterface::class); + + $this->paymentInfo = $this->getMock(InfoInterface::class); + + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->setMethods(['readPayment']) + ->getMock(); + + $this->builder = new VaultDataBuilder($this->subjectReader); + } + + /** + * @covers \Magento\Braintree\Gateway\Request\PayPal\VaultDataBuilder::build + * @param array $additionalInfo + * @param array $expected + * @dataProvider buildDataProvider + */ + public function testBuild(array $additionalInfo, array $expected) + { + $subject = [ + 'payment' => $this->paymentDataObject + ]; + + $this->subjectReader->expects(static::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($this->paymentDataObject); + + $this->paymentDataObject->expects(static::once()) + ->method('getPayment') + ->willReturn($this->paymentInfo); + + $this->paymentInfo->expects(static::once()) + ->method('getAdditionalInformation') + ->willReturn($additionalInfo); + + $actual = $this->builder->build($subject); + static::assertEquals($expected, $actual); + } + + /** + * Get variations to test build method + * @return array + */ + public function buildDataProvider() + { + return [ + [ + 'additionalInfo' => [ + VaultConfigProvider::IS_ACTIVE_CODE => true + ], + 'expected' => [ + 'options' => [ + 'storeInVaultOnSuccess' => true + ] + ] + ], + [ + 'additionalInfo' => [ + VaultConfigProvider::IS_ACTIVE_CODE => false + ], + 'expected' => [] + ], + [ + 'additionalInfo' => [], + 'expected' => [] + ], + ]; + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php index b5df7f312ec61..165bc94aa6842 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php @@ -5,14 +5,12 @@ */ namespace Magento\Braintree\Test\Unit\Gateway\Request; -use Magento\Braintree\Gateway\Config\Config; +use Magento\Braintree\Gateway\Helper\SubjectReader; use Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder; -use Magento\Braintree\Observer\DataAssignObserver; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; +use Magento\Sales\Api\Data\OrderPaymentExtension; use Magento\Sales\Model\Order\Payment; -use Magento\Braintree\Gateway\Helper\SubjectReader; use Magento\Vault\Model\PaymentToken; -use Magento\Sales\Api\Data\OrderPaymentExtension; class VaultCaptureDataBuilderTest extends \PHPUnit_Framework_TestCase { @@ -82,7 +80,7 @@ public function testBuild() $paymentExtension = $this->getMockBuilder(OrderPaymentExtension::class) ->setMethods(['getVaultPaymentToken']) ->disableOriginalConstructor() - ->getMock(); + ->getMockForAbstractClass(); $paymentToken = $this->getMockBuilder(PaymentToken::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php new file mode 100644 index 0000000000000..be2239151a2db --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php @@ -0,0 +1,252 @@ +paymentDataObject = $this->getMockForAbstractClass(PaymentDataObjectInterface::class); + + $this->paymentInfo = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods(['__wakeup']) + ->getMock(); + + $this->paymentToken = $objectManager->getObject(PaymentToken::class); + + $this->paymentTokenFactory = $this->getMockBuilder(AccountPaymentTokenFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $this->paymentExtension = $this->getMockBuilder(OrderPaymentExtensionInterface::class) + ->setMethods(['setVaultPaymentToken', 'getVaultPaymentToken']) + ->disableOriginalConstructor() + ->getMock(); + $this->paymentExtensionFactory = $this->getMockBuilder(OrderPaymentExtensionInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->subject = [ + 'payment' => $this->paymentDataObject, + ]; + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->setMethods(['readPayment', 'readTransaction']) + ->getMock(); + $this->subjectReader->expects(static::once()) + ->method('readPayment') + ->with($this->subject) + ->willReturn($this->paymentDataObject); + + $this->dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->handler = new VaultDetailsHandler( + $this->paymentTokenFactory, + $this->paymentExtensionFactory, + $this->subjectReader, + $this->dateTimeFactory + ); + } + + /** + * @covers \Magento\Braintree\Gateway\Response\PayPal\VaultDetailsHandler::handle + */ + public function testHandle() + { + /** @var Transaction $transaction */ + $transaction = $this->getTransaction(); + $response = [ + 'object' => $transaction + ]; + + $this->paymentExtension->expects(static::once()) + ->method('setVaultPaymentToken') + ->with($this->paymentToken); + $this->paymentExtension->expects(static::once()) + ->method('getVaultPaymentToken') + ->willReturn($this->paymentToken); + + $this->subjectReader->expects(static::once()) + ->method('readTransaction') + ->with($response) + ->willReturn($transaction); + + $this->paymentDataObject->expects(static::once()) + ->method('getPayment') + ->willReturn($this->paymentInfo); + + $this->paymentTokenFactory->expects(static::once()) + ->method('create') + ->willReturn($this->paymentToken); + + $this->paymentExtensionFactory->expects(static::once()) + ->method('create') + ->willReturn($this->paymentExtension); + + $dateTime = new \DateTime('2016-07-05 00:00:00', new \DateTimeZone('UTC')); + $expirationDate = '2017-07-05 00:00:00'; + $this->dateTimeFactory->expects(static::once()) + ->method('create') + ->willReturn($dateTime); + + $this->handler->handle($this->subject, $response); + + $extensionAttributes = $this->paymentInfo->getExtensionAttributes(); + /** @var PaymentTokenInterface $paymentToken */ + $paymentToken = $extensionAttributes->getVaultPaymentToken(); + static::assertNotNull($paymentToken); + + $tokenDetails = json_decode($paymentToken->getTokenDetails(), true); + + static::assertSame($this->paymentToken, $paymentToken); + static::assertEquals($transaction->paypalDetails->token, $paymentToken->getGatewayToken()); + static::assertEquals($transaction->paypalDetails->payerEmail, $tokenDetails['payerEmail']); + static::assertEquals($expirationDate, $paymentToken->getExpiresAt()); + } + + /** + * @covers \Magento\Braintree\Gateway\Response\PayPal\VaultDetailsHandler::handle + */ + public function testHandleWithoutToken() + { + $transaction = $this->getTransaction(); + $transaction->paypalDetails->token = null; + + $response = [ + 'object' => $transaction + ]; + + $this->subjectReader->expects(static::once()) + ->method('readTransaction') + ->with($response) + ->willReturn($transaction); + + $this->paymentDataObject->expects(static::once()) + ->method('getPayment') + ->willReturn($this->paymentInfo); + + $this->paymentTokenFactory->expects(static::never()) + ->method('create'); + + $this->dateTimeFactory->expects(static::never()) + ->method('create'); + + $this->handler->handle($this->subject, $response); + static::assertNull($this->paymentInfo->getExtensionAttributes()); + } + + /** + * Create Braintree transaction + * @return Transaction + */ + private function getTransaction() + { + $attributes = [ + 'id' => self::$transactionId, + 'paypalDetails' => $this->getPayPalDetails() + ]; + + $transaction = Transaction::factory($attributes); + + return $transaction; + } + + /** + * Get PayPal transaction details + * @return PayPalDetails + */ + private function getPayPalDetails() + { + $attributes = [ + 'token' => 'rc39al', + 'payerEmail' => 'john.doe@example.com' + ]; + + $details = new PayPalDetails($attributes); + + return $details; + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/RiskDataHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/RiskDataHandlerTest.php index 239524e04c0e0..2baf3356a0008 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/RiskDataHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/RiskDataHandlerTest.php @@ -5,12 +5,12 @@ */ namespace Magento\Braintree\Test\Unit\Gateway\Response; -use Braintree\RiskData; use Braintree\Transaction; -use Magento\Sales\Model\Order\Payment; use Magento\Braintree\Gateway\Helper\SubjectReader; use Magento\Braintree\Gateway\Response\RiskDataHandler; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; +use Magento\Sales\Model\Order\Payment; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class RiskDataHandlerTest @@ -25,99 +25,95 @@ class RiskDataHandlerTest extends \PHPUnit_Framework_TestCase private $riskDataHandler; /** - * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject + * @var SubjectReader|MockObject */ - private $subjectReaderMock; + private $subjectReader; /** * Set up */ protected function setUp() { - $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) ->disableOriginalConstructor() + ->setMethods(['readPayment', 'readTransaction']) ->getMock(); - $this->riskDataHandler = new RiskDataHandler($this->subjectReaderMock); + $this->riskDataHandler = new RiskDataHandler($this->subjectReader); } /** - * Run test for handle method + * Test for handle method + * @covers \Magento\Braintree\Gateway\Response\RiskDataHandler::handle + * @param string $riskDecision + * @param boolean $isFraud + * @dataProvider riskDataProvider */ - public function testHandle() + public function testHandle($riskDecision, $isFraud) { - $paymentData = $this->getPaymentDataObjectMock(); - $transaction = $this->getBraintreeTransactionMock(); + /** @var Payment|MockObject $payment */ + $payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods(['setAdditionalInformation', 'setIsFraudDetected']) + ->getMock(); + /** @var PaymentDataObjectInterface|MockObject $paymentDO */ + $paymentDO = $this->getMock(PaymentDataObjectInterface::class); + $paymentDO->expects(self::once()) + ->method('getPayment') + ->willReturn($payment); + + $transaction = Transaction::factory([ + 'riskData' => [ + 'id' => 'test-id', + 'decision' => $riskDecision + ] + ]); $response = [ 'object' => $transaction ]; $handlingSubject = [ - 'payment' =>$paymentData, + 'payment' => $paymentDO, ]; - $this->subjectReaderMock->expects(self::once()) + $this->subjectReader->expects(static::once()) ->method('readPayment') ->with($handlingSubject) - ->willReturn($paymentData); - $this->subjectReaderMock->expects(self::once()) + ->willReturn($paymentDO); + $this->subjectReader->expects(static::once()) ->method('readTransaction') ->with($response) ->willReturn($transaction); - $this->riskDataHandler->handle($handlingSubject, $response); - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getBraintreeTransactionMock() - { - $transaction = \Braintree\Transaction::factory([]); - $transaction->_set( - 'riskData', - RiskData::factory( - [ - 'id' => 'test-id', - 'decision' => 'test-decision', - ] - ) - ); - - return $transaction; - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getPaymentDataObjectMock() - { - $mock = $this->getMockBuilder(PaymentDataObjectInterface::class) - ->getMockForAbstractClass(); + $payment->expects(static::at(0)) + ->method('setAdditionalInformation') + ->with(RiskDataHandler::RISK_DATA_ID, 'test-id'); + $payment->expects(static::at(1)) + ->method('setAdditionalInformation') + ->with(RiskDataHandler::RISK_DATA_DECISION, $riskDecision); - $mock->expects(static::once()) - ->method('getPayment') - ->willReturn($this->getPaymentMock()); + if (!$isFraud) { + $payment->expects(static::never()) + ->method('setIsFraudDetected'); + } else { + $payment->expects(static::once()) + ->method('setIsFraudDetected') + ->with(true); + } - return $mock; + $this->riskDataHandler->handle($handlingSubject, $response); } /** - * @return \PHPUnit_Framework_MockObject_MockObject + * Get list of variations to test fraud + * @return array */ - private function getPaymentMock() + public function riskDataProvider() { - $paymentMock = $this->getMockBuilder(Payment::class) - ->disableOriginalConstructor() - ->getMock(); - - $paymentMock->expects(self::at(0)) - ->method('setAdditionalInformation') - ->with(RiskDataHandler::RISK_DATA_ID, 'test-id'); - $paymentMock->expects(self::at(1)) - ->method('setAdditionalInformation') - ->with(RiskDataHandler::RISK_DATA_DECISION, 'test-decision'); - - return $paymentMock; + return [ + ['decision' => 'Not Evaluated', 'isFraud' => false], + ['decision' => 'Approve', 'isFraud' => false], + ['decision' => 'Review', 'isFraud' => true], + ]; } } diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php index 57b625a4488ce..4f641bbb56765 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php @@ -7,18 +7,18 @@ use Braintree\Transaction; use Braintree\Transaction\CreditCardDetails; +use Magento\Braintree\Gateway\Config\Config; +use Magento\Braintree\Gateway\Helper\SubjectReader; use Magento\Braintree\Gateway\Response\VaultDetailsHandler; use Magento\Framework\DataObject; use Magento\Payment\Gateway\Data\PaymentDataObject; -use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; use Magento\Sales\Api\Data\OrderPaymentExtensionInterface; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; use Magento\Vault\Api\Data\PaymentTokenInterface; -use Magento\Vault\Api\Data\PaymentTokenInterfaceFactory; -use Magento\Braintree\Gateway\Helper\SubjectReader; +use Magento\Vault\Model\CreditCardTokenFactory; use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Magento\Braintree\Gateway\Config\Config; /** * VaultDetailsHandler Test @@ -40,7 +40,7 @@ class VaultDetailsHandlerTest extends \PHPUnit_Framework_TestCase private $payment; /** - * @var \Magento\Vault\Api\Data\PaymentTokenInterfaceFactory|MockObject + * @var CreditCardTokenFactory|MockObject */ private $paymentTokenFactory; @@ -72,7 +72,7 @@ class VaultDetailsHandlerTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->paymentToken = $this->getMock(PaymentTokenInterface::class); - $this->paymentTokenFactory = $this->getMockBuilder(PaymentTokenInterfaceFactory::class) + $this->paymentTokenFactory = $this->getMockBuilder(CreditCardTokenFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php index 469cfea9c5fbb..8f57cd0e6f575 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php @@ -37,7 +37,7 @@ class GeneralResponseValidatorTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->resultInterfaceFactoryMock = $this->getMockBuilder( - 'Magento\Payment\Gateway\Validator\ResultInterfaceFactory' + \Magento\Payment\Gateway\Validator\ResultInterfaceFactory::class )->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ResponseValidatorTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ResponseValidatorTest.php index 68fddf24bfd7d..c486783be6bca 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ResponseValidatorTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ResponseValidatorTest.php @@ -11,6 +11,9 @@ use Magento\Payment\Gateway\Validator\ResultInterfaceFactory; use Magento\Braintree\Gateway\Validator\ResponseValidator; use Magento\Braintree\Gateway\Helper\SubjectReader; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Braintree\Result\Error; +use Braintree\Result\Successful; /** * Class ResponseValidatorTest @@ -23,14 +26,14 @@ class ResponseValidatorTest extends \PHPUnit_Framework_TestCase private $responseValidator; /** - * @var ResultInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ResultInterfaceFactory|MockObject */ - private $resultInterfaceFactoryMock; + private $resultInterfaceFactory; /** - * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject + * @var SubjectReader|MockObject */ - private $subjectReaderMock; + private $subjectReader; /** * Set up @@ -39,18 +42,17 @@ class ResponseValidatorTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $this->resultInterfaceFactoryMock = $this->getMockBuilder( - 'Magento\Payment\Gateway\Validator\ResultInterfaceFactory' - )->disableOriginalConstructor() + $this->resultInterfaceFactory = $this->getMockBuilder(ResultInterfaceFactory::class) + ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) ->disableOriginalConstructor() ->getMock(); $this->responseValidator = new ResponseValidator( - $this->resultInterfaceFactoryMock, - $this->subjectReaderMock + $this->resultInterfaceFactory, + $this->subjectReader ); } @@ -63,7 +65,7 @@ public function testValidateReadResponseException() 'response' => null ]; - $this->subjectReaderMock->expects(self::once()) + $this->subjectReader->expects(self::once()) ->method('readResponseObject') ->with($validationSubject) ->willThrowException(new \InvalidArgumentException()); @@ -80,7 +82,7 @@ public function testValidateReadResponseObjectException() 'response' => ['object' => null] ]; - $this->subjectReaderMock->expects(self::once()) + $this->subjectReader->expects(self::once()) ->method('readResponseObject') ->with($validationSubject) ->willThrowException(new \InvalidArgumentException()); @@ -100,25 +102,25 @@ public function testValidateReadResponseObjectException() */ public function testValidate(array $validationSubject, $isValid, $messages) { - /** @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject $resultMock */ - $resultMock = $this->getMock(ResultInterface::class); + /** @var ResultInterface|MockObject $result */ + $result = $this->getMock(ResultInterface::class); - $this->subjectReaderMock->expects(self::once()) + $this->subjectReader->expects(self::once()) ->method('readResponseObject') ->with($validationSubject) ->willReturn($validationSubject['response']['object']); - $this->resultInterfaceFactoryMock->expects(self::once()) + $this->resultInterfaceFactory->expects(self::once()) ->method('create') ->with([ 'isValid' => $isValid, 'failsDescription' => $messages ]) - ->willReturn($resultMock); + ->willReturn($result); - $actualMock = $this->responseValidator->validate($validationSubject); + $actual = $this->responseValidator->validate($validationSubject); - self::assertEquals($resultMock, $actualMock); + self::assertEquals($result, $actual); } /** @@ -126,19 +128,21 @@ public function testValidate(array $validationSubject, $isValid, $messages) */ public function dataProviderTestValidate() { - $successTrue = new \stdClass(); + $successTrue = new Successful(); $successTrue->success = true; $successTrue->transaction = new \stdClass(); $successTrue->transaction->status = Transaction::AUTHORIZED; - $successFalse = new \stdClass(); + $successFalse = new Successful(); $successFalse->success = false; - $transactionDeclined = new \stdClass(); + $transactionDeclined = new Successful(); $transactionDeclined->success = true; $transactionDeclined->transaction = new \stdClass(); $transactionDeclined->transaction->status = Transaction::SETTLEMENT_DECLINED; + $errorResult = new Error(['errors' => []]); + return [ [ 'validationSubject' => [ @@ -171,6 +175,18 @@ public function dataProviderTestValidate() [ __('Wrong transaction status') ] + ], + [ + 'validationSubject' => [ + 'response' => [ + 'object' => $errorResult, + ] + ], + 'isValid' => false, + [ + __('Braintree error response.'), + __('Wrong transaction status') + ] ] ]; } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php index 32767d469f199..c2413517fe06f 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php @@ -9,7 +9,7 @@ use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\Quote\Payment; use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Braintree\Model\Ui\ConfigProvider; +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; use Magento\Braintree\Observer\DataAssignObserver; use Magento\Braintree\Gateway\Config\PayPal\Config; use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater; diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionMapTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionMapTest.php index c44a67b2c61d9..34c607c88784d 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionMapTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionMapTest.php @@ -11,6 +11,8 @@ use Magento\Braintree\Model\Report\Row\TransactionMap; use Magento\Framework\Api\AttributeValue; use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Phrase; +use Magento\Framework\Phrase\RendererInterface; use Magento\Store\Model\StoreManagerInterface; /** @@ -30,6 +32,16 @@ class TransactionMapTest extends \PHPUnit_Framework_TestCase */ private $attributeValueFactoryMock; + /** + * @var RendererInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $defaultRenderer; + + /** + * @var RendererInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $rendererMock; + /** * Setup */ @@ -39,6 +51,9 @@ protected function setUp() ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); + $this->defaultRenderer = Phrase::getRenderer(); + $this->rendererMock = $this->getMockBuilder(RendererInterface::class) + ->getMock(); } /** @@ -65,6 +80,8 @@ public function testGetCustomAttributes($transaction) $this->transactionStub ); + Phrase::setRenderer($this->rendererMock); + /** @var AttributeValue[] $result */ $result = $map->getCustomAttributes(); @@ -77,6 +94,31 @@ public function testGetCustomAttributes($transaction) $result[6]->getValue() ); $this->assertEquals(implode(', ', $transaction['refundIds']), $result[11]->getValue()); + $this->assertEquals($transaction['merchantAccountId'], $result[1]->getValue()); + $this->assertEquals($transaction['orderId'], $result[2]->getValue()); + $this->assertEquals($transaction['amount'], $result[7]->getValue()); + $this->assertEquals($transaction['processorSettlementResponseCode'], $result[8]->getValue()); + $this->assertEquals($transaction['processorSettlementResponseText'], $result[10]->getValue()); + $this->assertEquals($transaction['settlementBatchId'], $result[12]->getValue()); + $this->assertEquals($transaction['currencyIsoCode'], $result[13]->getValue()); + + $this->rendererMock->expects($this->at(0)) + ->method('render') + ->with([$transaction['paymentInstrumentType']]) + ->willReturn('Credit card'); + $this->assertEquals('Credit card', $result[3]->getValue()->render()); + + $this->rendererMock->expects($this->at(0)) + ->method('render') + ->with([$transaction['type']]) + ->willReturn('Sale'); + $this->assertEquals('Sale', $result[5]->getValue()->render()); + + $this->rendererMock->expects($this->at(0)) + ->method('render') + ->with([$transaction['status']]) + ->willReturn('Pending for settlement'); + $this->assertEquals('Pending for settlement', $result[9]->getValue()->render()); } /** @@ -90,9 +132,27 @@ public function getConfigDataProvider() 'id' => 1, 'createdAt' => new \DateTime(), 'paypalDetails' => new PayPalDetails(['paymentId' => 10]), - 'refundIds' => [1, 2, 3, 4, 5] + 'refundIds' => [1, 2, 3, 4, 5], + 'merchantAccountId' => 'MerchantId', + 'orderId' => 1, + 'paymentInstrumentType' => 'credit_card', + 'type' => 'sale', + 'amount' => '$19.99', + 'processorSettlementResponseCode' => 1, + 'status' => 'pending_for_settlement', + 'processorSettlementResponseText' => 'sample text', + 'settlementBatchId' => 2, + 'currencyIsoCode' => 'USD' ] ] ]; } + + /** + * @return void + */ + protected function tearDown() + { + Phrase::setRenderer($this->defaultRenderer); + } } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionsCollectionTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionsCollectionTest.php index 50488df2600c6..6024141280a02 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionsCollectionTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionsCollectionTest.php @@ -184,4 +184,42 @@ public function testGetItemsWithNullLimit() $this->assertEquals(TransactionsCollection::TRANSACTION_MAXIMUM_COUNT, count($items)); $this->assertInstanceOf(DocumentInterface::class, $items[1]); } + + /** + * Add fields to filter + * + * @dataProvider addToFilterDataProvider + */ + public function testAddToFilter($field, $condition, $filterMapperCall, $expectedCondition) + { + $this->filterMapperMock->expects(static::exactly($filterMapperCall)) + ->method('getFilter') + ->with($field, $expectedCondition) + ->willReturn(new BraintreeSearchNodeStub()); + + $collection = new TransactionsCollection( + $this->entityFactoryMock, + $this->braintreeAdapterMock, + $this->filterMapperMock + ); + + static::assertInstanceOf( + TransactionsCollection::class, + $collection->addFieldToFilter($field, $condition) + ); + } + + /** + * addToFilter DataProvider + * + * @return array + */ + public function addToFilterDataProvider() + { + return [ + ['orderId', ['like' => 1], 1, ['like' => 1]], + ['type', 'sale', 1, ['eq' => 'sale']], + [['type', 'orderId'], [], 0, []], + ]; + } } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/PayPal/TokenUiComponentProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/PayPal/TokenUiComponentProviderTest.php new file mode 100644 index 0000000000000..bdc39cbc5b868 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/PayPal/TokenUiComponentProviderTest.php @@ -0,0 +1,114 @@ +componentFactory = $this->getMockBuilder(TokenUiComponentInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->urlBuilder = $this->getMock(UrlInterface::class); + + $this->config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->setMethods(['getPayPalIcon']) + ->getMock(); + + $this->tokenUiComponentProvider = new TokenUiComponentProvider( + $this->componentFactory, + $this->urlBuilder, + $this->config + ); + } + + /** + * @covers \Magento\Braintree\Model\Ui\Adminhtml\PayPal\TokenUiComponentProvider::getComponentForToken + */ + public function testGetComponentForToken() + { + $nonceUrl = 'https://payment/adminhtml/nonce/url'; + $payerEmail = 'john.doe@test.com'; + $icon = [ + 'url' => 'https://payment/adminhtml/icon.png', + 'width' => 48, + 'height' => 32 + ]; + + $expected = [ + 'code' => 'vault', + 'nonceUrl' => $nonceUrl, + 'details' => [ + 'payerEmail' => $payerEmail, + 'icon' => $icon + ], + 'template' => 'vault.phtml' + ]; + + $this->config->expects(static::once()) + ->method('getPayPalIcon') + ->willReturn($icon); + + $paymentToken = $this->getMock(PaymentTokenInterface::class); + $paymentToken->expects(static::once()) + ->method('getTokenDetails') + ->willReturn('{"payerEmail":" ' . $payerEmail . '"}'); + $paymentToken->expects(static::once()) + ->method('getPublicHash') + ->willReturn('cmk32dl21l'); + + $this->urlBuilder->expects(static::once()) + ->method('getUrl') + ->willReturn($nonceUrl); + + $tokenComponent = $this->getMock(TokenUiComponentInterface::class); + $tokenComponent->expects(static::once()) + ->method('getConfig') + ->willReturn($expected); + + $this->componentFactory->expects(static::once()) + ->method('create') + ->willReturn($tokenComponent); + + $component = $this->tokenUiComponentProvider->getComponentForToken($paymentToken); + static::assertEquals($tokenComponent, $component); + static::assertEquals($expected, $component->getConfig()); + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/TokenUiComponentProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/TokenUiComponentProviderTest.php index d1665c71804cf..f159136cf4c46 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/TokenUiComponentProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/Adminhtml/TokenUiComponentProviderTest.php @@ -7,10 +7,10 @@ use Magento\Braintree\Model\Ui\Adminhtml\TokenUiComponentProvider; use Magento\Framework\UrlInterface; -use Magento\Framework\View\Element\Template; use Magento\Vault\Api\Data\PaymentTokenInterface; use Magento\Vault\Model\Ui\TokenUiComponentInterface; use Magento\Vault\Model\Ui\TokenUiComponentInterfaceFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class TokenUiComponentProviderTest @@ -19,12 +19,12 @@ class TokenUiComponentProviderTest extends \PHPUnit_Framework_TestCase { /** - * @var TokenUiComponentInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var TokenUiComponentInterfaceFactory|MockObject */ private $componentFactory; /** - * @var UrlInterface|\PHPUnit_Framework_MockObject_MockObject + * @var UrlInterface|MockObject */ private $urlBuilder; @@ -59,6 +59,7 @@ public function testGetComponentForToken() $expirationDate = '12/2015'; $expected = [ + 'code' => 'vault', 'nonceUrl' => $nonceUrl, 'details' => [ 'type' => $type, diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php index 0195c8bd7a883..bc142aa41f722 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php @@ -8,6 +8,7 @@ use Magento\Braintree\Gateway\Config\Config; use Magento\Braintree\Model\Adapter\BraintreeAdapter; use Magento\Braintree\Model\Ui\ConfigProvider; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use Magento\Braintree\Gateway\Config\PayPal\Config as PayPalConfig; use Magento\Framework\Locale\ResolverInterface; @@ -23,25 +24,15 @@ class ConfigProviderTest extends \PHPUnit_Framework_TestCase const MERCHANT_ACCOUNT_ID = '245345'; /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $config; /** - * @var PayPalConfig|\PHPUnit_Framework_MockObject_MockObject - */ - private $payPalConfig; - - /** - * @var BraintreeAdapter|\PHPUnit_Framework_MockObject_MockObject + * @var BraintreeAdapter|MockObject */ private $braintreeAdapter; - /** - * @var ResolverInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $localeResolver; - /** * @var ConfigProvider */ @@ -53,7 +44,7 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->payPalConfig = $this->getMockBuilder(PayPalConfig::class) + $payPalConfig = $this->getMockBuilder(PayPalConfig::class) ->disableOriginalConstructor() ->getMock(); @@ -61,13 +52,13 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->localeResolver = $this->getMockForAbstractClass(ResolverInterface::class); + $localeResolver = $this->getMockForAbstractClass(ResolverInterface::class); $this->configProvider = new ConfigProvider( $this->config, - $this->payPalConfig, + $payPalConfig, $this->braintreeAdapter, - $this->localeResolver + $localeResolver ); } @@ -90,26 +81,6 @@ public function testGetConfig($config, $expected) ->willReturn($value); } - $this->payPalConfig->expects(static::once()) - ->method('isActive') - ->willReturn(true); - - $this->payPalConfig->expects(static::once()) - ->method('isAllowToEditShippingAddress') - ->willReturn(true); - - $this->payPalConfig->expects(static::once()) - ->method('getMerchantName') - ->willReturn('Test'); - - $this->payPalConfig->expects(static::once()) - ->method('getTitle') - ->willReturn('Payment Title'); - - $this->localeResolver->expects(static::once()) - ->method('getLocale') - ->willReturn('en_US'); - static::assertEquals($expected, $this->configProvider->getConfig()); } @@ -179,15 +150,6 @@ public function getConfigDataProvider() 'enabled' => true, 'thresholdAmount' => 20, 'specificCountries' => ['GB', 'US', 'CA'] - ], - ConfigProvider::PAYPAL_CODE => [ - 'isActive' => true, - 'title' => 'Payment Title', - 'isAllowShippingAddressOverride' => true, - 'merchantName' => 'Test', - 'locale' => 'en_us', - 'paymentAcceptanceMarkSrc' => - 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-medium.png' ] ] ] diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/ConfigProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/ConfigProviderTest.php new file mode 100644 index 0000000000000..8859425eb0def --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/ConfigProviderTest.php @@ -0,0 +1,118 @@ +config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->localeResolver = $this->getMockForAbstractClass(ResolverInterface::class); + + $this->configProvider = new ConfigProvider( + $this->config, + $this->localeResolver + ); + } + + /** + * Run test getConfig method + * + * @param array $config + * @dataProvider getConfigDataProvider + */ + public function testGetConfig($expected) + { + $this->config->expects(static::once()) + ->method('isActive') + ->willReturn(true); + + $this->config->expects(static::once()) + ->method('isAllowToEditShippingAddress') + ->willReturn(true); + + $this->config->expects(static::once()) + ->method('getMerchantName') + ->willReturn('Test'); + + $this->config->expects(static::once()) + ->method('getTitle') + ->willReturn('Payment Title'); + + $this->localeResolver->expects(static::once()) + ->method('getLocale') + ->willReturn('en_US'); + + $this->config->expects(static::once()) + ->method('isSkipOrderReview') + ->willReturn(false); + + $this->config->expects(static::once()) + ->method('getPayPalIcon') + ->willReturn([ + 'width' => 30, 'height' => 26, 'url' => 'https://icon.test.url' + ]); + + static::assertEquals($expected, $this->configProvider->getConfig()); + } + + /** + * @return array + */ + public function getConfigDataProvider() + { + return [ + [ + 'expected' => [ + 'payment' => [ + ConfigProvider::PAYPAL_CODE => [ + 'isActive' => true, + 'title' => 'Payment Title', + 'isAllowShippingAddressOverride' => true, + 'merchantName' => 'Test', + 'locale' => 'en_us', + 'paymentAcceptanceMarkSrc' => + 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-medium.png', + 'vaultCode' => ConfigProvider::PAYPAL_VAULT_CODE, + 'skipOrderReview' => false, + 'paymentIcon' => [ + 'width' => 30, 'height' => 26, 'url' => 'https://icon.test.url' + ] + ] + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/TokenUiComponentProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/TokenUiComponentProviderTest.php new file mode 100644 index 0000000000000..d0368a22ef960 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/TokenUiComponentProviderTest.php @@ -0,0 +1,92 @@ +componentFactory = $this->getMockBuilder(TokenUiComponentInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->tokenComponent = $this->getMockForAbstractClass(TokenUiComponentInterface::class); + + $this->urlBuilder = $this->getMockForAbstractClass(UrlInterface::class); + + $this->paymentToken = $this->getMockForAbstractClass(PaymentTokenInterface::class); + + $this->componentProvider = new TokenUiComponentProvider( + $this->componentFactory, + $this->urlBuilder + ); + } + + /** + * @covers \Magento\Braintree\Model\Ui\PayPal\TokenUiComponentProvider::getComponentForToken + */ + public function testGetComponentForToken() + { + $tokenDetails = [ + 'payerEmail' => 'john.doe@example.com' + ]; + $hash = '4g1mn4ew0vj23n2jf'; + + $this->paymentToken->expects(static::once()) + ->method('getTokenDetails') + ->willReturn(json_encode($tokenDetails)); + + $this->componentFactory->expects(static::once()) + ->method('create') + ->willReturn($this->tokenComponent); + + $this->paymentToken->expects(static::once()) + ->method('getPublicHash') + ->willReturn($hash); + + $this->urlBuilder->expects(static::once()) + ->method('getUrl'); + + $actual = $this->componentProvider->getComponentForToken($this->paymentToken); + static::assertEquals($this->tokenComponent, $actual); + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Ui/Component/Report/Filters/Type/DateRangeTest.php b/app/code/Magento/Braintree/Test/Unit/Ui/Component/Report/Filters/Type/DateRangeTest.php new file mode 100644 index 0000000000000..b81dbe2fb036f --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Ui/Component/Report/Filters/Type/DateRangeTest.php @@ -0,0 +1,251 @@ +contextMock = $this->getMockForAbstractClass(ContextInterface::class); + $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->contextMock->expects(static::any()) + ->method('getProcessor') + ->willReturn($processor); + $this->uiComponentFactory = $this->getMockBuilder(UiComponentFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->filterBuilderMock = $this->getMockBuilder(FilterBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->filterModifierMock = $this->getMockBuilder(FilterModifier::class) + ->setMethods(['applyFilterModifier']) + ->disableOriginalConstructor() + ->getMock(); + + $this->dataProviderMock = $this->getMockForAbstractClass(DataProviderInterface::class); + } + + /** + * Run test prepare method + * + * @param string $name + * @param array $filterData + * @param array|null $expectedCondition + * @dataProvider getPrepareDataProvider + * @return void + */ + public function testPrepare($name, $filterData, $expectedCondition) + { + /** @var FormDate PHPUnit_Framework_MockObject_MockObject|$uiComponent */ + $uiComponent = $this->getMockBuilder(FormDate::class) + ->disableOriginalConstructor() + ->getMock(); + + $uiComponent->expects($this->any()) + ->method('getContext') + ->willReturn($this->contextMock); + + $this->contextMock->expects($this->any()) + ->method('getNamespace') + ->willReturn(DateRange::NAME); + $this->contextMock->expects($this->any()) + ->method('addComponentDefinition') + ->with(DateRange::NAME, ['extends' => DateRange::NAME]); + + $this->contextMock->expects($this->any()) + ->method('getFiltersParams') + ->willReturn($filterData); + + $this->contextMock->expects($this->any()) + ->method('getDataProvider') + ->willReturn($this->dataProviderMock); + + if ($expectedCondition !== null) { + if (is_string($filterData[$name])) { + $uiComponent->expects(static::once()) + ->method('convertDate') + ->with($filterData[$name]) + ->willReturn(new \DateTime($filterData[$name], new \DateTimeZone('UTC'))); + } else { + $uiComponent->method('convertDate') + ->willReturnMap([ + [ + $filterData[$name]['from'], 0, 0, 0, + new \DateTime($filterData[$name]['from'], new \DateTimeZone('UTC')) + ], + [ + $filterData[$name]['to'], 23, 59, 59, + new \DateTime($filterData[$name]['to'] . ' 23:59:00', new \DateTimeZone('UTC')) + ], + ]); + } + + $i=0; + switch (true) { + case is_string($filterData[$name]): + case isset($filterData[$name]['from']) && !isset($filterData[$name]['to']): + case !isset($filterData[$name]['from']) && isset($filterData[$name]['to']): + $filterMock = $this->getFilterMock( + $name, + $expectedCondition['type'], + $expectedCondition['date'], + $i + ); + $this->dataProviderMock->expects(static::once()) + ->method('addFilter') + ->with($filterMock); + break; + case isset($filterData[$name]['from']) && isset($filterData[$name]['to']): + $this->getFilterMock( + $name, + $expectedCondition['type_from'], + $expectedCondition['date_from'], + $i + ); + $filterMock = $this->getFilterMock( + $name, + $expectedCondition['type_to'], + $expectedCondition['date_to'], + $i + ); + $this->dataProviderMock->expects(static::exactly(2)) + ->method('addFilter') + ->with($filterMock); + break; + } + } + + $this->uiComponentFactory->expects($this->any()) + ->method('create') + ->with($name, DateRange::COMPONENT, ['context' => $this->contextMock]) + ->willReturn($uiComponent); + + $date = new DateRange( + $this->contextMock, + $this->uiComponentFactory, + $this->filterBuilderMock, + $this->filterModifierMock, + [], + ['name' => $name] + ); + $date->prepare(); + } + + /** + * Gets Filter mock + * + * @param string $name + * @param string $expectedType + * @param string $expectedDate + * @param int $i + * + * @return Filter|\PHPUnit_Framework_MockObject_MockObject + */ + private function getFilterMock($name, $expectedType, $expectedDate, &$i) + { + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setConditionType') + ->with($expectedType) + ->willReturnSelf(); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setField') + ->with($name) + ->willReturnSelf(); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setValue') + ->with($expectedDate) + ->willReturnSelf(); + + $filterMock = $this->getMock(Filter::class); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('create') + ->willReturn($filterMock); + + return $filterMock; + } + + /** + * @return array + */ + public function getPrepareDataProvider() + { + return [ + [ + 'test_date', + ['test_date' => ['from' => '11-05-2015', 'to' => null]], + ['date' => '2015-05-11T00:00:00+0000', 'type' => 'gteq'], + ], + [ + 'test_date', + ['test_date' => ['from' => null, 'to' => '11-05-2015']], + ['date' => '2015-05-11T23:59:00+0000', 'type' => 'lteq'], + ], + [ + 'test_date', + ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], + [ + 'date_from' => '2015-05-11T00:00:00+0000', 'type_from' => 'gteq', + 'date_to' => '2015-05-11T23:59:00+0000', 'type_to' => 'lteq' + ], + ], + [ + 'test_date', + ['test_date' => '11-05-2015'], + ['date' => '2015-05-11T00:00:00+0000', 'type' => 'eq'], + ], + [ + 'test_date', + ['test_date' => ['from' => '', 'to' => '']], + null, + ] + ]; + } +} diff --git a/app/code/Magento/Braintree/Ui/Component/Report/Filters/Type/DateRange.php b/app/code/Magento/Braintree/Ui/Component/Report/Filters/Type/DateRange.php new file mode 100644 index 0000000000000..adbb3b78cb663 --- /dev/null +++ b/app/code/Magento/Braintree/Ui/Component/Report/Filters/Type/DateRange.php @@ -0,0 +1,19 @@ + __('Paypal account'), - PaymentInstrumentType::COINBASE_ACCOUNT => __('Coinbase account'), - PaymentInstrumentType::EUROPE_BANK_ACCOUNT => __('Europe bank account'), - PaymentInstrumentType::CREDIT_CARD => __('Credit card'), - PaymentInstrumentType::APPLE_PAY_CARD => __('Apple pay card'), - PaymentInstrumentType::ANDROID_PAY_CARD => __('Android pay card') + PaymentInstrumentType::PAYPAL_ACCOUNT => __(PaymentInstrumentType::PAYPAL_ACCOUNT), + PaymentInstrumentType::COINBASE_ACCOUNT => __(PaymentInstrumentType::COINBASE_ACCOUNT), + PaymentInstrumentType::EUROPE_BANK_ACCOUNT => __(PaymentInstrumentType::EUROPE_BANK_ACCOUNT), + PaymentInstrumentType::CREDIT_CARD => __(PaymentInstrumentType::CREDIT_CARD), + PaymentInstrumentType::APPLE_PAY_CARD => __(PaymentInstrumentType::APPLE_PAY_CARD), + PaymentInstrumentType::ANDROID_PAY_CARD => __(PaymentInstrumentType::ANDROID_PAY_CARD) ]; } } diff --git a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/Status.php b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/Status.php index f5424c6dd9b7f..ca6d6522990b4 100644 --- a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/Status.php +++ b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/Status.php @@ -44,20 +44,20 @@ public function toOptionArray() private function getAvailableStatuses() { return [ - Transaction::AUTHORIZATION_EXPIRED => __('Authorization expired'), - Transaction::AUTHORIZING => __('Authorizing'), - Transaction::AUTHORIZED => __('Authorized'), - Transaction::GATEWAY_REJECTED => __('Gateway rejected'), - Transaction::FAILED => __('Failed'), - Transaction::PROCESSOR_DECLINED => __('Processor declined'), - Transaction::SETTLED => __('Settled'), - Transaction::SETTLING => __('Settling'), - Transaction::SUBMITTED_FOR_SETTLEMENT => __('Submitted for settlement'), - Transaction::VOIDED => __('Voided'), - Transaction::UNRECOGNIZED => __('Unrecognized'), - Transaction::SETTLEMENT_DECLINED => __('Settlement declined'), - Transaction::SETTLEMENT_PENDING => __('Settlement pending'), - Transaction::SETTLEMENT_CONFIRMED => __('Settlement confirmed') + Transaction::AUTHORIZATION_EXPIRED => __(Transaction::AUTHORIZATION_EXPIRED), + Transaction::AUTHORIZING => __(Transaction::AUTHORIZING), + Transaction::AUTHORIZED => __(Transaction::AUTHORIZED), + Transaction::GATEWAY_REJECTED => __(Transaction::GATEWAY_REJECTED), + Transaction::FAILED => __(Transaction::FAILED), + Transaction::PROCESSOR_DECLINED => __(Transaction::PROCESSOR_DECLINED), + Transaction::SETTLED => __(Transaction::SETTLED), + Transaction::SETTLING => __(Transaction::SETTLING), + Transaction::SUBMITTED_FOR_SETTLEMENT => __(Transaction::SUBMITTED_FOR_SETTLEMENT), + Transaction::VOIDED => __(Transaction::VOIDED), + Transaction::UNRECOGNIZED => __(Transaction::UNRECOGNIZED), + Transaction::SETTLEMENT_DECLINED => __(Transaction::SETTLEMENT_DECLINED), + Transaction::SETTLEMENT_PENDING => __(Transaction::SETTLEMENT_PENDING), + Transaction::SETTLEMENT_CONFIRMED => __(Transaction::SETTLEMENT_CONFIRMED) ]; } } diff --git a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/TransactionType.php b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/TransactionType.php index 312b2f518b464..0fe752d423277 100644 --- a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/TransactionType.php +++ b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/TransactionType.php @@ -44,8 +44,8 @@ public function toOptionArray() private function getAvailableTransactionTypes() { return [ - Transaction::SALE => __('Sale'), - Transaction::CREDIT => __('Credit') + Transaction::SALE => __(Transaction::SALE), + Transaction::CREDIT => __(Transaction::CREDIT) ]; } } diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index 1d256221702f7..5d0d179363310 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -17,6 +17,7 @@ "magento/module-quote": "100.1.*", "magento/module-paypal": "100.1.*", "magento/module-theme": "100.1.*", + "magento/module-ui": "100.1.*", "braintree/braintree_php": "3.7.0" }, "suggest": { diff --git a/app/code/Magento/Braintree/etc/adminhtml/di.xml b/app/code/Magento/Braintree/etc/adminhtml/di.xml index ed52db8cf7234..d154aabbb01b5 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/di.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/di.xml @@ -27,6 +27,7 @@ Magento\Braintree\Gateway\Request\ChannelDataBuilder Magento\Braintree\Gateway\Request\AddressDataBuilder Magento\Braintree\Gateway\Request\VaultDataBuilder + Magento\Braintree\Gateway\Request\DescriptorDataBuilder @@ -37,6 +38,7 @@ Magento\Braintree\Gateway\Request\PaymentDataBuilder Magento\Braintree\Gateway\Request\ChannelDataBuilder Magento\Braintree\Gateway\Request\AddressDataBuilder + Magento\Braintree\Gateway\Request\DescriptorDataBuilder @@ -45,6 +47,7 @@ Magento\Braintree\Model\Ui\Adminhtml\TokenUiComponentProvider + Magento\Braintree\Model\Ui\Adminhtml\PayPal\TokenUiComponentProvider diff --git a/app/code/Magento/Braintree/etc/adminhtml/system.xml b/app/code/Magento/Braintree/etc/adminhtml/system.xml index 3668cd7779fda..9d3e17bf06fe8 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/system.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/system.xml @@ -8,16 +8,14 @@
- - - - 1 - complex braintree-section - Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Group - - + + + + No setup or monthly fees and your customers never leave your store to complete the purchase.]]> + complex braintree-section Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment payment/braintree/active + recommended_solutions Magento\Config\Model\Config\Source\Yesno @@ -35,15 +33,19 @@ - + Magento\Config\Model\Config\Source\Yesno payment/braintree_cc_vault/active + + http://docs.magento.com/m2/ce/user_guide/payment/braintree.html + Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint + - Click here to login to your existing Braintree account. Or to setup a new account and accept payments on your website, click here to signup for a Braintree account.]]> + Click here to login to your existing Braintree account. Or to setup a new account and accept payments on your website, click here to signup for a Braintree account.

Powered by Braintree v.zero with Hosted Fields latest technology. Hosted Fields are small, transparent iframes that replace the sensitive credit card inputs in your checkout flow - helping you meet the latest data security requirements while ensuring your customization doesn't suffer. Find out more.]]>
1 Magento\Config\Block\System\Config\Form\Fieldset @@ -153,6 +155,14 @@ payment/braintree_paypal/title It is recommended to set this value to "PayPal" per store views. + + + Magento\Config\Model\Config\Source\Yesno + payment/braintree_paypal_vault/active + + + + validate-number @@ -200,6 +210,11 @@ payment/braintree_paypal/display_on_shopping_cart Also affects mini-shopping cart. + + + Magento\Config\Model\Config\Source\Yesno + payment/braintree_paypal/skip_order_review +
@@ -225,6 +240,36 @@ payment/braintree/verify_specific_countries + + + Braintree Support.]]> + Magento\Config\Block\System\Config\Form\Fieldset + + + payment/braintree/descriptor_name + + The value in the business name field of a customer's statement. Company name/DBA section must be either 3, 7 or 12 characters + and the product descriptor can be up to 18, 14, or 9 characters respectively (with an * in between for a total descriptor name of 22 characters). + + + + + payment/braintree/descriptor_phone + + The value in the phone number field of a customer's statement. Phone must be 10-14 characters and can only contain numbers, dashes, parentheses and periods. + + + + + payment/braintree/descriptor_url + + The value in the URL/web address field of a customer's statement. The URL must be 13 characters or shorter. + + +
diff --git a/app/code/Magento/Braintree/etc/config.xml b/app/code/Magento/Braintree/etc/config.xml index 7511ec2ca1563..bf19324ae7a02 100644 --- a/app/code/Magento/Braintree/etc/config.xml +++ b/app/code/Magento/Braintree/etc/config.xml @@ -25,13 +25,16 @@ 1 1 1 + 1 + 1 + 1 AE,VI,MC,DI,JCB,CUP,DN,MI 1 processing sandbox 0 - + cvv,number @@ -57,6 +60,8 @@ 1 1 1 + 1 + 1 processorResponseCode,processorResponseText,paymentId processorResponseCode,processorResponseText,paymentId,payerEmail @@ -64,6 +69,11 @@ BraintreeCreditCardVaultFacade Stored Cards (Braintree) + + BraintreePayPalVaultFacade + Stored Accounts (Braintree PayPal) + 1 +
diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml index def12ff4c8a7e..d051ef78cfcd2 100644 --- a/app/code/Magento/Braintree/etc/di.xml +++ b/app/code/Magento/Braintree/etc/di.xml @@ -19,13 +19,15 @@ - Magento\Braintree\Model\Ui\ConfigProvider::PAYPAL_CODE + Magento\Braintree\Model\Ui\PayPal\ConfigProvider::PAYPAL_CODE BraintreePayPalInfo BraintreePayPalValueHandlerPool + BraintreePayPalValidatorPool BraintreePayPalCommandPool - + + Magento\Braintree\Model\Ui\ConfigProvider::CC_VAULT_CODE @@ -51,6 +53,32 @@ Magento\Braintree\Model\Ui\ConfigProvider::CC_VAULT_CODE + + + Magento\Braintree\Model\Ui\PayPal\ConfigProvider::PAYPAL_VAULT_CODE + + + + + BraintreePayPalVaultPaymentConfig + + + + + + BraintreePayPalVaultPaymentValueHandler + + + + + + BraintreePayPalVaultPaymentConfig + BraintreePayPalVaultPaymentValueHandlerPool + BraintreePayPalFacade + Magento\Braintree\Model\Ui\PayPal\ConfigProvider::PAYPAL_VAULT_CODE + + + @@ -60,7 +88,7 @@ - Magento\Braintree\Model\Ui\ConfigProvider::PAYPAL_CODE + Magento\Braintree\Model\Ui\PayPal\ConfigProvider::PAYPAL_CODE @@ -70,6 +98,27 @@ Magento\Braintree\Gateway\Config\Config + + + BraintreeLoggerForTransactionSale + + + + + BraintreeLoggerForTransactionSale + + + + + BraintreeLoggerForTransactionSale + + + + + BraintreeLoggerForTransactionSale + + + @@ -85,6 +134,7 @@ BraintreeVoidCommand BraintreeRefundCommand BraintreeVoidCommand + BraintreeVoidCommand @@ -95,39 +145,49 @@ BraintreePayPalSaleCommand BraintreePayPalCaptureStrategyCommand BraintreeCaptureCommand + BraintreePayPalVaultAuthorizeCommand + BraintreePayPalVaultSaleCommand + BraintreeVaultCaptureCommand BraintreeVoidCommand BraintreeRefundCommand BraintreeVoidCommand - - + BraintreeCommandPool - - + - - BraintreeCommandManager - + BraintreePayPalCommandPool - + + - + + BraintreeCommandPool - + BraintreePayPalCommandPool + + + + BraintreeCommandManager + BraintreePayPalCommandManager + + + + - + BraintreeAuthorizeRequest @@ -146,11 +206,42 @@ Magento\Braintree\Gateway\Request\AddressDataBuilder Magento\Braintree\Gateway\Request\VaultDataBuilder Magento\Braintree\Gateway\Request\ThreeDSecureDataBuilder - Magento\Braintree\Gateway\Request\KountPaymentDataBuilder + Magento\Braintree\Gateway\Request\KountPaymentDataBuilder + Magento\Braintree\Gateway\Request\DescriptorDataBuilder + + + + + + + BraintreeSaleRequest + + + + + + BraintreeAuthorizeRequest + Magento\Braintree\Gateway\Request\SettlementDataBuilder + + + + + + + BraintreeCaptureRequest + Magento\Braintree\Gateway\Http\TransferFactory + Magento\Braintree\Gateway\Http\Client\TransactionSubmitForSettlement + Magento\Braintree\Gateway\Response\TransactionIdHandler + Magento\Braintree\Gateway\Validator\ResponseValidator + + + + + + Magento\Braintree\Gateway\Request\CaptureDataBuilder - @@ -169,74 +260,61 @@ Magento\Braintree\Gateway\Request\ChannelDataBuilder Magento\Braintree\Gateway\Request\AddressDataBuilder Magento\Braintree\Gateway\Request\ThreeDSecureDataBuilder - Magento\Braintree\Gateway\Request\KountPaymentDataBuilder + Magento\Braintree\Gateway\Request\KountPaymentDataBuilder + Magento\Braintree\Gateway\Request\DescriptorDataBuilder - - - + + - BraintreePayPalAuthorizeRequest - BraintreePayPalResponseHandler + BraintreeVaultSaleRequest - + - Magento\Braintree\Gateway\Request\CustomerDataBuilder - Magento\Braintree\Gateway\Request\PaymentDataBuilder - Magento\Braintree\Gateway\Request\ChannelDataBuilder + BraintreeVaultAuthorizeRequest + Magento\Braintree\Gateway\Request\SettlementDataBuilder - - - - BraintreeLoggerForTransactionSale - - - - - BraintreeLoggerForTransactionSale - - - - - BraintreeLoggerForTransactionSale - - - - - BraintreeLoggerForTransactionSale - - - - - + + - BraintreeSaleRequest + BraintreeVaultCaptureRequest + Magento\Braintree\Gateway\Http\TransferFactory + Magento\Braintree\Gateway\Http\Client\TransactionSale + Magento\Braintree\Gateway\Response\TransactionIdHandler + Magento\Braintree\Gateway\Validator\ResponseValidator - + - BraintreeAuthorizeRequest + Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder Magento\Braintree\Gateway\Request\SettlementDataBuilder - - + + + + - BraintreeVaultSaleRequest + BraintreePayPalAuthorizeRequest + BraintreePayPalResponseHandler - + - BraintreeVaultAuthorizeRequest - Magento\Braintree\Gateway\Request\SettlementDataBuilder + Magento\Braintree\Gateway\Request\CustomerDataBuilder + Magento\Braintree\Gateway\Request\PaymentDataBuilder + Magento\Braintree\Gateway\Request\ChannelDataBuilder + Magento\Braintree\Gateway\Request\PayPal\VaultDataBuilder + Magento\Braintree\Gateway\Request\PayPal\DeviceDataBuilder + Magento\Braintree\Gateway\Request\DescriptorDataBuilder @@ -254,45 +332,46 @@ - - - + + - BraintreeCaptureRequest - Magento\Braintree\Gateway\Http\TransferFactory - Magento\Braintree\Gateway\Http\Client\TransactionSubmitForSettlement - Magento\Braintree\Gateway\Response\TransactionIdHandler - Magento\Braintree\Gateway\Validator\ResponseValidator + BraintreePayPalVaultAuthorizeRequest + BraintreePayPalVaultResponseHandler - + - Magento\Braintree\Gateway\Request\CaptureDataBuilder + Magento\Braintree\Gateway\Request\CustomerDataBuilder + Magento\Braintree\Gateway\Request\PaymentDataBuilder + Magento\Braintree\Gateway\Request\ChannelDataBuilder + Magento\Braintree\Gateway\Request\AddressDataBuilder + Magento\Braintree\Gateway\Request\DescriptorDataBuilder - - - + + - BraintreeVaultCaptureRequest - Magento\Braintree\Gateway\Http\TransferFactory - Magento\Braintree\Gateway\Http\Client\TransactionSale - Magento\Braintree\Gateway\Response\TransactionIdHandler - Magento\Braintree\Gateway\Validator\ResponseValidator + BraintreePayPalVaultSaleRequest - + - Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder + BraintreePayPalVaultAuthorizeRequest Magento\Braintree\Gateway\Request\SettlementDataBuilder + + + + Magento\Vault\Model\CreditCardTokenFactory + + @@ -330,17 +409,14 @@ - - - - Magento\Braintree\Gateway\Response\PaymentDetailsHandler - Magento\Braintree\Gateway\Response\TransactionIdHandler - Magento\Braintree\Gateway\Response\PayPalDetailsHandler - - - + + + + Magento\Vault\Model\AccountPaymentTokenFactory + + @@ -356,6 +432,26 @@ Magento\Braintree\Gateway\Config\PayPal\Config + + + + Magento\Braintree\Gateway\Response\PaymentDetailsHandler + Magento\Braintree\Gateway\Response\TransactionIdHandler + Magento\Braintree\Gateway\Response\PayPalDetailsHandler + Magento\Braintree\Gateway\Response\PayPal\VaultDetailsHandler + + + + + + + Magento\Braintree\Gateway\Response\PaymentDetailsHandler + Magento\Braintree\Gateway\Response\TransactionIdHandler + Magento\Braintree\Gateway\Response\PayPalDetailsHandler + + + + @@ -379,7 +475,7 @@ - + Magento\Braintree\Gateway\Config\Config @@ -392,6 +488,22 @@ + + + + + + Magento\Braintree\Gateway\Config\PayPal\Config + + + + + + BraintreePayPalCountryValidator + + + + @@ -404,6 +516,7 @@ + @@ -411,19 +524,16 @@ - BraintreeTransactionsCollectionFactoryForReporting - BraintreeTransactionsReporting - @@ -433,5 +543,5 @@ - + diff --git a/app/code/Magento/Braintree/etc/frontend/di.xml b/app/code/Magento/Braintree/etc/frontend/di.xml index 98ae47dea794a..cdd56e236a72b 100644 --- a/app/code/Magento/Braintree/etc/frontend/di.xml +++ b/app/code/Magento/Braintree/etc/frontend/di.xml @@ -10,6 +10,7 @@ Magento\Braintree\Model\Ui\ConfigProvider + Magento\Braintree\Model\Ui\PayPal\ConfigProvider @@ -32,6 +33,7 @@ Magento\Braintree\Model\Ui\TokenUiComponentProvider + Magento\Braintree\Model\Ui\PayPal\TokenUiComponentProvider diff --git a/app/code/Magento/Braintree/i18n/en_US.csv b/app/code/Magento/Braintree/i18n/en_US.csv index f912e59c2edac..f5e2d36e1e9bb 100644 --- a/app/code/Magento/Braintree/i18n/en_US.csv +++ b/app/code/Magento/Braintree/i18n/en_US.csv @@ -140,6 +140,29 @@ Debug,Debug "liabilityShifted", "Liability Shifted" "liabilityShiftPossible", "Liability Shift Possible" "riskDataId", "Risk ID" -"riskDataDecision", "Risk Decision", -"paymentId", "Payment Id", -"payerEmail", "Payer Email", +"riskDataDecision", "Risk Decision" +"paymentId", "Payment Id" +"payerEmail", "Payer Email" +"sale","Sale" +"credit","Credit" +"authorization_expired","Authorization expired" +"authorizing","Authorizing" +"authorized","Authorized" +"gateway_rejected","Gateway rejected" +"failed","Failed" +"processor_declined","Processor declined" +"settled","Settled" +"settling","Settling" +"submitted_for_settlement","Submitted for settlement" +"voided","Voided" +"unrecognized","Unrecognized" +"settlement_declined","Settlement declined" +"settlement_pending","Settlement pending" +"settlement_confirmed","Settlement confirmed" +"paypal_account","Paypal account" +"coinbase_account","Coinbase account" +"europe_bank_accout","Europe bank account" +"credit_card","Credit card" +"apple_pay_card","Apple pay card" +"android_pay_card","Android pay card" +"Accept credit/debit cards and PayPal in your Magento store.
No setup or monthly fees and your customers never leave your store to complete the purchase.","Accept credit/debit cards and PayPal in your Magento store.
No setup or monthly fees and your customers never leave your store to complete the purchase." \ No newline at end of file diff --git a/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_index.xml b/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_index.xml index 76f6b5a4d616c..5e4f36e1c1fb4 100644 --- a/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_index.xml +++ b/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_index.xml @@ -18,13 +18,17 @@ braintree_cc_vault Magento_Vault::form/vault.phtml + + braintree_paypal_vault + Magento_Vault::form/vault.phtml + + class="Magento\Braintree\Block\Payment" + after="billing_method"/> diff --git a/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml b/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml index 68e0abc0cd009..579b82c61f690 100644 --- a/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml +++ b/app/code/Magento/Braintree/view/adminhtml/layout/sales_order_create_load_block_billing_method.xml @@ -18,6 +18,10 @@ braintree_cc_vault Magento_Vault::form/vault.phtml + + braintree_paypal_vault + Magento_Vault::form/vault.phtml + \ No newline at end of file diff --git a/app/code/Magento/Braintree/view/adminhtml/templates/form/paypal/vault.phtml b/app/code/Magento/Braintree/view/adminhtml/templates/form/paypal/vault.phtml new file mode 100644 index 0000000000000..22930bbc65666 --- /dev/null +++ b/app/code/Magento/Braintree/view/adminhtml/templates/form/paypal/vault.phtml @@ -0,0 +1,30 @@ +getData(TokenUiComponentProviderInterface::COMPONENT_DETAILS); +$icon = $details['icon']; +$id = $block->escapeHtml($block->getData('id')); +?> +
", + "nonceUrl": "escapeUrl($block->getData('nonceUrl')); ?>" + } + }' id="payment_" class="admin__field"> +
+ + + escapeHtml($details['payerEmail']); ?> +
+
diff --git a/app/code/Magento/Braintree/view/adminhtml/templates/form/vault.phtml b/app/code/Magento/Braintree/view/adminhtml/templates/form/vault.phtml index 3811461884725..001422d4bf911 100644 --- a/app/code/Magento/Braintree/view/adminhtml/templates/form/vault.phtml +++ b/app/code/Magento/Braintree/view/adminhtml/templates/form/vault.phtml @@ -7,7 +7,7 @@ use Magento\Vault\Model\Ui\TokenUiComponentProviderInterface; // @codingStandardsIgnoreFile /** @var \Magento\Framework\View\Element\Template $block */ -$details = $block->getData('details'); +$details = $block->getData(TokenUiComponentProviderInterface::COMPONENT_DETAILS); $icon = $block->getData('icons')[$details['type']]; $id = $block->escapeHtml($block->getData('id')); ?> @@ -15,6 +15,7 @@ $id = $block->escapeHtml($block->getData('id')); "Magento_Braintree/js/vault": { "container": "payment_", "publicHash": "escapeHtml($block->getData(TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH)); ?>", + "code": "escapeHtml($block->getData('code')); ?>", "nonceUrl": "escapeUrl($block->getData('nonceUrl')); ?>" } }' id="payment_" class="admin__field"> diff --git a/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml b/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml index d1b661b2c3ecd..031ddca7a8707 100644 --- a/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml +++ b/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml @@ -109,7 +109,6 @@ - @@ -135,6 +133,22 @@ + + + + ${ $.parentName } + + componentType = column, index = ${ $.index }:visible + + braintree_report.braintree_report.listing_top.listing_filters + ui/grid/filters/elements/group + Magento_Ui/js/grid/filters/range + date + createdAt + Created At + + + @@ -216,7 +230,6 @@ desc - dateRange date Magento_Ui/js/grid/columns/date Created At diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js b/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js index fcff173e7fcd4..14729714b4e60 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js @@ -14,7 +14,8 @@ define([ return Class.extend({ defaults: { $selector: null, - selector: 'edit_form' + selector: 'edit_form', + $container: null }, /** @@ -25,17 +26,18 @@ define([ var self = this; self.$selector = $('#' + self.selector); + self.$container = $('#' + self.container); self.$selector.on( - 'setVaultNotActive', + 'setVaultNotActive.' + self.getCode(), function () { - self.$selector.off('submitOrder.braintree_vault'); + self.$selector.off('submitOrder.' + self.getCode()); } ); - this._super(); + self._super(); - this.initEventHandlers(); + self.initEventHandlers(); - return this; + return self; }, /** @@ -43,14 +45,14 @@ define([ * @returns {String} */ getCode: function () { - return 'braintree'; + return this.code; }, /** * Init event handlers */ initEventHandlers: function () { - $('#' + this.container).find('[name="payment[token_switcher]"]') + $(this.$container).find('[name="payment[token_switcher]"]') .on('click', this.selectPaymentMethod.bind(this)); }, @@ -66,7 +68,7 @@ define([ * Enable form event listeners */ enableEventListeners: function () { - this.$selector.on('submitOrder.braintree_vault', this.submitOrder.bind(this)); + this.$selector.on('submitOrder.' + this.getCode(), this.submitOrder.bind(this)); }, /** @@ -129,7 +131,7 @@ define([ this.createPublicHashSelector(); this.$selector.find('[name="payment[public_hash]"]').val(this.publicHash); - this.$selector.find('#braintree_nonce').val(nonce); + this.$container.find('#' + this.getNonceSelectorName()).val(nonce); }, /** @@ -138,16 +140,16 @@ define([ createPublicHashSelector: function () { var $input; - if (this.$selector.find('#braintree_nonce').size() === 0) { + if (this.$container.find('#' + this.getNonceSelectorName()).size() === 0) { $input = $('').attr( { type: 'hidden', - id: 'braintree_nonce', + id: this.getNonceSelectorName(), name: 'payment[payment_method_nonce]' } ); - $input.appendTo(this.$selector); + $input.appendTo(this.$container); $input.prop('disabled', false); } }, @@ -160,6 +162,14 @@ define([ alert({ content: message }); + }, + + /** + * Get selector name for nonce input + * @returns {String} + */ + getNonceSelectorName: function () { + return 'nonce_' + this.getCode(); } }); }); diff --git a/app/code/Magento/Braintree/view/adminhtml/web/styles.css b/app/code/Magento/Braintree/view/adminhtml/web/styles.css index c0d8822e779d4..81378f636eb61 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/styles.css +++ b/app/code/Magento/Braintree/view/adminhtml/web/styles.css @@ -3,5 +3,6 @@ * See COPYING.txt for license details. */ -.braintree-section .entry-edit-head > .config-heading .heading strong {padding-left:150px;background:url(images/braintree_logo.png) no-repeat 0 0 / 145px auto;line-height:36px;} -.braintree-section .entry-edit-head > .config-heading:before {background: url("images/braintree_allinone.png") no-repeat 0 0 / 100% auto;content: "";display: inline;float: right;height: 35px;width: 280px;} +.braintree-section .heading {background: url("images/braintree_logo.png") no-repeat 0 50% / 18rem auto; padding-left: 20rem;} +.braintree-section .button-container {float: right;} +.braintree-section .config-alt {background: url("images/braintree_allinone.png") no-repeat scroll 0 0 / 100% auto; height: 28px; margin: 0.5rem 0 0; width: 230px;} \ No newline at end of file diff --git a/app/code/Magento/Braintree/view/base/web/images/paypal-small.png b/app/code/Magento/Braintree/view/base/web/images/paypal-small.png new file mode 100644 index 0000000000000..92d5c63bca442 Binary files /dev/null and b/app/code/Magento/Braintree/view/base/web/images/paypal-small.png differ diff --git a/app/code/Magento/Braintree/view/base/web/images/paypal.png b/app/code/Magento/Braintree/view/base/web/images/paypal.png new file mode 100644 index 0000000000000..291693037814a Binary files /dev/null and b/app/code/Magento/Braintree/view/base/web/images/paypal.png differ diff --git a/app/code/Magento/Braintree/view/frontend/layout/vault_cards_listaction.xml b/app/code/Magento/Braintree/view/frontend/layout/vault_cards_listaction.xml index b7bf5919a7e15..c8cd4e3cc4085 100644 --- a/app/code/Magento/Braintree/view/frontend/layout/vault_cards_listaction.xml +++ b/app/code/Magento/Braintree/view/frontend/layout/vault_cards_listaction.xml @@ -11,6 +11,9 @@ + + + diff --git a/app/code/Magento/Braintree/view/frontend/requirejs-config.js b/app/code/Magento/Braintree/view/frontend/requirejs-config.js index c08a45bdbecd7..76391a81fe663 100644 --- a/app/code/Magento/Braintree/view/frontend/requirejs-config.js +++ b/app/code/Magento/Braintree/view/frontend/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - braintree: 'https://js.braintreegateway.com/js/braintree-2.17.6.min.js' + braintree: 'https://js.braintreegateway.com/js/braintree-2.25.0.min.js' } } }; diff --git a/app/code/Magento/Braintree/view/frontend/templates/paypal/vault_token.phtml b/app/code/Magento/Braintree/view/frontend/templates/paypal/vault_token.phtml new file mode 100644 index 0000000000000..32c2d98e7e76b --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/templates/paypal/vault_token.phtml @@ -0,0 +1,49 @@ +escapeHtml($block->getPayerEmail()); +?> + + + <?php echo $block->escapeHtml(__('PayPal Logo')); ?> + + + +
+ getBlockHtml('formkey'); ?> + + +
+ + diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal-vault.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal-vault.js new file mode 100644 index 0000000000000..a609175fe25bf --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal-vault.js @@ -0,0 +1,87 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ +define([ + 'jquery', + 'underscore', + 'Magento_Vault/js/view/payment/method-renderer/vault', + 'Magento_Ui/js/model/messageList', + 'Magento_Checkout/js/model/full-screen-loader' +], function ($, _, VaultComponent, globalMessageList, fullScreenLoader) { + 'use strict'; + + return VaultComponent.extend({ + defaults: { + template: 'Magento_Braintree/payment/paypal/vault', + additionalData: {} + }, + + /** + * Get PayPal payer email + * @returns {String} + */ + getPayerEmail: function () { + return this.details.payerEmail; + }, + + /** + * Get type of payment + * @returns {String} + */ + getPaymentIcon: function () { + return window.checkoutConfig.payment['braintree_paypal'].paymentIcon; + }, + + /** + * Place order + */ + beforePlaceOrder: function () { + this.getPaymentMethodNonce(); + }, + + /** + * Send request to get payment method nonce + */ + getPaymentMethodNonce: function () { + var self = this; + + fullScreenLoader.startLoader(); + $.get(self.nonceUrl, { + 'public_hash': self.publicHash + }) + .done(function (response) { + fullScreenLoader.stopLoader(); + self.additionalData['payment_method_nonce'] = response.paymentMethodNonce; + self.placeOrder(); + }) + .fail(function (response) { + var error = JSON.parse(response.responseText); + + fullScreenLoader.stopLoader(); + globalMessageList.addErrorMessage({ + message: error.message + }); + }); + }, + + /** + * Get payment method data + * @returns {Object} + */ + getData: function () { + var data = { + 'method': this.code, + 'additional_data': { + 'public_hash': this.publicHash + } + }; + + data['additional_data'] = _.extend(data['additional_data'], this.additionalData); + + return data; + } + }); +}); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js index 2b7aea3db5c4f..075a1fdaf1fc1 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js @@ -11,8 +11,20 @@ define([ 'Magento_Braintree/js/view/payment/adapter', 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/full-screen-loader', - 'Magento_Checkout/js/model/payment/additional-validators' -], function ($, _, Component, Braintree, quote, fullScreenLoader, additionalValidators) { + 'Magento_Checkout/js/model/payment/additional-validators', + 'Magento_Vault/js/view/payment/vault-enabler', + 'Magento_Checkout/js/action/create-billing-address' +], function ( + $, + _, + Component, + Braintree, + quote, + fullScreenLoader, + additionalValidators, + VaultEnabler, + createBillingAddress +) { 'use strict'; return Component.extend({ @@ -22,12 +34,24 @@ define([ active: false, paymentMethodNonce: null, grandTotalAmount: null, + isReviewRequired: false, + customerEmail: null, + + /** + * Additional payment data + * + * {Object} + */ + additionalData: {}, /** * PayPal client configuration * {Object} */ clientConfig: { + dataCollector: { + paypal: true + }, /** * Triggers when widget is loaded @@ -35,6 +59,7 @@ define([ */ onReady: function (checkout) { Braintree.checkout = checkout; + this.additionalData['device_data'] = checkout.deviceData; this.enableButton(); Braintree.onReady(); }, @@ -60,17 +85,24 @@ define([ var self = this; this._super() - .observe(['active']); + .observe(['active', 'isReviewRequired', 'customerEmail']); + + this.vaultEnabler = new VaultEnabler(); + this.vaultEnabler.setPaymentCode(this.getVaultCode()); + this.vaultEnabler.isActivePaymentTokenEnabler.subscribe(function () { + self.onVaultPaymentTokenEnablerChange(); + }); this.grandTotalAmount = quote.totals()['base_grand_total']; quote.totals.subscribe(function () { if (self.grandTotalAmount !== quote.totals()['base_grand_total']) { self.grandTotalAmount = quote.totals()['base_grand_total']; - self.reInitPayPal(); } }); + // for each component initialization need update property + this.isReviewRequired(false); this.initClientConfig(); return this; @@ -131,8 +163,6 @@ define([ this.clientConfig[name] = fn.bind(this); } }, this); - - Braintree.config = _.extend(Braintree.config, this.clientConfig); }, /** @@ -152,14 +182,16 @@ define([ var billingAddress = { street: [address.streetAddress], city: address.locality, - regionCode: address.region, postcode: address.postalCode, countryId: address.countryCodeAlpha2, + email: customer.email, firstname: customer.firstName, lastname: customer.lastName, telephone: customer.phone }; + billingAddress['region_code'] = address.region; + billingAddress = createBillingAddress(billingAddress); quote.billingAddress(billingAddress); }, @@ -173,7 +205,13 @@ define([ if (quote.billingAddress() === null && typeof data.details.billingAddress !== 'undefined') { this.setBillingAddress(data.details, data.details.billingAddress); } - this.placeOrder(); + + if (this.isSkipOrderReview()) { + this.placeOrder(); + } else { + this.customerEmail(data.details.email); + this.isReviewRequired(true); + } }, /** @@ -193,15 +231,6 @@ define([ Braintree.setup(); }, - /** - * Triggers when customer click "Continue to PayPal" button - */ - payWithPayPal: function () { - if (additionalValidators.validate()) { - Braintree.checkout.paypal.initAuthFlow(); - } - }, - /** * Get locale * @returns {String} @@ -224,11 +253,12 @@ define([ */ getPayPalConfig: function () { var totals = quote.totals(), - config = {}; + config = {}, + isActiveVaultEnabler = this.isActiveVault(); config.paypal = { container: 'paypal-container', - singleUse: true, + singleUse: !isActiveVaultEnabler, headless: true, amount: this.grandTotalAmount, currency: totals['base_currency_code'], @@ -296,12 +326,18 @@ define([ * @returns {Object} */ getData: function () { - return { + var data = { 'method': this.getCode(), 'additional_data': { 'payment_method_nonce': this.paymentMethodNonce } }; + + data['additional_data'] = _.extend(data['additional_data'], this.additionalData); + + this.vaultEnabler.visitAdditionalData(data); + + return data; }, /** @@ -313,12 +349,43 @@ define([ return window.checkoutConfig.payment[this.getCode()].paymentAcceptanceMarkSrc; }, + /** + * @returns {String} + */ + getVaultCode: function () { + return window.checkoutConfig.payment[this.getCode()].vaultCode; + }, + + /** + * Check if need to skip order review + * @returns {Boolean} + */ + isSkipOrderReview: function () { + return window.checkoutConfig.payment[this.getCode()].skipOrderReview; + }, + + /** + * Checks if vault is active + * @returns {Boolean} + */ + isActiveVault: function () { + return this.vaultEnabler.isVaultEnabled() && this.vaultEnabler.isActivePaymentTokenEnabler(); + }, + + /** + * Re-init PayPal Auth flow to use Vault + */ + onVaultPaymentTokenEnablerChange: function () { + this.clientConfig.paypal.singleUse = !this.isActiveVault(); + this.reInitPayPal(); + }, + /** * Disable submit button */ disableButton: function () { // stop any previous shown loaders - fullScreenLoader.stopLoader(); + fullScreenLoader.stopLoader(true); fullScreenLoader.startLoader(); $('[data-button="place"]').attr('disabled', 'disabled'); }, @@ -329,6 +396,31 @@ define([ enableButton: function () { $('[data-button="place"]').removeAttr('disabled'); fullScreenLoader.stopLoader(); + }, + + /** + * Triggers when customer click "Continue to PayPal" button + */ + payWithPayPal: function () { + if (additionalValidators.validate()) { + Braintree.checkout.paypal.initAuthFlow(); + } + }, + + /** + * Get button title + * @returns {String} + */ + getButtonTitle: function () { + return this.isSkipOrderReview() ? 'Pay with PayPal' : 'Continue to PayPal'; + }, + + /** + * Get button id + * @returns {String} + */ + getButtonId: function () { + return this.getCode() + (this.isSkipOrderReview() ? '_pay_with' : '_continue_to'); } }); }); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/vault.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/vault.js index 3c349afca754d..dc2b0bdf34128 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/vault.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/vault.js @@ -6,12 +6,11 @@ /*global define*/ define([ 'jquery', - 'Magento_Braintree/js/view/payment/method-renderer/cc-form', 'Magento_Vault/js/view/payment/method-renderer/vault', 'Magento_Braintree/js/view/payment/adapter', 'Magento_Ui/js/model/messageList', 'Magento_Checkout/js/model/full-screen-loader' -], function ($, Component, VaultComponent, Braintree, globalMessageList, fullScreenLoader) { +], function ($, VaultComponent, Braintree, globalMessageList, fullScreenLoader) { 'use strict'; return VaultComponent.extend({ @@ -53,7 +52,7 @@ define([ var self = this; /** - * Define on ready callback + * Define already callback */ Braintree.onReady = function () { self.getPaymentMethodNonce(); diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html index 9d1dda23c2d84..cd080c83ecfea 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html @@ -7,9 +7,9 @@
+ name="payment[method]" + class="radio" + data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()"/> @@ -109,8 +109,8 @@ -
- \ No newline at end of file + diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html index f392d21b04091..a674299994c06 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html @@ -21,20 +21,57 @@
-
+
-
+ +
+ + +
+ + + +
+
+
+ +
+ +
+ +
+
+
diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal/vault.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal/vault.html new file mode 100644 index 0000000000000..eef06f048cdc1 --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal/vault.html @@ -0,0 +1,47 @@ + +
+
+ + +
+ +
+ +
+ + + +
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js b/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js index ca9d6493a1674..9005015d2460e 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js @@ -24,7 +24,7 @@ define([ /** * Stop full page loader action * - * @param {Boolean} forceStop + * @param {Boolean} [forceStop] */ stopLoader: function (forceStop) { var $elem = $(containerId), diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/payment/method-group.js b/app/code/Magento/Checkout/view/frontend/web/js/model/payment/method-group.js new file mode 100644 index 0000000000000..4236a215d7359 --- /dev/null +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/payment/method-group.js @@ -0,0 +1,31 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'uiElement', + 'mage/translate' +], function (Element, $t) { + 'use strict'; + + var DEFAULT_GROUP_ALIAS = 'default'; + + return Element.extend({ + defaults: { + alias: DEFAULT_GROUP_ALIAS, + title: $t('Payment Method'), + sortOrder: 100, + displayArea: 'payment-methods-items-${ $.alias }' + }, + + /** + * Checks if group instance is default + * + * @returns {Boolean} + */ + isDefault: function () { + return this.alias === DEFAULT_GROUP_ALIAS; + } + }); +}); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js index 18f6b8479c229..918d305ee031b 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js @@ -10,14 +10,22 @@ define([ 'Magento_Checkout/js/model/payment/method-list', 'Magento_Checkout/js/model/payment/renderer-list', 'uiLayout', - 'Magento_Checkout/js/model/checkout-data-resolver' -], function (_, ko, utils, Component, paymentMethods, rendererList, layout, checkoutDataResolver) { + 'Magento_Checkout/js/model/checkout-data-resolver', + 'mage/translate', + 'uiRegistry' +], function (_, ko, utils, Component, paymentMethods, rendererList, layout, checkoutDataResolver, $t, registry) { 'use strict'; return Component.extend({ defaults: { template: 'Magento_Checkout/payment-methods/list', - visible: paymentMethods().length > 0 + visible: paymentMethods().length > 0, + configDefaultGroup: { + name: 'methodGroup', + component: 'Magento_Checkout/js/model/payment/method-group' + }, + paymentGroupsList: [], + defaultGroupTitle: $t('Select a new payment method') }, /** @@ -26,7 +34,7 @@ define([ * @returns {Component} Chainable. */ initialize: function () { - this._super().initChildren(); + this._super().initDefaulGroup().initChildren(); paymentMethods.subscribe( function (changes) { checkoutDataResolver.resolvePaymentMethod(); @@ -47,6 +55,27 @@ define([ return this; }, + /** @inheritdoc */ + initObservable: function () { + this._super(). + observe(['paymentGroupsList']); + + return this; + }, + + /** + * Creates default group + * + * @returns {Component} Chainable. + */ + initDefaulGroup: function() { + layout([ + this.configDefaultGroup + ]); + + return this; + }, + /** * Create renders for child payment methods. * @@ -77,7 +106,7 @@ define([ rendererTemplate = { parent: '${ $.$data.parentName }', name: '${ $.$data.name }', - displayArea: 'payment-method-items', + displayArea: payment.displayArea, component: payment.component }; rendererComponent = utils.template(rendererTemplate, templateData); @@ -95,49 +124,105 @@ define([ * @param {Object} paymentMethodData */ createRenderer: function (paymentMethodData) { - var isRendererForMethod = false; + var isRendererForMethod = false, + currentGroup; + + registry.get(this.configDefaultGroup.name, function (defaultGroup) { + _.each(rendererList(), function (renderer) { - _.find(rendererList(), function (renderer) { + if (renderer.hasOwnProperty('typeComparatorCallback') && + typeof renderer.typeComparatorCallback == 'function' + ) { + isRendererForMethod = renderer.typeComparatorCallback(renderer.type, paymentMethodData.method); + } else { + isRendererForMethod = renderer.type === paymentMethodData.method; + } - if (renderer.hasOwnProperty('typeComparatorCallback') && - typeof renderer.typeComparatorCallback == 'function' - ) { - isRendererForMethod = renderer.typeComparatorCallback(renderer.type, paymentMethodData.method); - } else { - isRendererForMethod = renderer.type === paymentMethodData.method; - } + if (isRendererForMethod) { + currentGroup = renderer.group ? renderer.group : defaultGroup; - if (isRendererForMethod) { - layout( - [ + this.collectPaymentGroups(currentGroup); + + layout([ this.createComponent( { config: renderer.config, component: renderer.component, name: renderer.type, method: paymentMethodData.method, - item: paymentMethodData + item: paymentMethodData, + displayArea: currentGroup.displayArea } - ) - ] - ); - } + )]); + } + }.bind(this)); }.bind(this)); }, + /** + * Collects unique groups of available payment methods + * + * @param {Object} group + */ + collectPaymentGroups: function (group) { + var groupsList = this.paymentGroupsList(), + isGroupExists = _.some(groupsList, function (existsGroup) { + return existsGroup.alias === group.alias; + }); + + if (!isGroupExists) { + groupsList.push(group); + groupsList = _.sortBy(groupsList, function (existsGroup) { + return existsGroup.sortOrder; + }); + this.paymentGroupsList(groupsList); + } + }, + + /** + * Returns payment group title + * + * @param {Object} group + * @returns {String} + */ + getGroupTitle: function (group) { + var title = group().title; + + if (group().isDefault() && this.paymentGroupsList().length > 1) { + title = this.defaultGroupTitle; + } + + return title + ':'; + }, + + /** + * Checks if at least one payment method available + * + * @returns {String} + */ + isPaymentMethodsAvailable: function () { + return _.some(this.paymentGroupsList(), function (group) { + return this.getRegion(group.displayArea)().length; + }, this); + }, + /** * Remove view renderer. * * @param {String} paymentMethodCode */ removeRenderer: function (paymentMethodCode) { - var items = this.getRegion('payment-method-items'); + var items; + + _.each(this.paymentGroupsList(), function (group) { + items = this.getRegion(group.displayArea); - _.find(items(), function (value) { - if (value.item.method.indexOf(paymentMethodCode) === 0) { - value.disposeSubscriptions(); - value.destroy(); - } + _.find(items(), function (value) { + if (value.item.method.indexOf(paymentMethodCode) === 0) { + value.disposeSubscriptions(); + value.destroy(); + } + }); }, this); } }); diff --git a/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html b/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html index 66769f37386d6..f6c41c62eeca6 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html @@ -4,9 +4,19 @@ * See COPYING.txt for license details. */ --> -
- - - +
+
+
+
+ +
+
+
-
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/payment.html b/app/code/Magento/Checkout/view/frontend/web/template/payment.html index 292ece611e6b6..46467839da3fb 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/payment.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/payment.html @@ -5,7 +5,6 @@ */ -->