PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.4.1
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.4.1
2.4.1 2.4.0 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 All 68 releases
← All changes | includes/API/AdminAPI.php +47 -7 2.3.3 → 2.4.1 View file →
@@ -55,9 +55,17 @@
55 55 {
56 56 register_rest_route($this->namespace, '/transactions', [
57 57 'methods' => WP_REST_Server::READABLE,
58 58 'callback' => [$this, 'get_transactions'],
59 - 'permission_callback' => [$this, 'check_admin_permissions']
59 + 'permission_callback' => [$this, 'check_admin_permissions'],
60 + 'args' => [
61 + // A tab. Anything else is `all` (no `type`, so a non-string falls back too instead of a 400).
62 + 'status' => [
63 + 'required' => false,
64 + 'default' => 'all',
65 + 'sanitize_callback' => [__CLASS__, 'sanitize_transaction_status']
66 + ]
67 + ]
60 68 ]);
61 69
62 70 register_rest_route($this->namespace, '/subscriptions', [
63 71 'methods' => WP_REST_Server::READABLE,
@@ -388,8 +396,28 @@
388 396 return true;
389 397 }
390 398
391 399 /**
400 + * The `status` param of GET /transactions: a v2 tab (`all`, `completed`, `incomplete`, `refunded`).
401 + *
402 + * Anything else is `all`, as DB::get_transactions() reads an unknown status. An unknown value used
403 + * to reach the count query as a transaction type and fatal in array_map() over a string. Runs as
404 + * the route arg's sanitize_callback and again in the handler.
405 + *
406 + * @since 2.4.0
407 + *
408 + * @param mixed $value Raw param.
409 + * @return string
410 + */
411 + public static function sanitize_transaction_status($value): string
412 + {
413 + $status = is_string($value) ? sanitize_text_field($value) : '';
414 + $tabs = (new \Better_Payment\Lite\Classes\Helper())->get_transaction_types('v2');
415 +
416 + return in_array($status, $tabs, true) ? $status : 'all';
417 + }
418 +
419 + /**
392 420 * Get transactions with filters
393 421 *
394 422 * @since 1.5.0
395 423 * @param WP_REST_Request $request
@@ -403,9 +431,9 @@
403 431
404 432 try {
405 433 $paged = $request->get_param('paged') ? intval($request->get_param('paged')) : 1;
406 434 $per_page = $request->get_param('per_page') ? intval($request->get_param('per_page')) : 20;
407 - $status = $request->get_param('status') ? sanitize_text_field($request->get_param('status')) : 'all';
435 + $status = self::sanitize_transaction_status($request->get_param('status'));
408 436 $search_text = $request->get_param('search_text') ? sanitize_text_field($request->get_param('search_text')) : '';
409 437 $payment_date_from = $request->get_param('payment_date_from') ? sanitize_text_field($request->get_param('payment_date_from')) : '';
410 438 $payment_date_to = $request->get_param('payment_date_to') ? sanitize_text_field($request->get_param('payment_date_to')) : '';
411 439 $order_by = $request->get_param('order_by') ? sanitize_text_field($request->get_param('order_by')) : 'payment_date';
@@ -444,8 +472,11 @@
444 472
445 473 $transactions = DB::get_transactions($filters, 0, 'v2');
446 474
447 475 foreach ($transactions as $key => $transaction) {
476 + // The bucket the row is labelled under (an unconfirmed pre-2.4.0 free checkout is
477 + // `incomplete`). The React list prefers it to its own raw-status map.
478 + $transactions[$key]->status_type = \Better_Payment\Lite\Classes\Helper::transaction_status_type($transaction, 'v2');
448 479 $transactions[$key]->form_fields_info = maybe_unserialize($transaction->form_fields_info);
449 480 }
450 481 unset($filters['offset']);
451 482 $total = DB::get_transaction_count($filters, 'v2', null, $status);
@@ -960,18 +991,20 @@
960 991
961 992 try {
962 993 $id = $request->get_param('id');
963 994 $transaction = DB::get_transaction($id);
995 +
996 + if (!$transaction) {
997 + return new WP_Error('transaction_not_found', 'Transaction not found', ['status' => 404]);
998 + }
999 +
1000 + // From the stored row, before form_fields_info is unserialized (see get_transactions()).
1001 + $transaction->status_type = \Better_Payment\Lite\Classes\Helper::transaction_status_type($transaction, 'v2');
964 1002 $transaction->form_fields_info = maybe_unserialize($transaction->form_fields_info);
965 1003 $woo_products = maybe_unserialize($transaction->form_fields_info['detailed_product_info'] ?? []);
966 1004 $transaction->woo_products = $woo_products['woo_products'] ?? [];
967 1005 $transaction->woo_products = array_values($transaction->woo_products);
968 1006
969 -
970 - if (!$transaction) {
971 - return new WP_Error('transaction_not_found', 'Transaction not found', ['status' => 404]);
972 - }
973 -
974 1007 return rest_ensure_response($transaction);
975 1008 } catch (\Exception $e) {
976 1009 return new WP_Error('transaction_error', $e->getMessage(), ['status' => 500]);
977 1010 }
@@ -1000,8 +1033,15 @@
1000 1033 // Check if transaction exists
1001 1034 $transaction = DB::get_transaction($id);
1002 1035 if (!$transaction) {
1003 1036 return new WP_Error('transaction_not_found', 'Transaction not found', ['status' => 404]);
1037 + }
1038 +
1039 + // A pre-2.4.0 free Stripe checkout stores the form's full amount although nothing was
1040 + // charged, so `Completed` would count money that never arrived. Such rows are never
1041 + // rewritten; Helper's counting rule decides them (docs/features/transactions.md).
1042 + if (\Better_Payment\Lite\Classes\Helper::LEGACY_FREE_CHECKOUT_STATUS === $transaction->status) {
1043 + return new WP_Error('legacy_free_checkout', __('This free checkout cannot be marked as completed: its stored amount was never charged.', 'better-payment'), ['status' => 400]);
1004 1044 }
1005 1045
1006 1046 // Check if transaction is already completed
1007 1047 $completed_statuses = ['Completed', 'completed', 'Paid', 'paid', 'Success', 'success', 'Refunded', 'refunded', 'Failed', 'failed'];