PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.8
Yatra – Travel Booking & Tour Operator Software v3.0.2.8
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
yatra / app / Services / PaymentService.php

PaymentService.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.8, at app/Services/PaymentService.php

327 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Services;
6
7 use Yatra\Repositories\PaymentRepository;
8 use Yatra\Repositories\BookingRepository;
9
10 /**
11 * Payment Service
12 *
13 * Contains business logic for payments.
14 *
15 * @package Yatra\Services
16 */
17 class PaymentService
18 {
19 private PaymentRepository $paymentRepository;
20 private BookingRepository $bookingRepository;
21
22 public function __construct()
23 {
24 $this->paymentRepository = new PaymentRepository();
25 $this->bookingRepository = new BookingRepository();
26 }
27
28 /**
29 * Get paginated payments
30 *
31 * @param array $filters Filters
32 * @return array
33 */
34 public function getPayments(array $filters = []): array
35 {
36 $result = $this->paymentRepository->paginate($filters);
37
38 $result['data'] = array_map([$this, 'formatPayment'], $result['data']);
39
40 return $result;
41 }
42
43 /**
44 * Get single payment
45 *
46 * @param int $id Payment ID
47 * @return array|null
48 */
49 public function getPayment(int $id): ?array
50 {
51 $payment = $this->paymentRepository->findWithBooking($id);
52
53 if (!$payment) {
54 return null;
55 }
56
57 return $this->formatPayment($payment);
58 }
59
60 /**
61 * Get payments for a booking
62 *
63 * @param int $bookingId Booking ID
64 * @return array
65 */
66 public function getBookingPayments(int $bookingId): array
67 {
68 $payments = $this->paymentRepository->findByBookingId($bookingId);
69
70 return array_map([$this, 'formatPayment'], $payments);
71 }
72
73 /**
74 * Create a new payment
75 *
76 * @param array $data Payment data
77 * @return array {success: bool, payment_id?: int, message: string}
78 */
79 public function createPayment(array $data): array
80 {
81 // Validate booking exists
82 $booking = $this->bookingRepository->find((int) $data['booking_id']);
83
84 if (!$booking) {
85 return ['success' => false, 'message' => __('Booking not found.', 'yatra')];
86 }
87
88 // Create payment
89 $paymentId = $this->paymentRepository->create($data);
90
91 if (!$paymentId) {
92 return ['success' => false, 'message' => __('Failed to create payment.', 'yatra')];
93 }
94
95 // Update booking amount paid
96 $totalPaid = $this->paymentRepository->getTotalPaidForBooking((int) $data['booking_id']);
97 $this->bookingRepository->updateAmountPaid((int) $data['booking_id'], $totalPaid);
98
99 return [
100 'success' => true,
101 'payment_id' => $paymentId,
102 'message' => __('Payment recorded successfully.', 'yatra'),
103 ];
104 }
105
106 /**
107 * Update a payment
108 *
109 * @param int $id Payment ID
110 * @param array $data Payment data
111 * @return array {success: bool, message: string}
112 */
113 public function updatePayment(int $id, array $data): array
114 {
115 $payment = $this->paymentRepository->find($id);
116
117 if (!$payment) {
118 return ['success' => false, 'message' => __('Payment not found.', 'yatra')];
119 }
120
121 $updated = $this->paymentRepository->update($id, $data);
122
123 if (!$updated) {
124 return ['success' => false, 'message' => __('Failed to update payment.', 'yatra')];
125 }
126
127 // Recalculate booking amount paid
128 $totalPaid = $this->paymentRepository->getTotalPaidForBooking((int) $payment->booking_id);
129 $this->bookingRepository->updateAmountPaid((int) $payment->booking_id, $totalPaid);
130
131 return [
132 'success' => true,
133 'message' => __('Payment updated successfully.', 'yatra'),
134 ];
135 }
136
137 /**
138 * Update payment status
139 *
140 * @param int $id Payment ID
141 * @param string $status New status
142 * @return array {success: bool, message: string}
143 */
144 public function updateStatus(int $id, string $status): array
145 {
146 $validStatuses = ['pending', 'completed', 'failed', 'refunded', 'cancelled'];
147
148 if (!in_array($status, $validStatuses, true)) {
149 return ['success' => false, 'message' => __('Invalid status.', 'yatra')];
150 }
151
152 $payment = $this->paymentRepository->find($id);
153
154 if (!$payment) {
155 return ['success' => false, 'message' => __('Payment not found.', 'yatra')];
156 }
157
158 $updated = $this->paymentRepository->updateStatus($id, $status);
159
160 if (!$updated) {
161 return ['success' => false, 'message' => __('Failed to update status.', 'yatra')];
162 }
163
164 // Recalculate booking amount paid
165 $totalPaid = $this->paymentRepository->getTotalPaidForBooking((int) $payment->booking_id);
166 $this->bookingRepository->updateAmountPaid((int) $payment->booking_id, $totalPaid);
167
168 return [
169 'success' => true,
170 'message' => sprintf(__('Payment status updated to %s.', 'yatra'), $status),
171 ];
172 }
173
174 /**
175 * Delete a payment
176 *
177 * @param int $id Payment ID
178 * @return array {success: bool, message: string}
179 */
180 public function deletePayment(int $id): array
181 {
182 $payment = $this->paymentRepository->find($id);
183
184 if (!$payment) {
185 return ['success' => false, 'message' => __('Payment not found.', 'yatra')];
186 }
187
188 $bookingId = (int) $payment->booking_id;
189
190 $deleted = $this->paymentRepository->delete($id);
191
192 if (!$deleted) {
193 return ['success' => false, 'message' => __('Failed to delete payment.', 'yatra')];
194 }
195
196 // Recalculate booking amount paid
197 $totalPaid = $this->paymentRepository->getTotalPaidForBooking($bookingId);
198 $this->bookingRepository->updateAmountPaid($bookingId, $totalPaid);
199
200 return [
201 'success' => true,
202 'message' => __('Payment deleted successfully.', 'yatra'),
203 ];
204 }
205
206 /**
207 * Get payment statistics
208 *
209 * @return array
210 */
211 public function getStats(): array
212 {
213 return $this->paymentRepository->getStats();
214 }
215
216 /**
217 * Admin toolbar: counts per payment status.
218 */
219 public function getAdminStatusCounts(): array
220 {
221 return $this->paymentRepository->getAdminStatusCounts();
222 }
223
224 /**
225 * Process refund
226 *
227 * @param int $paymentId Payment ID to refund
228 * @param float $amount Refund amount (optional, full refund if not provided)
229 * @param string $reason Refund reason
230 * @return array {success: bool, refund_id?: int, message: string}
231 */
232 public function processRefund(int $paymentId, ?float $amount = null, string $reason = ''): array
233 {
234 $payment = $this->paymentRepository->find($paymentId);
235
236 if (!$payment) {
237 return ['success' => false, 'message' => __('Payment not found.', 'yatra')];
238 }
239
240 if ($payment->status !== 'completed') {
241 return ['success' => false, 'message' => __('Only completed payments can be refunded.', 'yatra')];
242 }
243
244 $refundAmount = $amount ?? (float) $payment->amount;
245
246 if ($refundAmount > (float) $payment->amount) {
247 return ['success' => false, 'message' => __('Refund amount exceeds payment amount.', 'yatra')];
248 }
249
250 // Create refund record
251 $refundId = $this->paymentRepository->create([
252 'booking_id' => $payment->booking_id,
253 'gateway' => $payment->gateway,
254 'amount' => -$refundAmount, // Negative for refund
255 'currency' => $payment->currency,
256 'status' => 'completed',
257 'payment_type' => 'refund',
258 'notes' => $reason,
259 ]);
260
261 if (!$refundId) {
262 return ['success' => false, 'message' => __('Failed to process refund.', 'yatra')];
263 }
264
265 // Update original payment status
266 $this->paymentRepository->updateStatus($paymentId, 'refunded');
267
268 // Recalculate booking amount paid
269 $totalPaid = $this->paymentRepository->getTotalPaidForBooking((int) $payment->booking_id);
270 $this->bookingRepository->updateAmountPaid((int) $payment->booking_id, $totalPaid);
271
272 return [
273 'success' => true,
274 'refund_id' => $refundId,
275 'message' => __('Refund processed successfully.', 'yatra'),
276 ];
277 }
278
279 /**
280 * Format payment for API response
281 *
282 * @param object $payment Raw payment data
283 * @return array
284 */
285 private function formatPayment(object $payment): array
286 {
287 $contactName = isset($payment->contact_first_name)
288 ? trim($payment->contact_first_name . ' ' . ($payment->contact_last_name ?? ''))
289 : null;
290
291 $status = (string) ($payment->status ?? 'pending');
292 $gateway = (string) ($payment->gateway ?? '');
293 $bookingRef = $payment->booking_reference ?? null;
294 $processedAt = $payment->processed_at ?? null;
295 $createdAt = $payment->created_at ?? null;
296 $paymentDate = ($processedAt !== null && $processedAt !== '') ? $processedAt : ($createdAt ?? '');
297
298 return [
299 'id' => (int) $payment->id,
300 'booking_id' => (int) $payment->booking_id,
301 'booking_reference' => $bookingRef,
302 'booking_number' => ($bookingRef !== null && $bookingRef !== '')
303 ? (string) $bookingRef
304 : '#' . (int) ($payment->booking_id ?? 0),
305 'contact_email' => $payment->contact_email ?? null,
306 'contact_name' => $contactName,
307 'customer_name' => $contactName,
308 'customer_email' => $payment->contact_email ?? null,
309 'trip_title' => $payment->trip_title ?? null,
310 'transaction_id' => $payment->transaction_id,
311 'gateway' => $payment->gateway,
312 'payment_method' => $gateway,
313 'amount' => (float) $payment->amount,
314 'currency' => $payment->currency,
315 'status' => $payment->status,
316 'payment_status' => $status,
317 'payment_type' => $payment->payment_type,
318 'notes' => $payment->notes,
319 'processed_at' => $payment->processed_at,
320 'created_at' => $payment->created_at,
321 'payment_date' => $paymentDate,
322 'payment_number' => sprintf('PAY-%06d', (int) $payment->id),
323 ];
324 }
325 }
326
327