Skip to content

Commit 6e10311

Browse files
committed
Merge remote-tracking branch 'mainline/2.3-develop' into 2.3-develop-pr33
2 parents 1e672f7 + 9b2cefd commit 6e10311

File tree

14 files changed

+376
-103
lines changed

14 files changed

+376
-103
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Braintree\Gateway\Request;
7+
8+
use Magento\Payment\Gateway\Request\BuilderInterface;
9+
use Magento\Braintree\Gateway\SubjectReader;
10+
11+
/**
12+
* Class BillingAddressDataBuilder
13+
*/
14+
class BillingAddressDataBuilder implements BuilderInterface
15+
{
16+
/**
17+
* @var SubjectReader
18+
*/
19+
private $subjectReader;
20+
21+
/**
22+
* BillingAddress block name
23+
*/
24+
private const BILLING_ADDRESS = 'billing';
25+
26+
/**
27+
* The customer’s company. 255 character maximum.
28+
*/
29+
private const COMPANY = 'company';
30+
31+
/**
32+
* The first name value must be less than or equal to 255 characters.
33+
*/
34+
private const FIRST_NAME = 'firstName';
35+
36+
/**
37+
* The last name value must be less than or equal to 255 characters.
38+
*/
39+
private const LAST_NAME = 'lastName';
40+
41+
/**
42+
* The street address. Maximum 255 characters, and must contain at least 1 digit.
43+
* Required when AVS rules are configured to require street address.
44+
*/
45+
private const STREET_ADDRESS = 'streetAddress';
46+
47+
/**
48+
* The postal code. Postal code must be a string of 5 or 9 alphanumeric digits,
49+
* optionally separated by a dash or a space. Spaces, hyphens,
50+
* and all other special characters are ignored.
51+
*/
52+
private const POSTAL_CODE = 'postalCode';
53+
54+
/**
55+
* The ISO 3166-1 alpha-2 country code specified in an address.
56+
* The gateway only accepts specific alpha-2 values.
57+
*
58+
* @link https://developers.braintreepayments.com/reference/general/countries/php#list-of-countries
59+
*/
60+
private const COUNTRY_CODE = 'countryCodeAlpha2';
61+
62+
/**
63+
* The extended address information—such as apartment or suite number. 255 character maximum.
64+
*/
65+
private const EXTENDED_ADDRESS = 'extendedAddress';
66+
67+
/**
68+
* The locality/city. 255 character maximum.
69+
*/
70+
private const LOCALITY = 'locality';
71+
72+
/**
73+
* The state or province. For PayPal addresses, the region must be a 2-letter abbreviation;
74+
*/
75+
private const REGION = 'region';
76+
77+
/**
78+
* @param SubjectReader $subjectReader
79+
*/
80+
public function __construct(SubjectReader $subjectReader)
81+
{
82+
$this->subjectReader = $subjectReader;
83+
}
84+
85+
/**
86+
* @inheritdoc
87+
*/
88+
public function build(array $buildSubject)
89+
{
90+
$paymentDO = $this->subjectReader->readPayment($buildSubject);
91+
92+
$result = [];
93+
$order = $paymentDO->getOrder();
94+
95+
$billingAddress = $order->getBillingAddress();
96+
if ($billingAddress) {
97+
$result[self::BILLING_ADDRESS] = [
98+
self::REGION => $billingAddress->getRegionCode(),
99+
self::POSTAL_CODE => $billingAddress->getPostcode(),
100+
self::COUNTRY_CODE => $billingAddress->getCountryId(),
101+
self::FIRST_NAME => $billingAddress->getFirstname(),
102+
self::STREET_ADDRESS => $billingAddress->getStreetLine1(),
103+
self::LAST_NAME => $billingAddress->getLastname(),
104+
self::COMPANY => $billingAddress->getCompany(),
105+
self::EXTENDED_ADDRESS => $billingAddress->getStreetLine2(),
106+
self::LOCALITY => $billingAddress->getCity()
107+
];
108+
}
109+
110+
return $result;
111+
}
112+
}

app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ErrorCodeProviderTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ public function getErrorCodeDataProvider(): array
7474
'errors' => [],
7575
'transaction' => [
7676
'status' => 'processor_declined',
77-
'processorResponseCode' => '1000'
77+
'processorResponseCode' => '2059'
7878
],
79-
'expectedResult' => ['1000']
79+
'expectedResult' => ['2059']
8080
],
8181
[
8282
'errors' => [

app/code/Magento/Braintree/etc/braintree_error_mapping.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<message code="81723" translate="true">Cardholder name is too long.</message>
2222
<message code="81736" translate="true">CVV verification failed.</message>
2323
<message code="cvv" translate="true">CVV verification failed.</message>
24+
<message code="2059" translate="true">Address Verification Failed.</message>
2425
<message code="81737" translate="true">Postal code verification failed.</message>
2526
<message code="81750" translate="true">Credit card number is prohibited.</message>
2627
<message code="81801" translate="true">Addresses must have at least one field filled in.</message>

app/code/Magento/Braintree/etc/di.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@
381381
<item name="customer" xsi:type="string">Magento\Braintree\Gateway\Request\CustomerDataBuilder</item>
382382
<item name="payment" xsi:type="string">Magento\Braintree\Gateway\Request\PaymentDataBuilder</item>
383383
<item name="channel" xsi:type="string">Magento\Braintree\Gateway\Request\ChannelDataBuilder</item>
384-
<item name="address" xsi:type="string">Magento\Braintree\Gateway\Request\AddressDataBuilder</item>
384+
<item name="address" xsi:type="string">Magento\Braintree\Gateway\Request\BillingAddressDataBuilder</item>
385385
<item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item>
386386
<item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item>
387387
<item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item>

app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ define([
1717
'Magento_Vault/js/view/payment/vault-enabler',
1818
'Magento_Checkout/js/action/create-billing-address',
1919
'Magento_Braintree/js/view/payment/kount',
20-
'mage/translate'
20+
'mage/translate',
21+
'Magento_Ui/js/model/messageList'
2122
], function (
2223
$,
2324
_,
@@ -31,7 +32,8 @@ define([
3132
VaultEnabler,
3233
createBillingAddress,
3334
kount,
34-
$t
35+
$t,
36+
globalMessageList
3537
) {
3638
'use strict';
3739

@@ -415,6 +417,18 @@ define([
415417
*/
416418
onVaultPaymentTokenEnablerChange: function () {
417419
this.reInitPayPal();
420+
},
421+
422+
/**
423+
* Show error message
424+
*
425+
* @param {String} errorMessage
426+
* @private
427+
*/
428+
showError: function (errorMessage) {
429+
globalMessageList.addErrorMessage({
430+
message: errorMessage
431+
});
418432
}
419433
});
420434
});
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\ConfigurableProduct\Model\Plugin\Frontend;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Api\Data\ProductInterfaceFactory;
12+
use Magento\Catalog\Model\Category;
13+
use Magento\Catalog\Model\Product;
14+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
15+
use Magento\Customer\Model\Session;
16+
use Magento\Framework\Cache\FrontendInterface;
17+
use Magento\Framework\EntityManager\MetadataPool;
18+
use Magento\Framework\Serialize\SerializerInterface;
19+
20+
/**
21+
* Cache of used products for configurable product
22+
*
23+
* @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
24+
*/
25+
class UsedProductsCache
26+
{
27+
/**
28+
* @var MetadataPool
29+
*/
30+
private $metadataPool;
31+
32+
/**
33+
* @var FrontendInterface
34+
*/
35+
private $cache;
36+
37+
/**
38+
* @var SerializerInterface
39+
*/
40+
private $serializer;
41+
42+
/**
43+
* @var ProductInterfaceFactory
44+
*/
45+
private $productFactory;
46+
47+
/**
48+
* @var Session
49+
*/
50+
private $customerSession;
51+
52+
/**
53+
* @param MetadataPool $metadataPool
54+
* @param FrontendInterface $cache
55+
* @param SerializerInterface $serializer
56+
* @param ProductInterfaceFactory $productFactory
57+
* @param Session $customerSession
58+
*/
59+
public function __construct(
60+
MetadataPool $metadataPool,
61+
FrontendInterface $cache,
62+
SerializerInterface $serializer,
63+
ProductInterfaceFactory $productFactory,
64+
Session $customerSession
65+
) {
66+
$this->metadataPool = $metadataPool;
67+
$this->cache = $cache;
68+
$this->serializer = $serializer;
69+
$this->productFactory = $productFactory;
70+
$this->customerSession = $customerSession;
71+
}
72+
73+
/**
74+
* Retrieve used products for configurable product
75+
*
76+
* @param Configurable $subject
77+
* @param callable $proceed
78+
* @param Product $product
79+
* @param array|null $requiredAttributeIds
80+
* @return ProductInterface[]
81+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
82+
*/
83+
public function aroundGetUsedProducts(
84+
Configurable $subject,
85+
callable $proceed,
86+
$product,
87+
$requiredAttributeIds = null
88+
) {
89+
$cacheKey = $this->getCacheKey($product, $requiredAttributeIds);
90+
$usedProducts = $this->readUsedProductsCacheData($cacheKey);
91+
if ($usedProducts === null) {
92+
$usedProducts = $proceed($product, $requiredAttributeIds);
93+
$this->saveUsedProductsCacheData($product, $usedProducts, $cacheKey);
94+
}
95+
96+
return $usedProducts;
97+
}
98+
99+
/**
100+
* Generate cache key for product
101+
*
102+
* @param Product $product
103+
* @param array|null $requiredAttributeIds
104+
* @return string
105+
*/
106+
private function getCacheKey($product, $requiredAttributeIds = null): string
107+
{
108+
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
109+
$keyParts = [
110+
'getUsedProducts',
111+
$product->getData($metadata->getLinkField()),
112+
$product->getStoreId(),
113+
$this->customerSession->getCustomerGroupId(),
114+
];
115+
if ($requiredAttributeIds !== null) {
116+
sort($requiredAttributeIds);
117+
$keyParts[] = implode('', $requiredAttributeIds);
118+
}
119+
$cacheKey = sha1(implode('_', $keyParts));
120+
121+
return $cacheKey;
122+
}
123+
124+
/**
125+
* Read used products data from cache
126+
*
127+
* Looking for cache record stored under provided $cacheKey
128+
* In case data exists turns it into array of products
129+
*
130+
* @param string $cacheKey
131+
* @return ProductInterface[]|null
132+
*/
133+
private function readUsedProductsCacheData(string $cacheKey): ?array
134+
{
135+
$data = $this->cache->load($cacheKey);
136+
if (!$data) {
137+
return null;
138+
}
139+
140+
$items = $this->serializer->unserialize($data);
141+
if (!$items) {
142+
return null;
143+
}
144+
145+
$usedProducts = [];
146+
foreach ($items as $item) {
147+
/** @var Product $productItem */
148+
$productItem = $this->productFactory->create();
149+
$productItem->setData($item);
150+
$usedProducts[] = $productItem;
151+
}
152+
153+
return $usedProducts;
154+
}
155+
156+
/**
157+
* Save $subProducts to cache record identified with provided $cacheKey
158+
*
159+
* Cached data will be tagged with combined list of product tags and data specific tags i.e. 'price' etc.
160+
*
161+
* @param Product $product
162+
* @param ProductInterface[] $subProducts
163+
* @param string $cacheKey
164+
* @return bool
165+
*/
166+
private function saveUsedProductsCacheData(Product $product, array $subProducts, string $cacheKey): bool
167+
{
168+
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
169+
$data = $this->serializer->serialize(
170+
array_map(
171+
function ($item) {
172+
return $item->getData();
173+
},
174+
$subProducts
175+
)
176+
);
177+
$tags = array_merge(
178+
$product->getIdentities(),
179+
[
180+
Category::CACHE_TAG,
181+
Product::CACHE_TAG,
182+
'price',
183+
Configurable::TYPE_CODE . '_' . $product->getData($metadata->getLinkField()),
184+
]
185+
);
186+
$result = $this->cache->save($data, $cacheKey, $tags);
187+
188+
return (bool) $result;
189+
}
190+
}

0 commit comments

Comments
 (0)