PluginProbe ʕ •ᴥ•ʔ
Worldline Global Online Pay for WooCommerce / 2.5.22
Worldline Global Online Pay for WooCommerce v2.5.22
2.5.22 2.5.20 2.5.17 trunk 1.0.0 1.0.1 2.0.0 2.1.0 2.2.0 2.3.0 2.4.0 2.4.1 2.4.2 2.4.4 2.4.5 2.4.6 2.5.1 2.5.10 2.5.11 2.5.12 2.5.14 2.5.16 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9
worldline-for-woocommerce / src / Admin / CancelAuthorizationUi.php
worldline-for-woocommerce / src / Admin Last commit date
CancelAuthorizationUi.php 1 week ago CaptureAuthorizationUi.php 1 week ago
CancelAuthorizationUi.php
178 lines
1 <?php
2
3 declare (strict_types=1);
4 namespace Syde\Vendor\Worldline\Inpsyde\WorldlineForWoocommerce\Admin;
5
6 use Syde\Vendor\Worldline\Inpsyde\WorldlineForWoocommerce\WorldlinePaymentGateway\OrderMetaKeys;
7 use WC_Order;
8 class CancelAuthorizationUi
9 {
10 /**
11 * Registers hooks for rendering the cancel UI, loading assets and handling AJAX cancel requests.
12 */
13 public function register() : void
14 {
15 \add_action('woocommerce_order_item_add_action_buttons', [$this, 'add_cancel_button'], 20, 1);
16 \add_action('admin_footer', [$this, 'render_cancel_container_footer']);
17 \add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
18 \add_action('wp_ajax_worldline_cancel_authorization', [$this, 'handle_cancel_ajax']);
19 }
20 /**
21 * Handles the AJAX request for cancellation submission, validates the amount and triggers cancellation processing.
22 */
23 public function handle_cancel_ajax() : void
24 {
25 try {
26 $orderId = \absint($_POST['order_id'] ?? 0);
27 $amount = (float) \str_replace(',', '.', $_POST['amount'] ?? 0);
28 $order = \wc_get_order($orderId);
29 if (!$order) {
30 throw new \Exception('Order not found');
31 }
32 $amountCents = (int) \round($amount * 100);
33 $totals = $this->getAuthorizationTotals($order);
34 if ($amountCents <= 0 || $amountCents > $totals['available']) {
35 throw new \Exception(\__('Amount to cancel is not correct. Please provide a valid amount.', 'worldline-for-woocommerce'));
36 }
37 $gateways = \WC_Payment_Gateways::instance()->payment_gateways();
38 $gateway = $gateways[$order->get_payment_method()] ?? null;
39 if (!$gateway || !\method_exists($gateway, 'process_cancel')) {
40 throw new \Exception('Gateway does not support cancellation');
41 }
42 $isFinal = $amountCents === $totals['available'];
43 $gateway->process_cancel($orderId, $amount, $isFinal);
44 $order = \wc_get_order($orderId);
45 if (!$order) {
46 throw new \Exception('Order not found');
47 }
48 $totalsAfter = $this->getAuthorizationTotals($order);
49 \wp_send_json_success(['message' => \__('Cancellation submitted successfully.', 'worldline-for-woocommerce'), 'captured' => $totalsAfter['captured'], 'pendingCapture' => $totalsAfter['pending_capture'], 'cancelled' => $totalsAfter['cancelled'], 'available' => $totalsAfter['available'], 'capturedFormatted' => \wc_price($totalsAfter['captured'] / 100, ['currency' => $order->get_currency()]), 'pendingCaptureFormatted' => \wc_price($totalsAfter['pending_capture'] / 100, ['currency' => $order->get_currency()]), 'cancelledFormatted' => \wc_price($totalsAfter['cancelled'] / 100, ['currency' => $order->get_currency()]), 'availableFormatted' => \wc_price($totalsAfter['available'] / 100, ['currency' => $order->get_currency()])]);
50 } catch (\Throwable $e) {
51 \wp_send_json_error(['message' => $e->getMessage()]);
52 }
53 }
54 /**
55 * Renders the hidden cancel row container in the admin footer for the order edit screen.
56 */
57 public function render_cancel_container_footer() : void
58 {
59 if (!$this->is_order_edit_screen()) {
60 return;
61 }
62 $order = $this->get_current_order();
63 if (!$order) {
64 return;
65 }
66 $order = $this->get_current_order();
67 if (!$order) {
68 return;
69 }
70 echo '<div id="cancel_items_container" class="wc-order-data-row wc-order-cancel-items wc-order-data-row-toggle" style="display:none;">';
71 include $this->root_path('inc/admin-views/html-order-cancel.php');
72 echo '</div>';
73 }
74 /**
75 * Returns the currently opened WooCommerce order from the admin request.
76 */
77 private function get_current_order() : ?\WC_Order
78 {
79 $id = 0;
80 if (isset($_GET['id'])) {
81 $id = \absint($_GET['id']);
82 }
83 if (!$id && isset($_GET['post'])) {
84 $id = \absint($_GET['post']);
85 }
86 return $id ? \wc_get_order($id) : null;
87 }
88 /**
89 * Displays the Cancel button when the current order is eligible for cancellation actions.
90 */
91 public function add_cancel_button(WC_Order $order) : void
92 {
93 if (!$this->is_order_edit_screen()) {
94 return;
95 }
96 if (!$this->is_authorized_order($order)) {
97 return;
98 }
99 echo '<button type="button" class="button button-secondary do-cancel-items">' . \esc_html__('Cancel', 'worldline-for-woocommerce') . '</button>';
100 }
101 /**
102 * Enqueues admin JavaScript and CSS assets needed for the cancel UI.
103 */
104 public function enqueue_assets() : void
105 {
106 if (!$this->is_order_edit_screen()) {
107 return;
108 }
109 $main_plugin_file = $this->root_path('worldline-for-woocommerce.php');
110 $base = 'admin-actions-frontend-main';
111 $assetPath = $this->root_path("assets/{$base}.asset.php");
112 $asset = \file_exists($assetPath) ? require $assetPath : ['dependencies' => [], 'version' => '1.0.0'];
113 \wp_enqueue_script('worldline-admin-actions', \plugins_url("assets/{$base}.js", $main_plugin_file), $asset['dependencies'] ?? [], $asset['version'] ?? '1.0.0', \true);
114 $cssPath = $this->root_path("assets/{$base}.css");
115 if (\file_exists($cssPath)) {
116 \wp_enqueue_style('worldline-admin-actions', \plugins_url("assets/{$base}.css", $main_plugin_file), ['woocommerce_admin_styles'], $asset['version'] ?? '1.0.0');
117 }
118 }
119 /**
120 * Checks whether the current admin screen is a WooCommerce order edit screen.
121 */
122 private function is_order_edit_screen() : bool
123 {
124 if (!\function_exists('get_current_screen')) {
125 return \false;
126 }
127 $screen = \get_current_screen();
128 if (!$screen) {
129 return \false;
130 }
131 return \in_array($screen->id, ['shop_order', 'woocommerce_page_wc-orders'], \true);
132 }
133 /**
134 * Resolves an absolute path inside the plugin root.
135 */
136 private function root_path(string $rel) : string
137 {
138 return \dirname(__DIR__, 2) . '/' . \ltrim($rel, '/');
139 }
140 /**
141 * Determines whether the order is still in a state where cancellation actions should be available.
142 */
143 private function is_authorized_order(\WC_Order $order) : bool
144 {
145 $code = (string) $order->get_meta('_wlop_transaction_status_code', \true);
146 $paymentStatus = (string) $order->get_meta('_wlop_payment_status', \true);
147 $pendingCapture = (int) $order->get_meta(OrderMetaKeys::PAYMENT_PENDING_CAPTURE_AMOUNT, \true);
148 if ($pendingCapture > 0) {
149 return \true;
150 }
151 return $code === '5' || $code === '' && \strtoupper($paymentStatus) === 'AUTHORIZED';
152 }
153 /**
154 * Calculates authorization-related totals used for cancellation validation and UI display.
155 */
156 private function getAuthorizationTotals(\WC_Order $order) : array
157 {
158 $total = (int) \round((float) $order->get_total() * 100);
159 $captured = (int) $order->get_meta(OrderMetaKeys::PAYMENT_CAPTURED_AMOUNT, \true);
160 $cancelled = (int) $order->get_meta(OrderMetaKeys::PAYMENT_CANCELED_AMOUNT, \true);
161 $pendingCapture = (int) $order->get_meta(OrderMetaKeys::PAYMENT_PENDING_CAPTURE_AMOUNT, \true);
162 if ($total < 0) {
163 $total = 0;
164 }
165 if ($captured < 0) {
166 $captured = 0;
167 }
168 if ($cancelled < 0) {
169 $cancelled = 0;
170 }
171 if ($pendingCapture < 0) {
172 $pendingCapture = 0;
173 }
174 $available = \max(0, $total - $captured - $cancelled - $pendingCapture);
175 return ['total' => $total, 'captured' => $captured, 'cancelled' => $cancelled, 'pending_capture' => $pendingCapture, 'available' => $available];
176 }
177 }
178