PluginProbe
SuperFrete / 3.3.2
SuperFrete v3.3.2
trunk 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4
superfrete / app / Controllers / WebhookController.php

WebhookController.php in SuperFrete 3.3.2, at app/Controllers/WebhookController.php

450 lines 14.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SuperFrete_API\Controllers;
4
5 use SuperFrete_API\Http\WebhookVerifier;
6 use SuperFrete_API\Helpers\Logger;
7 use SuperFrete_API\Controllers\WebhookRetryManager;
8 use WP_REST_Server;
9 use WP_REST_Request;
10 use WP_REST_Response;
11 use WP_Error;
12
13 if (!defined('ABSPATH')) {
14 exit; // Security check
15 }
16
17 class WebhookController
18 {
19
20 public function __construct()
21 {
22 add_action('rest_api_init', [$this, 'register_routes']);
23 }
24
25 /**
26 * Register REST API routes for webhooks
27 */
28 public function register_routes()
29 {
30 register_rest_route('superfrete/v1', '/webhook', [
31 'methods' => WP_REST_Server::CREATABLE,
32 'callback' => [$this, 'handle_webhook'],
33 'permission_callback' => '__return_true', // We handle auth via signature verification
34 'args' => []
35 ]);
36
37 // Test endpoint for development
38 register_rest_route('superfrete/v1', '/webhook/test', [
39 'methods' => WP_REST_Server::READABLE,
40 'callback' => [$this, 'webhook_test'],
41 'permission_callback' => [$this, 'check_admin_permissions']
42 ]);
43 }
44
45 /**
46 * Handle incoming webhook from SuperFrete
47 *
48 * @param WP_REST_Request $request
49 * @return WP_REST_Response|WP_Error
50 */
51 public function handle_webhook(WP_REST_Request $request)
52 {
53 $start_time = microtime(true);
54 $webhook_id = wp_generate_uuid4();
55
56 Logger::log('SuperFrete', "Webhook received: ID $webhook_id");
57
58 try {
59 // Get request data
60 $payload = $request->get_json_params();
61 $headers = WebhookVerifier::get_webhook_headers();
62
63 // Log incoming webhook
64 $this->log_webhook_attempt($webhook_id, $payload, $headers, 'received');
65
66 // Validate payload structure
67 if (!WebhookVerifier::validate_payload_structure($payload)) {
68 return $this->webhook_error_response(
69 'Invalid payload structure',
70 400,
71 $webhook_id,
72 $payload,
73 $start_time
74 );
75 }
76
77 // Verify signature
78 $webhook_secret = get_option('superfrete_webhook_secret');
79 if (!$webhook_secret) {
80 return $this->webhook_error_response(
81 'Webhook secret not configured',
82 500,
83 $webhook_id,
84 $payload,
85 $start_time
86 );
87 }
88
89 $raw_body = $request->get_body();
90 if (!WebhookVerifier::verify_signature($raw_body, $headers['signature'], $webhook_secret)) {
91 return $this->webhook_error_response(
92 'Invalid signature',
93 401,
94 $webhook_id,
95 $payload,
96 $start_time
97 );
98 }
99
100 Logger::log('SuperFrete', "Webhook signature verified: ID $webhook_id");
101
102 // Process the webhook
103 $result = $this->process_webhook($payload, $webhook_id);
104
105 if ($result['success']) {
106 $this->log_webhook_success($webhook_id, $payload, $result, $start_time);
107
108 return new WP_REST_Response([
109 'status' => 'success',
110 'message' => 'Webhook processed successfully',
111 'webhook_id' => $webhook_id
112 ], 200);
113 } else {
114 // Queue for retry if processing failed
115 $this->queue_webhook_retry($payload, $result['error']);
116
117 return $this->webhook_error_response(
118 $result['error'],
119 500,
120 $webhook_id,
121 $payload,
122 $start_time
123 );
124 }
125
126 } catch (Exception $e) {
127 Logger::log('SuperFrete', "Webhook exception: ID $webhook_id - " . $e->getMessage());
128
129 // Queue for retry on exception
130 if (isset($payload)) {
131 $this->queue_webhook_retry($payload, $e->getMessage());
132 }
133
134 return $this->webhook_error_response(
135 'Internal server error',
136 500,
137 $webhook_id,
138 $payload ?? [],
139 $start_time
140 );
141 }
142 }
143
144 /**
145 * Process webhook event
146 *
147 * @param array $payload
148 * @param string $webhook_id
149 * @return array
150 */
151 private function process_webhook($payload, $webhook_id)
152 {
153 try {
154 $event_type = $payload['event'];
155 $superfrete_data = $payload['data'];
156 $superfrete_id = $superfrete_data['id'];
157
158 Logger::log('SuperFrete', "Processing $event_type for freight ID: $superfrete_id");
159
160 // Find order by SuperFrete ID
161 $order_id = $this->find_order_by_superfrete_id($superfrete_id);
162 if (!$order_id) {
163 return [
164 'success' => false,
165 'error' => "Order not found for SuperFrete ID: $superfrete_id"
166 ];
167 }
168
169 $order = wc_get_order($order_id);
170 if (!$order) {
171 return [
172 'success' => false,
173 'error' => "Invalid order ID: $order_id"
174 ];
175 }
176
177 Logger::log('SuperFrete', "Found order #$order_id for freight ID: $superfrete_id");
178
179 // Process based on event type
180 switch ($event_type) {
181 case 'order.posted':
182 return $this->handle_order_posted($order, $superfrete_data, $webhook_id);
183
184 case 'order.delivered':
185 return $this->handle_order_delivered($order, $superfrete_data, $webhook_id);
186
187 default:
188 return [
189 'success' => false,
190 'error' => "Unsupported event type: $event_type"
191 ];
192 }
193
194 } catch (Exception $e) {
195 return [
196 'success' => false,
197 'error' => $e->getMessage()
198 ];
199 }
200 }
201
202 /**
203 * Handle order.posted event
204 *
205 * @param WC_Order $order
206 * @param array $superfrete_data
207 * @param string $webhook_id
208 * @return array
209 */
210 private function handle_order_posted($order, $superfrete_data, $webhook_id)
211 {
212 try {
213 $order_id = $order->get_id();
214
215 // Update order status to shipped
216 $order->update_status('shipped', 'SuperFrete: Pedido postado nos Correios');
217
218 // Store tracking information
219 $tracking_code = $superfrete_data['tracking'] ?? '';
220 $tracking_url = $superfrete_data['tracking_url'] ?? '';
221 $posted_at = $superfrete_data['posted_at'] ?? '';
222
223 $order = wc_get_order($order_id);
224 if ($order) {
225 $order->update_meta_data('_superfrete_tracking_code', $tracking_code);
226 $order->update_meta_data('_superfrete_tracking_url', $tracking_url);
227 $order->update_meta_data('_superfrete_posted_at', $posted_at);
228 $order->update_meta_data('_superfrete_webhook_id', $webhook_id);
229 $order->save();
230 }
231
232 // Add order note
233 $note = sprintf(
234 'SuperFrete: Pedido postado nos Correios%s%s',
235 $tracking_code ? "\nCódigo de rastreamento: $tracking_code" : '',
236 $tracking_url ? "\nURL de rastreamento: $tracking_url" : ''
237 );
238 $order->add_order_note($note);
239
240 Logger::log('SuperFrete', "Order #$order_id status updated to shipped with tracking: $tracking_code");
241
242 return ['success' => true, 'message' => 'Order marked as shipped'];
243
244 } catch (Exception $e) {
245 return [
246 'success' => false,
247 'error' => "Failed to process order.posted: " . $e->getMessage()
248 ];
249 }
250 }
251
252 /**
253 * Handle order.delivered event
254 *
255 * @param WC_Order $order
256 * @param array $superfrete_data
257 * @param string $webhook_id
258 * @return array
259 */
260 private function handle_order_delivered($order, $superfrete_data, $webhook_id)
261 {
262 try {
263 $order_id = $order->get_id();
264
265 // Update order status to completed
266 $order->update_status('completed', 'SuperFrete: Pedido entregue');
267
268 // Store delivery information
269 $delivered_at = $superfrete_data['delivered_at'] ?? '';
270 $tracking_code = $superfrete_data['tracking'] ?? '';
271
272 $order = wc_get_order($order_id);
273 if ($order) {
274 $order->update_meta_data('_superfrete_delivered_at', $delivered_at);
275 $order->update_meta_data('_superfrete_delivery_webhook_id', $webhook_id);
276 $order->save();
277 }
278
279 // Add order note
280 $note = sprintf(
281 'SuperFrete: Pedido entregue%s%s',
282 $delivered_at ? "\nData de entrega: $delivered_at" : '',
283 $tracking_code ? "\nCódigo de rastreamento: $tracking_code" : ''
284 );
285 $order->add_order_note($note);
286
287 Logger::log('SuperFrete', "Order #$order_id marked as delivered");
288
289 return ['success' => true, 'message' => 'Order marked as delivered'];
290
291 } catch (Exception $e) {
292 return [
293 'success' => false,
294 'error' => "Failed to process order.delivered: " . $e->getMessage()
295 ];
296 }
297 }
298
299 /**
300 * Find order by SuperFrete ID
301 *
302 * @param string $superfrete_id
303 * @return int|false
304 */
305 private function find_order_by_superfrete_id($superfrete_id)
306 {
307 global $wpdb;
308
309 $order_id = $wpdb->get_var($wpdb->prepare(
310 "SELECT post_id FROM {$wpdb->postmeta}
311 WHERE meta_key = '_superfrete_id' AND meta_value = %s",
312 $superfrete_id
313 ));
314
315 return $order_id ? (int) $order_id : false;
316 }
317
318 /**
319 * Queue webhook for retry
320 *
321 * @param array $payload
322 * @param string $error_message
323 */
324 private function queue_webhook_retry($payload, $error_message)
325 {
326 if (!isset($payload['data']['id'])) {
327 return;
328 }
329
330 $order_id = $this->find_order_by_superfrete_id($payload['data']['id']);
331
332 // Use WebhookRetryManager to queue the retry
333 $retry_manager = new WebhookRetryManager();
334 $retry_manager->queue_retry(
335 $order_id ?: 0,
336 $payload['data']['id'],
337 $payload['event'],
338 $payload,
339 $error_message
340 );
341 }
342
343 /**
344 * Log webhook attempt
345 */
346 private function log_webhook_attempt($webhook_id, $payload, $headers, $status)
347 {
348 global $wpdb;
349
350 $table_name = $wpdb->prefix . 'superfrete_webhook_logs';
351
352 $order_id = null;
353 $superfrete_id = null;
354 $event_type = '';
355
356 if (isset($payload['data']['id'])) {
357 $superfrete_id = $payload['data']['id'];
358 $order_id = $this->find_order_by_superfrete_id($superfrete_id);
359 }
360
361 if (isset($payload['event'])) {
362 $event_type = $payload['event'];
363 }
364
365 $wpdb->insert($table_name, [
366 'webhook_id' => $webhook_id,
367 'order_id' => $order_id,
368 'superfrete_id' => $superfrete_id,
369 'event_type' => $event_type,
370 'status' => $status,
371 'payload' => wp_json_encode($payload),
372 'created_at' => current_time('mysql')
373 ]);
374 }
375
376 /**
377 * Log successful webhook processing
378 */
379 private function log_webhook_success($webhook_id, $payload, $result, $start_time)
380 {
381 global $wpdb;
382
383 $processing_time = round((microtime(true) - $start_time) * 1000);
384
385 $table_name = $wpdb->prefix . 'superfrete_webhook_logs';
386
387 $wpdb->update(
388 $table_name,
389 [
390 'status' => 'success',
391 'response_data' => wp_json_encode($result),
392 'processing_time_ms' => $processing_time
393 ],
394 ['webhook_id' => $webhook_id],
395 ['%s', '%s', '%d'],
396 ['%s']
397 );
398 }
399
400 /**
401 * Create error response and log it
402 */
403 private function webhook_error_response($message, $status_code, $webhook_id, $payload, $start_time)
404 {
405 global $wpdb;
406
407 $processing_time = round((microtime(true) - $start_time) * 1000);
408
409 $table_name = $wpdb->prefix . 'superfrete_webhook_logs';
410
411 $wpdb->update(
412 $table_name,
413 [
414 'status' => 'error',
415 'http_status_code' => $status_code,
416 'error_message' => $message,
417 'processing_time_ms' => $processing_time
418 ],
419 ['webhook_id' => $webhook_id],
420 ['%s', '%d', '%s', '%d'],
421 ['%s']
422 );
423
424 Logger::log('SuperFrete', "Webhook error: $message (ID: $webhook_id)");
425
426 return new WP_Error('webhook_error', $message, ['status' => $status_code]);
427 }
428
429 /**
430 * Webhook test endpoint
431 */
432 public function webhook_test(WP_REST_Request $request)
433 {
434 return new WP_REST_Response([
435 'status' => 'success',
436 'message' => 'SuperFrete webhook endpoint is working',
437 'timestamp' => current_time('mysql'),
438 'url' => rest_url('superfrete/v1/webhook')
439 ], 200);
440 }
441
442 /**
443 * Check admin permissions
444 */
445 public function check_admin_permissions()
446 {
447 return current_user_can('manage_woocommerce');
448 }
449 }
450