PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.2.2
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.2.2
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.2 2.2.1 2.2.0 2.1.2 2.1.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 All 66 releases
better-payment / includes / API / AdminAPI.php

AdminAPI.php in Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More 2.2.2, at includes/API/AdminAPI.php

866 lines 30.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Better_Payment\Lite\API;
4
5 use Better_Payment\Lite\Admin\DB;
6 use Better_Payment\Lite\Traits\Helper;
7 use WP_Error;
8 use WP_REST_Controller;
9 use WP_REST_Server;
10
11 /**
12 * Exit if accessed directly
13 */
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 /**
19 * Admin REST API Controller
20 *
21 * @since 1.5.0
22 */
23 class AdminAPI extends WP_REST_Controller
24 {
25 use Helper;
26
27 /**
28 * Namespace
29 *
30 * @var string
31 */
32 protected $namespace = 'better-payment/v1';
33
34 /**
35 * Constructor
36 *
37 * @since 1.5.0
38 */
39 public function __construct()
40 {
41 add_action('rest_api_init', [$this, 'register_routes']);
42 }
43
44 /**
45 * Register REST API routes
46 *
47 * @since 1.5.0
48 */
49 public function register_routes()
50 {
51 register_rest_route($this->namespace, '/transactions', [
52 'methods' => WP_REST_Server::READABLE,
53 'callback' => [$this, 'get_transactions'],
54 'permission_callback' => [$this, 'check_admin_permissions']
55 ]);
56
57 register_rest_route($this->namespace, '/transactions/count', [
58 'methods' => WP_REST_Server::READABLE,
59 'callback' => [$this, 'get_transaction_count'],
60 'permission_callback' => [$this, 'check_admin_permissions']
61 ]);
62
63 register_rest_route($this->namespace, '/transactions/(?P<id>\d+)', [
64 'methods' => WP_REST_Server::DELETABLE,
65 'callback' => [$this, 'delete_transaction'],
66 'permission_callback' => [$this, 'check_admin_permissions']
67 ]);
68
69 register_rest_route($this->namespace, '/transactions/(?P<id>\d+)', [
70 'methods' => WP_REST_Server::READABLE,
71 'callback' => [$this, 'get_transaction'],
72 'permission_callback' => [$this, 'check_admin_permissions']
73 ]);
74
75 register_rest_route($this->namespace, '/transactions/(?P<id>\d+)/mark-complete', [
76 'methods' => WP_REST_Server::CREATABLE,
77 'callback' => [$this, 'mark_transaction_complete'],
78 'permission_callback' => [$this, 'check_admin_permissions']
79 ]);
80
81 register_rest_route($this->namespace, '/transactions/(?P<id>\d+)/referer', [
82 'methods' => WP_REST_Server::READABLE,
83 'callback' => [$this, 'get_transaction_referer'],
84 'permission_callback' => [$this, 'check_admin_permissions']
85 ]);
86
87 register_rest_route($this->namespace, '/settings', [
88 [
89 'methods' => WP_REST_Server::READABLE,
90 'callback' => [$this, 'get_settings'],
91 'permission_callback' => [$this, 'check_admin_permissions']
92 ],
93 [
94 'methods' => WP_REST_Server::CREATABLE,
95 'callback' => [$this, 'update_settings'],
96 'permission_callback' => [$this, 'check_admin_permissions']
97 ]
98 ]);
99
100 register_rest_route($this->namespace, '/dashboard/dismissible-section', [
101 'methods' => WP_REST_Server::READABLE,
102 'callback' => [$this, 'show_dismissible_section'],
103 'permission_callback' => [$this, 'check_admin_permissions']
104 ]);
105
106 register_rest_route($this->namespace, '/dashboard/dismissible-section-data', [
107 'methods' => WP_REST_Server::READABLE,
108 'callback' => [$this, 'get_dismissible_section_data'],
109 'permission_callback' => [$this, 'check_admin_permissions']
110 ]);
111
112 register_rest_route($this->namespace, '/dashboard/dismissible-section-dismiss', [
113 'methods' => WP_REST_Server::READABLE,
114 'callback' => [$this, 'dismiss_dismissible_section'],
115 'permission_callback' => [$this, 'check_admin_permissions']
116 ]);
117
118 register_rest_route($this->namespace, '/dashboard/sale-info-dismissed', [
119 'methods' => WP_REST_Server::READABLE,
120 'callback' => [$this, 'is_sale_info_dismissed'],
121 'permission_callback' => [$this, 'check_admin_permissions']
122 ]);
123
124 register_rest_route($this->namespace, '/dashboard/sale-info-dismiss', [
125 'methods' => WP_REST_Server::READABLE,
126 'callback' => [$this, 'dismiss_sale_info'],
127 'permission_callback' => [$this, 'check_admin_permissions']
128 ]);
129
130 register_rest_route($this->namespace, '/options', [
131 'methods' => WP_REST_Server::READABLE,
132 'callback' => [$this, 'get_options'],
133 'permission_callback' => [$this, 'check_admin_permissions']
134 ]);
135
136 // FluentCart product search
137 register_rest_route($this->namespace, '/fluentcart-products', [
138 'methods' => WP_REST_Server::READABLE,
139 'callback' => [$this, 'search_fluentcart_products'],
140 'permission_callback' => [$this, 'check_admin_permissions'],
141 'args' => [
142 'search' => [
143 'required' => false,
144 'type' => 'string',
145 'sanitize_callback' => 'sanitize_text_field'
146 ]
147 ]
148 ]);
149
150 // FluentCart single product
151 register_rest_route($this->namespace, '/fluentcart-product/(?P<id>\d+)', [
152 'methods' => WP_REST_Server::READABLE,
153 'callback' => [$this, 'get_fluentcart_product'],
154 'permission_callback' => [$this, 'check_admin_permissions'],
155 'args' => [
156 'id' => [
157 'required' => true,
158 'type' => 'integer',
159 'sanitize_callback' => 'absint'
160 ]
161 ]
162 ]);
163
164 // Stripe price details - fetches product name and price from Stripe API
165 register_rest_route($this->namespace, '/stripe-price', [
166 'methods' => WP_REST_Server::READABLE,
167 'callback' => [$this, 'get_stripe_price'],
168 'permission_callback' => [$this, 'check_admin_permissions'],
169 'args' => [
170 'price_id' => [
171 'required' => true,
172 'type' => 'string',
173 'sanitize_callback' => 'sanitize_text_field'
174 ]
175 ]
176 ]);
177 }
178
179 /**
180 * Get transaction count
181 *
182 * @since 1.5.0
183 * @param WP_REST_Request $request
184 * @return WP_REST_Response|WP_Error
185 */
186 public function get_transaction_count($request) {
187 if (!$this->bp_valid_nonce($request)) {
188 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
189 }
190
191 $all = DB::get_transaction_count();
192 $completed = DB::get_transaction_count('', 'v2', 0, 'completed');
193 $incomplete = DB::get_transaction_count('', 'v2', 1, 'incomplete');
194
195 return rest_ensure_response([
196 'success' => true,
197 'count' => [
198 'all' => $all,
199 'completed' => $completed,
200 'incomplete' => $incomplete
201 ]
202 ]);
203 }
204
205 public function get_options($request) {
206 if (!$this->bp_valid_nonce($request)) {
207 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
208 }
209
210 $option_name = sanitize_text_field($request->get_param('option_name'));
211 $options = get_option($option_name, true);
212 if (!$options) {
213 return rest_ensure_response("UTC");
214 }
215 return rest_ensure_response($options);
216 }
217
218 /**
219 * Check if sale info is dismissed
220 *
221 * @since 1.5.0
222 * @param WP_REST_Request $request
223 * @return WP_REST_Response|WP_Error
224 */
225 public function is_sale_info_dismissed($request) {
226 if (!$this->bp_valid_nonce($request)) {
227 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
228 }
229
230 $is_sale_info_dismissed = get_option('better_payment_sale_info_dismissed', false);
231
232 return rest_ensure_response([
233 'success' => true,
234 'isSaleInfoDismissed' => $is_sale_info_dismissed
235 ]);
236 }
237
238 /**
239 * Dismiss sale info
240 *
241 * @since 1.5.0
242 * @param WP_REST_Request $request
243 * @return WP_REST_Response|WP_Error
244 */
245 public function dismiss_sale_info($request) {
246 if (!$this->bp_valid_nonce($request)) {
247 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
248 }
249
250 $dismissed = update_option('better_payment_sale_info_dismissed', true);
251
252 return rest_ensure_response([
253 'success' => $dismissed,
254 'message' => __('Sale info dismissed', 'better-payment')
255 ]);
256 }
257
258 /**
259 * Dismiss dismissible section
260 *
261 * @since 1.5.0
262 * @param WP_REST_Request $request
263 * @return WP_REST_Response|WP_Error
264 */
265 public function dismiss_dismissible_section($request) {
266 if (!$this->bp_valid_nonce($request)) {
267 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
268 }
269
270 $dismissed = update_option('better_payment_progress_bar_dismissed', true);
271
272 return rest_ensure_response([
273 'success' => $dismissed,
274 'message' => __('Dismissible section dismissed', 'better-payment')
275 ]);
276 }
277
278 /**
279 * Validate nonce
280 *
281 * @since 1.5.0
282 * @param WP_REST_Request $request
283 * @return bool
284 */
285 private function bp_valid_nonce($request) {
286 $nonce = $request->get_header('x_wp_nonce');
287 if (!wp_verify_nonce($nonce, 'wp_rest')) {
288 return false;
289 }
290 return true;
291 }
292
293 /**
294 * Get dismissible section data
295 *
296 * @since 1.5.0
297 * @param WP_REST_Request $request
298 * @return WP_REST_Response|WP_Error
299 */
300 public function get_dismissible_section_data($request) {
301 if (!$this->bp_valid_nonce($request)) {
302 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
303 }
304
305 $settings = DB::get_settings();
306 $progress_steps['steps'] = $this->bp_calculate_progress_steps($settings);
307 $completed_steps = count( array_filter($progress_steps['steps'], function($step) { return $step['completed']; }) );
308 $total_steps = count($progress_steps['steps']);
309 $progress_steps['percentage'] = $total_steps > 0 ? ($completed_steps / $total_steps) * 100 : 0;
310
311 return rest_ensure_response([
312 'success' => true,
313 'data' => $progress_steps
314 ]);
315 }
316
317 /**
318 * Check if dismissible section should be shown
319 *
320 * @since 1.5.0
321 * @param WP_REST_Request $request
322 * @return WP_REST_Response|WP_Error
323 */
324 public function show_dismissible_section($request) {
325 if (!$this->bp_valid_nonce($request)) {
326 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
327 }
328
329 $show_dismissible_section = $this->bp_section_dismissed();
330
331 return rest_ensure_response([
332 'success' => true,
333 'sectionDismissed' => $show_dismissible_section
334 ]);
335 }
336
337 /**
338 * Check admin permissions
339 *
340 * @since 1.5.0
341 * @return bool
342 */
343 public function check_admin_permissions()
344 {
345 if (!current_user_can('manage_options')) {
346 return new WP_Error('unauthorized', 'Unauthorized', ['status' => 401]);
347 }
348 return true;
349 }
350
351 /**
352 * Get transactions with filters
353 *
354 * @since 1.5.0
355 * @param WP_REST_Request $request
356 * @return WP_REST_Response|WP_Error
357 */
358 public function get_transactions($request)
359 {
360 if (!$this->bp_valid_nonce($request)) {
361 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
362 }
363
364 try {
365 $paged = $request->get_param('paged') ? intval($request->get_param('paged')) : 1;
366 $per_page = $request->get_param('per_page') ? intval($request->get_param('per_page')) : 20;
367 $status = $request->get_param('status') ? sanitize_text_field($request->get_param('status')) : 'all';
368 $search_text = $request->get_param('search_text') ? sanitize_text_field($request->get_param('search_text')) : '';
369 $payment_date_from = $request->get_param('payment_date_from') ? sanitize_text_field($request->get_param('payment_date_from')) : '';
370 $payment_date_to = $request->get_param('payment_date_to') ? sanitize_text_field($request->get_param('payment_date_to')) : '';
371 $order_by = $request->get_param('order_by') ? sanitize_text_field($request->get_param('order_by')) : 'payment_date';
372 $order = $request->get_param('order') ? sanitize_text_field($request->get_param('order')) : 'DESC';
373 $source = $request->get_param('source') ? sanitize_text_field($request->get_param('source')) : '';
374 $currency = $request->get_param('currency') ? sanitize_text_field($request->get_param('currency')) : 'all';
375
376 $filters = [];
377 $filters['per_page'] = $per_page;
378 $filters['paged'] = $paged;
379 $filters['offset'] = ($paged - 1) * $per_page;
380 if ($status && $status !== 'all') {
381 $filters['status'] = $status;
382 }
383 if ($search_text) {
384 $filters['search_text'] = $search_text;
385 }
386 if ($payment_date_from) {
387 $filters['payment_date_from'] = $payment_date_from;
388 }
389 if ($payment_date_to) {
390 $filters['payment_date_to'] = $payment_date_to;
391 }
392 if ($order_by) {
393 $filters['order_by'] = $order_by;
394 }
395 if ($order) {
396 $filters['order'] = $order;
397 }
398 if ($source) {
399 $filters['source'] = $source;
400 }
401 if ($currency) {
402 $filters['currency'] = $currency;
403 }
404
405 $transactions = DB::get_transactions($filters, 0, 'v2');
406
407 foreach ($transactions as $key => $transaction) {
408 $transactions[$key]->form_fields_info = maybe_unserialize($transaction->form_fields_info);
409 }
410 unset($filters['offset']);
411 $total = DB::get_transaction_count($filters, 'v2', null, $status);
412
413 return rest_ensure_response([
414 'transactions' => $transactions,
415 'total' => $total,
416 'page' => $paged,
417 'per_page' => $per_page,
418 'pages' => ceil($total / $per_page)
419 ]);
420 } catch (\Exception $e) {
421 return new WP_Error('transactions_error', $e->getMessage(), ['status' => 500]);
422 }
423 }
424
425 /**
426 * Get single transaction
427 *
428 * @since 1.5.0
429 * @param WP_REST_Request $request
430 * @return WP_REST_Response|WP_Error
431 */
432 public function get_transaction($request)
433 {
434 if (!$this->bp_valid_nonce($request)) {
435 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
436 }
437
438 try {
439 $id = $request->get_param('id');
440 $transaction = DB::get_transaction($id);
441 $transaction->form_fields_info = maybe_unserialize($transaction->form_fields_info);
442 $woo_products = maybe_unserialize($transaction->form_fields_info['detailed_product_info'] ?? []);
443 $transaction->woo_products = $woo_products['woo_products'] ?? [];
444 $transaction->woo_products = array_values($transaction->woo_products);
445
446
447 if (!$transaction) {
448 return new WP_Error('transaction_not_found', 'Transaction not found', ['status' => 404]);
449 }
450
451 return rest_ensure_response($transaction);
452 } catch (\Exception $e) {
453 return new WP_Error('transaction_error', $e->getMessage(), ['status' => 500]);
454 }
455 }
456
457 /**
458 * Mark transaction as complete
459 *
460 * @since 1.5.0
461 * @param WP_REST_Request $request
462 * @return WP_REST_Response|WP_Error
463 */
464 public function mark_transaction_complete($request)
465 {
466 if (!$this->bp_valid_nonce($request)) {
467 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
468 }
469
470 try {
471 $id = intval($request->get_param('id'));
472
473 if (!$id) {
474 return new WP_Error('invalid_id', 'Invalid transaction ID', ['status' => 400]);
475 }
476
477 // Check if transaction exists
478 $transaction = DB::get_transaction($id);
479 if (!$transaction) {
480 return new WP_Error('transaction_not_found', 'Transaction not found', ['status' => 404]);
481 }
482
483 // Check if transaction is already completed
484 $completed_statuses = ['Completed', 'completed', 'Paid', 'paid', 'Success', 'success', 'Refunded', 'refunded', 'Failed', 'failed'];
485 if (in_array($transaction->status, $completed_statuses)) {
486 return new WP_Error('already_completed', 'Transaction is already in a completed state', ['status' => 400]);
487 }
488
489 // Mark transaction as completed
490 if (DB::mark_as_completed($id)) {
491 return rest_ensure_response([
492 'success' => true,
493 'message' => __('Transaction marked as completed!', 'better-payment')
494 ]);
495 } else {
496 return new WP_Error('mark_complete_failed', 'Failed to mark transaction as completed', ['status' => 500]);
497 }
498 } catch (\Exception $e) {
499 return new WP_Error('mark_complete_error', $e->getMessage(), ['status' => 500]);
500 }
501 }
502
503 /**
504 * Get transaction referer information
505 *
506 * @since 1.5.0
507 * @param WP_REST_Request $request
508 * @return WP_REST_Response|WP_Error
509 */
510 public function get_transaction_referer($request)
511 {
512 if (!$this->bp_valid_nonce($request)) {
513 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
514 }
515
516 try {
517 $id = intval($request->get_param('id'));
518
519 if (!$id) {
520 return new WP_Error('invalid_id', 'Invalid transaction ID', ['status' => 400]);
521 }
522
523 // Get transaction
524 $transaction = DB::get_transaction($id);
525 if (!$transaction) {
526 return new WP_Error('transaction_not_found', 'Transaction not found', ['status' => 404]);
527 }
528
529 // Extract form fields info
530 $form_fields_info = maybe_unserialize($transaction->form_fields_info);
531 $referer_page_id = !empty($form_fields_info['referer_page_id']) ? $form_fields_info['referer_page_id'] : '';
532 $referer_widget_id = !empty($form_fields_info['referer_widget_id']) ? $form_fields_info['referer_widget_id'] : '';
533
534 // Initialize response data
535 $referer_data = [
536 'referer_url' => $transaction->referer,
537 'page_title' => __('N/A', 'better-payment'),
538 'page_link' => '#',
539 'widget_name' => __('N/A', 'better-payment'),
540 'widget_settings' => [],
541 'site_logo' => '',
542 'site_name' => ''
543 ];
544
545 // Get widget settings if available
546 if (!empty($referer_page_id) && !empty($referer_widget_id)) {
547 $widget_settings = $this->get_elementor_widget_settings($referer_page_id, $referer_widget_id);
548
549 if (!empty($widget_settings)) {
550 // Get widget name from settings
551 $widget_name = !empty($widget_settings['form_name']) ? $widget_settings['form_name'] : __('N/A', 'better-payment');
552 $widget_name = ($transaction->referer !== 'elementor-form' && !empty($widget_settings['better_payment_form_title']))
553 ? $widget_settings['better_payment_form_title']
554 : $widget_name;
555
556 $referer_data['widget_name'] = $widget_name;
557 $referer_data['widget_settings'] = $widget_settings;
558 }
559 }
560
561 // Get page information if available
562 if (!empty($referer_page_id)) {
563 $referer_data['page_title'] = get_the_title($referer_page_id);
564 $referer_data['page_link'] = get_permalink($referer_page_id);
565 $referer_data['site_logo'] = wp_get_attachment_image_src(get_theme_mod('custom_logo'), 'full')[0];
566 $referer_data['site_name'] = get_bloginfo('name');;
567 }
568
569 return rest_ensure_response([
570 'success' => true,
571 'data' => $referer_data
572 ]);
573 } catch (\Exception $e) {
574 return new WP_Error('referer_error', $e->getMessage(), ['status' => 500]);
575 }
576 }
577
578 /**
579 * Delete transaction
580 *
581 * @since 1.5.0
582 * @param WP_REST_Request $request
583 * @return WP_REST_Response|WP_Error
584 */
585 public function delete_transaction($request)
586 {
587 if (!$this->bp_valid_nonce($request)) {
588 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
589 }
590
591 try {
592 $id = intval($request->get_param('id'));
593
594 if (!$id) {
595 return new WP_Error('invalid_id', 'Invalid transaction ID', ['status' => 400]);
596 }
597
598 // Check if transaction exists
599 $transaction = DB::get_transaction($id);
600 if (!$transaction) {
601 return new WP_Error('transaction_not_found', 'Transaction not found', ['status' => 404]);
602 }
603
604 // Delete the transaction
605 if (DB::delete_transaction($id)) {
606 return rest_ensure_response([
607 'success' => true,
608 'message' => __('Transaction deleted successfully', 'better-payment')
609 ]);
610 } else {
611 return new WP_Error('delete_failed', 'Failed to delete transaction', ['status' => 500]);
612 }
613 } catch (\Exception $e) {
614 return new WP_Error('delete_error', $e->getMessage(), ['status' => 500]);
615 }
616 }
617
618 /**
619 * Get settings
620 *
621 * @since 1.5.0
622 * @param WP_REST_Request $request
623 * @return WP_REST_Response|WP_Error
624 */
625 public function get_settings($request)
626 {
627 if (!$this->bp_valid_nonce($request)) {
628 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
629 }
630
631 try {
632 $settings = DB::get_settings();
633 return rest_ensure_response($settings);
634 } catch (\Exception $e) {
635 return new WP_Error('settings_error', $e->getMessage(), ['status' => 500]);
636 }
637 }
638
639 /**
640 * Update settings
641 *
642 * @since 1.5.0
643 * @param WP_REST_Request $request
644 * @return WP_REST_Response|WP_Error
645 */
646 public function update_settings($request)
647 {
648 if (!$this->bp_valid_nonce($request)) {
649 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
650 }
651
652 try {
653 $settings = $request->get_json_params();
654 $settings = $this->sanitize_settings($settings);
655 $result = DB::update_settings($settings);
656
657 return rest_ensure_response([
658 'success' => true,
659 'message' => __('Settings updated successfully', 'better-payment'),
660 'settings' => $result
661 ]);
662 } catch (\Exception $e) {
663 return new WP_Error('settings_update_error', $e->getMessage(), ['status' => 500]);
664 }
665 }
666
667 /**
668 * Search FluentCart products
669 *
670 * @since 1.5.0
671 * @param WP_REST_Request $request
672 * @return WP_REST_Response|WP_Error
673 */
674 public function search_fluentcart_products($request) {
675 $search = $request->get_param('search');
676
677 if (!function_exists('fluentCart') || !class_exists('\FluentCart\App\Models\Product')) {
678 return rest_ensure_response([]);
679 }
680
681 try {
682 $product_model = new \FluentCart\App\Models\Product();
683
684 $query = $product_model->newQuery()
685 ->where('post_status', 'publish')
686 ->limit(10);
687
688 if (!empty($search)) {
689 $query->where('post_title', 'LIKE', '%' . sanitize_text_field($search) . '%');
690 }
691
692 $products = $query->get();
693 $product_list = [];
694
695 if (!empty($products)) {
696 foreach ($products as $product) {
697 $product_list[] = [
698 'value' => strval($product->ID),
699 'label' => $product->post_title
700 ];
701 }
702 }
703
704 return rest_ensure_response($product_list);
705 } catch (\Exception $e) {
706 return rest_ensure_response([]);
707 }
708 }
709
710 /**
711 * Get single FluentCart product
712 *
713 * @since 1.5.0
714 * @param WP_REST_Request $request
715 * @return WP_REST_Response|WP_Error
716 */
717 public function get_fluentcart_product($request) {
718 $product_id = absint($request->get_param('id'));
719
720 if (!function_exists('fluentCart') || !class_exists('\FluentCart\App\Models\Product')) {
721 return new WP_Error('fluentcart_not_available', 'FluentCart is not available', ['status' => 404]);
722 }
723
724 try {
725 $product = \FluentCart\App\Models\Product::with('detail', 'variants')->find($product_id);
726
727 if (!$product) {
728 return new WP_Error('product_not_found', 'Product not found', ['status' => 404]);
729 }
730
731 $price = '';
732 if ($product->detail && $product->detail->min_price) {
733 $price = strval($product->detail->min_price);
734 } elseif ($product->variants && count($product->variants) > 0) {
735 $price = strval($product->variants[0]->item_price);
736 }
737
738 return rest_ensure_response([
739 'name' => $product->post_title,
740 'price' => $price,
741 'permalink' => get_permalink($product->ID) ?: ''
742 ]);
743 } catch (\Exception $e) {
744 return new WP_Error('product_error', $e->getMessage(), ['status' => 500]);
745 }
746 }
747
748 /**
749 * Get Stripe price details
750 *
751 * Fetches product name and price from Stripe API using the price ID.
752 *
753 * @since 1.5.0
754 * @param WP_REST_Request $request
755 * @return WP_REST_Response|WP_Error
756 */
757 public function get_stripe_price($request) {
758 if (!$this->bp_valid_nonce($request)) {
759 return new WP_Error('invalid_nonce', 'Invalid Request', ['status' => 403]);
760 }
761
762 $price_id = sanitize_text_field($request->get_param('price_id'));
763
764 // Validate price ID format (should start with 'price_')
765 if (empty($price_id) || strpos($price_id, 'price_') !== 0) {
766 return new WP_Error('invalid_price_id', 'Invalid Stripe Price ID format', ['status' => 400]);
767 }
768
769 // Get Stripe settings
770 $global_settings = get_option('better_payment_settings');
771 $is_live_mode = !empty($global_settings['better_payment_settings_payment_stripe_live_mode'])
772 && 'yes' === $global_settings['better_payment_settings_payment_stripe_live_mode'];
773
774 $secret_key = $is_live_mode
775 ? (!empty($global_settings['better_payment_settings_payment_stripe_live_secret'])
776 ? $global_settings['better_payment_settings_payment_stripe_live_secret'] : '')
777 : (!empty($global_settings['better_payment_settings_payment_stripe_test_secret'])
778 ? $global_settings['better_payment_settings_payment_stripe_test_secret'] : '');
779
780 if (empty($secret_key)) {
781 return new WP_Error('missing_api_key', 'Stripe API key not configured', ['status' => 400]);
782 }
783
784 // Fetch price details from Stripe using Helper trait method
785 $price_details = $this->get_stripe_price_details($price_id, $secret_key);
786
787 if (empty($price_details) || isset($price_details['error'])) {
788 $error_message = isset($price_details['error']['message'])
789 ? $price_details['error']['message']
790 : 'Failed to fetch Stripe price details';
791 return new WP_Error('stripe_api_error', $error_message, ['status' => 500]);
792 }
793
794 // Extract product name - need to fetch product details separately
795 $product_name = '';
796 if (!empty($price_details['product'])) {
797 $product_id = $price_details['product'];
798 $product_details = $this->get_stripe_product_details($product_id, $secret_key);
799 $product_name = !empty($product_details['name']) ? $product_details['name'] : '';
800 }
801
802 // Extract price amount and currency
803 $amount = isset($price_details['unit_amount']) ? floatval($price_details['unit_amount']) / 100 : 0;
804 $currency = isset($price_details['currency']) ? strtoupper($price_details['currency']) : 'USD';
805
806 return rest_ensure_response([
807 'success' => true,
808 'product_name' => $product_name,
809 'amount' => $amount,
810 'currency' => $currency,
811 'formatted_price' => $currency . ' ' . number_format($amount, 2)
812 ]);
813 }
814
815 /**
816 * Get Stripe product details
817 *
818 * @since 1.5.0
819 * @param string $product_id Stripe product ID
820 * @param string $secret_key Stripe secret key
821 * @return array Product details or empty array
822 */
823 private function get_stripe_product_details($product_id, $secret_key) {
824 if (empty($product_id) || empty($secret_key)) {
825 return [];
826 }
827
828 $api_url = 'https://api.stripe.com/v1/products/' . $product_id;
829
830 $response = wp_remote_get($api_url, [
831 'headers' => [
832 'Authorization' => 'Bearer ' . $secret_key,
833 ],
834 'timeout' => 20,
835 ]);
836
837 if (is_wp_error($response)) {
838 return [];
839 }
840
841 $body = wp_remote_retrieve_body($response);
842 $data = json_decode($body, true);
843
844 if (!isset($data) || !is_array($data)) {
845 return [];
846 }
847
848 return $data;
849 }
850
851 /**
852 * Sanitize settings
853 *
854 * @since 1.5.0
855 * @param array $settings
856 * @return array
857 */
858 private function sanitize_settings($settings)
859 {
860 foreach ($settings as $key => $value) {
861 $settings[$key] = sanitize_text_field($value);
862 }
863 return $settings;
864 }
865 }
866