PluginProbe
HyperPay Payments / trunk
HyperPay Payments vtrunk
6.6.0 trunk 1.7 1.8 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.1.0 2.2.0 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.5 4.0.0 4.0.2 All 34 releases
hyperpay-gateways / src / Traits / HyperpayExpressBlock.php

HyperpayExpressBlock.php in HyperPay Payments trunk, at src/Traits/HyperpayExpressBlock.php

451 lines 13.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Hyperpay\Gateways\Traits;
4 if ( ! defined( 'ABSPATH' ) ) exit;
5
6 use WC_Validation;
7 use Exception;
8 use Hyperpay\Gateways\Helpers\Http;
9
10 /**
11 * Express Payments Blocks integration
12 *
13 * @since 1.0.3
14 */
15 trait HyperpayExpressBlock
16 {
17
18 public function initialize()
19 {
20 add_action('wc_ajax_hyperpay_prepare_checkout', [$this, "initCheckout"]);
21 add_action('wc_ajax_hyperpay_update_method', [$this, 'ajax_update_shipping_method']);
22 add_action('wc_ajax_hyperpay_update_checkout', [$this, "init_shipping_options"]);
23 add_action('wc_ajax_hyperpay_process_checkout', [$this, 'process_checkout']);
24 }
25
26
27 public function initCheckout()
28 {
29 $data = [
30 'headers' => [
31 "Authorization" => "Bearer {$this->accessToken}"
32 ],
33 'body' => [
34 "entityId" => $this->entityId,
35 "paymentType" => $this->trans_type,
36 ]
37 ];
38
39
40 if ($this->testMode) {
41 $data['body']["testMode"] = $this->trans_mode;
42 }
43
44 if ($this->server_to_server) {
45 $data['body']['paymentBrand'] = $this->brands[0];
46 }
47
48
49 $response = wp_remote_post($this->token_url, $data);
50 $result = json_decode(wp_remote_retrieve_body($response), true);
51
52
53 if (is_wp_error($response)) {
54 $error = $response->get_error_message();
55 } elseif (wp_remote_retrieve_response_code($response) != 200) {
56 $error = $result['result']['description'] ?? "something wrong";
57 }
58
59 if (isset($error) || !\preg_match("/^(000\.200)/", $result['result']['code'] ?? '')) {
60 return wp_send_json_error($result['result']['description'] ?? $error);
61 }
62
63
64 return wp_send_json($result['id']);
65 }
66
67
68 public function init_shipping_options()
69 {
70
71 $shipping_inputs = filter_input_array(
72 INPUT_POST,
73 [
74 'countryCode' => FILTER_SANITIZE_STRING,
75 'administrativeArea' => FILTER_SANITIZE_STRING,
76 'postalCode' => FILTER_SANITIZE_STRING,
77 'locality' => FILTER_SANITIZE_STRING,
78 'address' => FILTER_SANITIZE_STRING,
79 'address_2' => FILTER_SANITIZE_STRING,
80 ]
81 );
82
83 $shipping_address = [
84 'country' => $shipping_inputs['countryCode'],
85 'state' => $shipping_inputs['administrativeArea'],
86 'postcode' => $shipping_inputs['postalCode'],
87 'city' => $shipping_inputs['locality'],
88 'address' => $shipping_inputs['address'],
89 'address_2' => $shipping_inputs['address_2'],
90 ];
91
92
93
94 $product_view_options = filter_input_array(INPUT_POST, ['is_product_page' => FILTER_SANITIZE_STRING]);
95 $should_show_itemized_view = !isset($product_view_options['is_product_page']) ? true : filter_var($product_view_options['is_product_page'], FILTER_VALIDATE_BOOLEAN);
96
97 $shipping_options = $this->get_shipping_options($shipping_address, $should_show_itemized_view);
98 return wp_send_json($shipping_options);
99 }
100
101 /**
102 * Update shipping method.
103 */
104 public function ajax_update_shipping_method()
105 {
106
107 if (!defined('WOOCOMMERCE_CART')) {
108 define('WOOCOMMERCE_CART', true);
109 }
110
111 $shipping_methods = filter_input(INPUT_POST, 'shipping_method', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
112 $this->update_shipping_method($shipping_methods);
113
114 WC()->cart->calculate_totals();
115
116 $product_view_options = filter_input_array(INPUT_POST, ['is_product_page' => FILTER_SANITIZE_STRING]);
117 $should_show_itemized_view = !isset($product_view_options['is_product_page']) ? true : filter_var($product_view_options['is_product_page'], FILTER_VALIDATE_BOOLEAN);
118
119 $data = [];
120 $data += $this->build_display_items($should_show_itemized_view);
121 $data['result'] = 'success';
122
123 wp_send_json($data);
124 }
125
126 /**
127 * Gets shipping options available for specified shipping address
128 *
129 * @param array $shipping_address Shipping address.
130 * @param boolean $itemized_display_items Indicates whether to show subtotals or itemized views.
131 *
132 * @return array Shipping options data.
133 * phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag
134 */
135 public function get_shipping_options($shipping_address, $itemized_display_items = false)
136 {
137 try {
138 // Set the shipping options.
139 $data = [];
140
141 // Remember current shipping method before resetting.
142 $chosen_shipping_methods = WC()->session->get('chosen_shipping_methods');
143 $this->calculate_shipping($shipping_address);
144
145 $packages = WC()->shipping->get_packages();
146 $shipping_rate_ids = [];
147
148 if (!empty($packages) && WC()->customer->has_calculated_shipping()) {
149 foreach ($packages as $package) {
150 if (empty($package['rates'])) {
151 throw new Exception(__('Unable to find shipping method for address.', 'hyperpay-gateways'));
152 }
153
154 foreach ($package['rates'] as $rate) {
155 if (in_array($rate->id, $shipping_rate_ids, true)) {
156 // The Payment Requests will try to load indefinitely if there are duplicate shipping
157 // option IDs.
158 throw new Exception(__('Unable to provide shipping options for Payment Requests.', 'hyperpay-gateways'));
159 }
160 $shipping_rate_ids[] = $rate->id;
161 $data['shipping_options'][] = [
162 'identifier' => $rate->id,
163 'label' => $rate->label,
164 'detail' => '',
165 'amount' => (float)($rate->cost),
166 ];
167 }
168 }
169 } else {
170 throw new Exception(__('Unable to find shipping method for address.', 'hyperpay-gateways'));
171 }
172
173 // The first shipping option is automatically applied on the client.
174 // Keep chosen shipping method by sorting shipping options if the method still available for new address.
175 // Fallback to the first available shipping method.
176 if (isset($data['shipping_options'][0])) {
177 if (isset($chosen_shipping_methods[0])) {
178 $chosen_method_id = $chosen_shipping_methods[0];
179 $compare_shipping_options = function ($a, $b) use ($chosen_method_id) {
180 if ($a['id'] === $chosen_method_id) {
181 return -1;
182 }
183
184 if ($b['id'] === $chosen_method_id) {
185 return 1;
186 }
187
188 return 0;
189 };
190 usort($data['shipping_options'], $compare_shipping_options);
191 }
192
193 $first_shipping_method_id = $data['shipping_options'][0]['id'];
194 $this->update_shipping_method([$first_shipping_method_id]);
195 }
196
197 WC()->cart->calculate_totals();
198
199 $data += $this->build_display_items($itemized_display_items);
200 $data['result'] = 'success';
201 } catch (Exception $e) {
202 $data += $this->build_display_items($itemized_display_items);
203 $data['result'] = $e->getMessage();
204 }
205
206 return $data;
207 }
208
209
210 /**
211 * Updates shipping method in WC session
212 *
213 * @param array $shipping_methods Array of selected shipping methods ids.
214 */
215 public function update_shipping_method($shipping_methods)
216 {
217 $chosen_shipping_methods = WC()->session->get('chosen_shipping_methods');
218
219 if (is_array($shipping_methods)) {
220 foreach ($shipping_methods as $i => $value) {
221 $chosen_shipping_methods[$i] = wc_clean($value);
222 }
223 }
224
225 WC()->session->set('chosen_shipping_methods', $chosen_shipping_methods);
226 }
227
228 /**
229 * Builds the line items to pass to Payment Request
230 *
231 * @since 3.1.0
232 * @version 4.0.0
233 */
234 protected function build_display_items($itemized_display_items = false)
235 {
236 if (!defined('WOOCOMMERCE_CART')) {
237 define('WOOCOMMERCE_CART', true);
238 }
239
240 $items = [];
241 $lines = [];
242 $subtotal = 0;
243 $discounts = 0;
244 $display_items = !apply_filters('hyperpay_wc_stripe_payment_request_hide_itemization', true) || $itemized_display_items;
245
246 foreach (WC()->cart->get_cart() as $cart_item) {
247 $subtotal += $cart_item['line_subtotal'];
248 $amount = $cart_item['line_subtotal'];
249 $quantity_label = 1 < $cart_item['quantity'] ? ' (x' . $cart_item['quantity'] . ')' : '';
250 $product_name = $cart_item['data']->get_name();
251
252 $lines[] = [
253 'label' => $product_name . $quantity_label,
254 'amount' => (float)($amount),
255 ];
256 }
257
258 if ($display_items) {
259 $items = array_merge($items, $lines);
260 } else {
261 // Default show only subtotal instead of itemization.
262
263 $items[] = [
264 'label' => 'Subtotal',
265 'amount' => (float)($subtotal),
266 ];
267 }
268
269 if (version_compare(WC_VERSION, '3.2', '<')) {
270 $discounts = wc_format_decimal(WC()->cart->get_cart_discount_total(), WC()->cart->dp);
271 } else {
272 $applied_coupons = array_values(WC()->cart->get_coupon_discount_totals());
273
274 foreach ($applied_coupons as $amount) {
275 $discounts += (float) $amount;
276 }
277 }
278
279 $discounts = wc_format_decimal($discounts, WC()->cart->dp);
280 $tax = wc_format_decimal(WC()->cart->tax_total + WC()->cart->shipping_tax_total, WC()->cart->dp);
281 $shipping = wc_format_decimal(WC()->cart->shipping_total, WC()->cart->dp);
282 $items_total = wc_format_decimal(WC()->cart->cart_contents_total, WC()->cart->dp) + $discounts;
283 $order_total = version_compare(WC_VERSION, '3.2', '<') ? wc_format_decimal($items_total + $tax + $shipping - $discounts, WC()->cart->dp) : WC()->cart->get_total(false);
284
285 if (wc_tax_enabled()) {
286 $items[] = array(
287 'label' => esc_html__( 'Tax', 'hyperpay-gateways' ),
288 'amount' => (float) ( $tax ),
289 );
290 }
291
292 if (WC()->cart->needs_shipping()) {
293 $items[] = array(
294 'label' => esc_html__( 'Shipping', 'hyperpay-gateways' ),
295 'amount' => (float) ( $shipping ),
296 );
297 }
298
299 if (WC()->cart->has_discount()) {
300 $items[] = array(
301 'label' => esc_html__( 'Discount', 'hyperpay-gateways' ),
302 'amount' => (float) ( $discounts ),
303 );
304 }
305
306 if (version_compare(WC_VERSION, '3.2', '<')) {
307 $cart_fees = WC()->cart->fees;
308 } else {
309 $cart_fees = WC()->cart->get_fees();
310 }
311
312 // Include fees and taxes as display items.
313 foreach ($cart_fees as $key => $fee) {
314 $items[] = [
315 'label' => $fee->name,
316 'amount' => (float)($fee->amount),
317 ];
318 }
319
320 return [
321 'displayItems' => $items,
322 'total' => [
323 'label' => "By HyperPay",
324 'amount' => max(0, apply_filters('woocommerce_stripe_calculated_total', (float)($order_total), $order_total, WC()->cart)),
325 'pending' => false,
326 ],
327 ];
328 }
329
330
331
332 /**
333 * Calculate and set shipping method.
334 *
335 * @param array $address Shipping address.
336 *
337 * @since 3.1.0
338 * @version 5.0.0
339 */
340 protected function calculate_shipping($address = [])
341 {
342 $country = $address['country'];
343 $state = $address['state'];
344 $postcode = $address['postcode'];
345 $city = $address['city'];
346 $address_1 = $address['address'];
347 $address_2 = $address['address_2'];
348
349 // Normalizes state to calculate shipping zones.
350
351 // Normalizes postal code in case of redacted data from Apple Pay.
352
353 WC()->shipping->reset_shipping();
354
355 if ($postcode && WC_Validation::is_postcode($postcode, $country)) {
356 $postcode = wc_format_postcode($postcode, $country);
357 }
358
359 if ($country) {
360 WC()->customer->set_location($country, $state, $postcode, $city);
361 WC()->customer->set_shipping_location($country, $state, $postcode, $city);
362 } else {
363 WC()->customer->set_billing_address_to_base();
364 WC()->customer->set_shipping_address_to_base();
365 }
366
367 WC()->customer->set_calculated_shipping(true);
368 WC()->customer->save();
369
370 $packages = [];
371
372 $packages[0]['contents'] = WC()->cart->get_cart();
373 $packages[0]['contents_cost'] = 0;
374 $packages[0]['applied_coupons'] = WC()->cart->applied_coupons;
375 $packages[0]['user']['ID'] = get_current_user_id();
376 $packages[0]['destination']['country'] = $country;
377 $packages[0]['destination']['state'] = $state;
378 $packages[0]['destination']['postcode'] = $postcode;
379 $packages[0]['destination']['city'] = $city;
380 $packages[0]['destination']['address'] = $address_1;
381 $packages[0]['destination']['address_2'] = $address_2;
382
383 foreach (WC()->cart->get_cart() as $item) {
384 if ($item['data']->needs_shipping()) {
385 if (isset($item['line_total'])) {
386 $packages[0]['contents_cost'] += $item['line_total'];
387 }
388 }
389 }
390
391 $packages = apply_filters('woocommerce_cart_shipping_packages', $packages);
392
393 WC()->shipping->calculate_shipping($packages);
394 }
395
396 /**
397 * Create order. Security is handled by WC.
398 *
399 * @since 3.1.0
400 * @version 5.1.0
401 */
402 public function process_checkout()
403 {
404 if (WC()->cart->is_empty()) {
405 wp_send_json_error(__('Empty cart', 'hyperpay-gateways'));
406 }
407
408 if (!isset($_POST['checkoutId']) || !isset($_POST['hyperpay_nonce']) || !wp_verify_nonce(sanitize_key(wp_unslash($_POST['hyperpay_nonce'])), 'hyperpay_process_checkout')) {
409 wp_send_json_error(__('Invalid request.', 'hyperpay-gateways'));
410 }
411
412 if (!defined('WOOCOMMERCE_CHECKOUT')) {
413 define('WOOCOMMERCE_CHECKOUT', true);
414 }
415
416
417 $this->legacy = false;
418
419 WC()->checkout()->process_checkout();
420
421 die(0);
422 }
423
424
425 public function process_payment($order_id)
426 {
427 // that mean the request comes from checkout blocks
428 if (isset($_POST['checkoutId'], $_POST['hyperpay_nonce']) && wp_verify_nonce(sanitize_key(wp_unslash($_POST['hyperpay_nonce'])), 'hyperpay_process_checkout')) {
429
430 $checkoutId = sanitize_text_field(wp_unslash($_POST['checkoutId']));
431 $url = $this->token_url . "/$checkoutId";
432
433
434 $checkout = $this->getCheckoutData($order_id);
435 $checkout['data']['body']['shopperResultUrl'] = $checkout['postBackURL'];
436
437 // HTTP Request to oppwa to get checkout
438 $response = Http::post($url, $checkout['data']);
439
440 if (!\preg_match("/^(000\.200)/", $response['result']['code'] ?? '')) {
441 return wp_send_json_error($response['result']['description'] ?? 'unable to update checkout');
442 }
443
444 return wp_send_json(["result" => "SUCCESS"]);
445 }
446
447 // legacy checkout
448 return parent::process_payment($order_id);
449 }
450 }
451