PluginProbe
Packeta / 2.3.2
Packeta v2.3.2
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / src / Packetery / Module / Checkout / CheckoutStorage.php

CheckoutStorage.php in Packeta 2.3.2, at src/Packetery/Module/Checkout/CheckoutStorage.php

226 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare( strict_types=1 );
4
5 namespace Packetery\Module\Checkout;
6
7 use Packetery\Module\Framework\WcAdapter;
8 use Packetery\Module\Framework\WpAdapter;
9 use Packetery\Module\Order;
10 use Packetery\Module\Transients;
11 use Packetery\Nette\Http\Request;
12
13 use function is_array;
14
15 /**
16 * @phpstan-type CheckoutData array<string, array<string, mixed>>
17 */
18 class CheckoutStorage {
19
20 /**
21 * @var Request
22 */
23 private $httpRequest;
24
25 /**
26 * @var WpAdapter
27 */
28 private $wpAdapter;
29
30 /**
31 * @var WcAdapter
32 */
33 private $wcAdapter;
34
35 public function __construct(
36 Request $httpRequest,
37 WpAdapter $wpAdapter,
38 WcAdapter $wcAdapter
39 ) {
40 $this->httpRequest = $httpRequest;
41 $this->wpAdapter = $wpAdapter;
42 $this->wcAdapter = $wcAdapter;
43 }
44
45 /**
46 * @return mixed
47 */
48 public function getFromTransient() {
49 return $this->wpAdapter->getTransient( $this->getTransientNamePacketaCheckoutData() );
50 }
51
52 /**
53 * @param CheckoutData|array{} $savedData
54 */
55 public function setTransient( array $savedData ): void {
56 $this->wpAdapter->setTransient(
57 $this->getTransientNamePacketaCheckoutData(),
58 $savedData,
59 $this->getSessionExpiration()
60 );
61 }
62
63 public function deleteTransient(): void {
64 $this->wpAdapter->deleteTransient( $this->getTransientNamePacketaCheckoutData() );
65 }
66
67 /**
68 * Gets checkout POST data including stored pickup point if not present in the data.
69 *
70 * @param string $chosenShippingMethod Chosen shipping method id.
71 * @param int|null $orderId Id of order to be updated.
72 *
73 * @return array
74 */
75 public function getPostDataIncludingStoredData( string $chosenShippingMethod, ?int $orderId = null ): array {
76 $checkoutData = $this->httpRequest->getPost();
77 if ( $checkoutData !== null && ! is_array( $checkoutData ) ) {
78 $checkoutData = null;
79 }
80 $savedCheckoutData = $this->getFromTransient();
81
82 if (
83 ! isset( $savedCheckoutData[ $chosenShippingMethod ] ) &&
84 (
85 $checkoutData === null ||
86 ( is_array( $savedCheckoutData ) && count( $checkoutData ) === 0 )
87 )
88 ) {
89 $this->logInvalidOrderData( $chosenShippingMethod, $checkoutData, $savedCheckoutData, $orderId );
90
91 return [];
92 }
93
94 if (
95 ! is_array( $savedCheckoutData ) ||
96 ! isset( $savedCheckoutData[ $chosenShippingMethod ] ) ||
97 ! is_array( $savedCheckoutData[ $chosenShippingMethod ] )
98 ) {
99 return $checkoutData;
100 }
101
102 $savedCarrierData = $savedCheckoutData[ $chosenShippingMethod ];
103 if ( $this->isKeyPresentInSavedDataButNotInPostData( $checkoutData, $savedCarrierData, Order\Attribute::POINT_ID ) ) {
104 foreach ( Order\Attribute::$pickupPointAttributes as $attribute ) {
105 $checkoutData[ $attribute['name'] ] = $savedCarrierData[ $attribute['name'] ];
106 }
107 }
108
109 if ( $this->isKeyPresentInSavedDataButNotInPostData( $checkoutData, $savedCarrierData, Order\Attribute::ADDRESS_IS_VALIDATED ) ) {
110 foreach ( Order\Attribute::$homeDeliveryAttributes as $attribute ) {
111 $checkoutData[ $attribute['name'] ] = $savedCarrierData[ $attribute['name'] ];
112 }
113 }
114
115 if ( $this->isKeyPresentInSavedDataButNotInPostData( $checkoutData, $savedCarrierData, Order\Attribute::CAR_DELIVERY_ID ) ) {
116 foreach ( Order\Attribute::$carDeliveryAttributes as $attribute ) {
117 $checkoutData[ $attribute['name'] ] = $savedCarrierData[ $attribute['name'] ];
118 }
119 }
120
121 if ( $this->isKeyPresentInSavedDataButNotInPostData( $checkoutData, $savedCarrierData, Order\Attribute::CARRIER_ID ) ) {
122 $checkoutData[ Order\Attribute::CARRIER_ID ] = $savedCarrierData[ Order\Attribute::CARRIER_ID ];
123 }
124
125 return $checkoutData;
126 }
127
128 /**
129 * Gets name of transient for selected pickup point.
130 */
131 private function getTransientNamePacketaCheckoutData(): string {
132 if ( $this->wpAdapter->isUserLoggedIn() ) {
133 $token = $this->wpAdapter->getSessionToken();
134 } else {
135 $this->wcAdapter->initializeSession();
136 $token = $this->wcAdapter->sessionGetCustomerId();
137 }
138
139 return Transients::CHECKOUT_DATA_PREFIX . $token;
140 }
141
142 /**
143 * @param string $chosenShippingMethod
144 * @param array|null $checkoutData
145 * @param mixed $savedCheckoutData Data from transient.
146 * @param int|null $orderId
147 */
148 private function logInvalidOrderData( string $chosenShippingMethod, ?array $checkoutData, $savedCheckoutData, ?int $orderId ): void {
149 $wcLogger = $this->wcAdapter->getLogger();
150 $dataToLog = [
151 'chosenShippingMethod' => $chosenShippingMethod,
152 'checkoutData' => $checkoutData,
153 'savedCheckoutData' => $savedCheckoutData,
154 ];
155 if ( $orderId !== null ) {
156 $dataToLog['orderId'] = $orderId;
157 }
158 $wcLogger->warning(
159 sprintf(
160 'Data of the order to be validated or saved are not set: %s',
161 $this->wpAdapter->jsonEncode( $dataToLog )
162 ),
163 [ 'source' => 'packeta' ]
164 );
165 }
166
167 private function isKeyPresentInSavedDataButNotInPostData( array $checkoutData, array $savedCarrierData, string $key ): bool {
168 $isKeyMissingInCheckoutData = ! isset( $checkoutData[ $key ] ) || $checkoutData[ $key ] === '';
169 $isKeyPresentInSavedCarrierData = isset( $savedCarrierData[ $key ] ) && $savedCarrierData[ $key ] !== '';
170
171 return $isKeyMissingInCheckoutData && $isKeyPresentInSavedCarrierData;
172 }
173
174 public function migrateGuestSessionToUserSession( string $guestSessionId ): void {
175 $oldTransientId = Transients::CHECKOUT_DATA_PREFIX . $guestSessionId;
176 $oldTransientData = $this->wpAdapter->getTransient( $oldTransientId );
177 if ( $this->validateDataStructure( $oldTransientData ) ) {
178 /** @var CheckoutData|array{} $oldTransientData */
179 $this->setTransient( $oldTransientData );
180 }
181 if ( $oldTransientData !== false ) {
182 $this->wpAdapter->deleteTransient( $oldTransientId );
183 }
184 }
185
186 /**
187 * Validates if the provided data has the structure of CheckoutData.
188 *
189 * @param mixed $data
190 */
191 public function validateDataStructure( $data ): bool {
192 if ( ! is_array( $data ) || $data === [] ) {
193 return false;
194 }
195
196 foreach ( $data as $key => $value ) {
197 if ( ! is_string( $key ) ) {
198 return false;
199 }
200
201 if ( ! is_array( $value ) ) {
202 return false;
203 }
204
205 foreach ( $value as $nestedKey => $nestedValue ) {
206 if ( ! is_string( $nestedKey ) ) {
207 return false;
208 }
209 }
210 }
211
212 return true;
213 }
214
215 private function getSessionExpiration(): int {
216 $default = 2 * DAY_IN_SECONDS;
217
218 $wcSessionExpiration = $this->wpAdapter->applyFilters( 'wc_session_expiration', $default );
219 if ( is_numeric( $wcSessionExpiration ) && (int) $wcSessionExpiration > $default ) {
220 return (int) $wcSessionExpiration;
221 }
222
223 return $default;
224 }
225 }
226