PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
← All changes | app/Controllers/PaymentController.php +61 -14 3.0.3 → 3.0.16 View file →
@@ -38,60 +38,91 @@
38 38 * Register REST API routes
39 39 */
40 40 public function register_routes(): void
41 41 {
42 + // Payment stats — view cap (read-only aggregates).
42 43 register_rest_route($this->namespace, '/payments/stats', [
43 44 [
44 45 'methods' => 'GET',
45 46 'callback' => [$this, 'getPaymentStats'],
46 - 'permission_callback' => [$this, 'checkAdminPermission'],
47 + 'permission_callback' => [$this, 'checkCanView'],
47 48 ],
48 49 ]);
49 50
50 - // List all payments
51 + // List + create payments. Create needs the edit-bookings cap
52 + // because adding a payment mutates the booking's payment state.
51 53 register_rest_route($this->namespace, '/payments', [
52 54 [
53 55 'methods' => 'GET',
54 56 'callback' => [$this, 'getPayments'],
55 - 'permission_callback' => [$this, 'checkAdminPermission'],
57 + 'permission_callback' => [$this, 'checkCanView'],
56 58 ],
57 59 [
58 60 'methods' => 'POST',
59 61 'callback' => [$this, 'createPayment'],
60 - 'permission_callback' => [$this, 'checkAdminPermission'],
62 + 'permission_callback' => [$this, 'checkCanEdit'],
61 63 ]
62 64 ]);
63 65
64 - // Get single payment
66 + // Single-payment read / update / delete. Update + delete are
67 + // refund-equivalent operations from the customer's perspective
68 + // (changing the amount or removing a recorded payment can
69 + // affect what the customer owes), so we gate them on the
70 + // dedicated refund cap. Accountant role holds refund without
71 + // holding edit-bookings, so they can issue refunds without
72 + // also being able to edit the underlying booking.
65 73 register_rest_route($this->namespace, '/payments/(?P<id>\d+)', [
66 74 [
67 75 'methods' => 'GET',
68 76 'callback' => [$this, 'getPayment'],
69 - 'permission_callback' => [$this, 'checkAdminPermission'],
77 + 'permission_callback' => [$this, 'checkCanView'],
70 78 ],
71 79 [
72 80 'methods' => 'PUT',
73 81 'callback' => [$this, 'updatePayment'],
74 - 'permission_callback' => [$this, 'checkAdminPermission'],
82 + 'permission_callback' => [$this, 'checkCanRefund'],
75 83 ],
76 84 [
77 85 'methods' => 'DELETE',
78 86 'callback' => [$this, 'deletePayment'],
79 - 'permission_callback' => [$this, 'checkAdminPermission'],
87 + 'permission_callback' => [$this, 'checkCanRefund'],
80 88 ]
81 89 ]);
82 90 }
83 91
84 92 /**
85 - * Check admin permission (align with bookings list — shop managers may only have yatra caps).
93 + * Granular permission checks. WP administrators pass every cap
94 + * via the Team module's admin-fallback filter, so an explicit
95 + * `manage_options` check isn't needed at this layer.
86 96 */
97 + public function checkCanView(): bool
98 + {
99 + return current_user_can('yatra_view_bookings');
100 + }
101 +
102 + public function checkCanEdit(): bool
103 + {
104 + return current_user_can('yatra_edit_bookings');
105 + }
106 +
107 + public function checkCanRefund(): bool
108 + {
109 + // Refund cap is high-sensitivity. Held by Owner + Manager +
110 + // Accountant by default. Sales Agent / Front Desk / Guide
111 + // can record payments via the create endpoint above but
112 + // cannot modify or delete existing ones.
113 + return current_user_can('yatra_refund_bookings');
114 + }
115 +
116 + /**
117 + * @deprecated Kept for any external code referencing the old
118 + * method name. Routes to view — safer than the old "view OR
119 + * manage_options" shorthand, and admin users still pass via
120 + * the admin-fallback layer.
121 + */
87 122 public function checkAdminPermission(): bool
88 123 {
89 - if (current_user_can('yatra_view_bookings')) {
90 - return true;
91 - }
92 -
93 - return current_user_can('manage_options');
124 + return $this->checkCanView();
94 125 }
95 126
96 127 /**
97 128 * GET /payments/stats - Counts per status for admin toolbar
@@ -116,8 +147,12 @@
116 147 'gateway' => $request->get_param('gateway') ?: '',
117 148 'search' => $request->get_param('search') ?: '',
118 149 'date_from' => $request->get_param('date_from') ?: '',
119 150 'date_to' => $request->get_param('date_to') ?: '',
151 + // Column sorting from the table headers. Both are validated against a
152 + // whitelist in the repository — never interpolated raw into SQL.
153 + 'orderby' => $request->get_param('orderby') ?: '',
154 + 'order' => $request->get_param('order') ?: '',
120 155 ];
121 156
122 157 $result = $this->paymentService->getPayments($filters);
123 158
@@ -153,8 +188,16 @@
153 188 $data = $request->get_json_params();
154 189
155 190 try {
156 191 $payment = $this->paymentService->createPayment($data);
192 +
193 + // The service reports validation failures (unknown booking, invalid
194 + // status) as ['success' => false]; answering 201 made the admin form
195 + // redirect as if the payment had been saved.
196 + if (is_array($payment) && isset($payment['success']) && !$payment['success']) {
197 + return new WP_REST_Response($payment, 400);
198 + }
199 +
157 200 return new WP_REST_Response($payment, 201);
158 201 } catch (\Exception $e) {
159 202 return new WP_Error('payment_creation_failed', $e->getMessage(), ['status' => 400]);
160 203 }
@@ -172,8 +215,12 @@
172 215 $payment = $this->paymentService->updatePayment($id, $data);
173 216
174 217 if (!$payment) {
175 218 return new WP_Error('payment_not_found', 'Payment not found', ['status' => 404]);
219 + }
220 +
221 + if (is_array($payment) && isset($payment['success']) && !$payment['success']) {
222 + return new WP_REST_Response($payment, 400);
176 223 }
177 224
178 225 return new WP_REST_Response($payment, 200);
179 226 } catch (\Exception $e) {