PluginProbe
Packeta / 2.0.9
Packeta v2.0.9
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.0.9, at src/Packetery/Module/Checkout/CheckoutStorage.php

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