This commit is contained in:
@@ -43,6 +43,10 @@ class InitiatePaymentAction
|
||||
throw PaymentInitiationNotAllowedException::notPendingPayment($booking);
|
||||
}
|
||||
|
||||
if ($booking->is_return_leg) {
|
||||
throw PaymentInitiationNotAllowedException::isReturnLeg($booking);
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($booking, $method) {
|
||||
$booking = Booking::whereKey($booking->id)->lockForUpdate()->first();
|
||||
|
||||
@@ -62,11 +66,12 @@ class InitiatePaymentAction
|
||||
}
|
||||
|
||||
$merchantOrderId = $this->merchantOrderId($booking);
|
||||
$amount = $this->amount($booking);
|
||||
|
||||
$result = $this->paymentService->initiate(new PaymentRequestData(
|
||||
bookingId: $booking->id,
|
||||
merchantOrderId: $merchantOrderId,
|
||||
amount: (string) $booking->price,
|
||||
amount: $amount,
|
||||
currency: self::CURRENCY,
|
||||
method: $method,
|
||||
notifyUrl: $this->notifyUrl($booking, $method),
|
||||
@@ -76,7 +81,7 @@ class InitiatePaymentAction
|
||||
'booking_id' => $booking->id,
|
||||
'gateway' => $method,
|
||||
'status' => $result->status,
|
||||
'amount' => $booking->price,
|
||||
'amount' => $amount,
|
||||
'currency' => self::CURRENCY,
|
||||
'gateway_transaction_id' => $result->gatewayTransactionId ?? $merchantOrderId,
|
||||
'gateway_payload' => $result->gatewayPayload,
|
||||
@@ -85,6 +90,20 @@ class InitiatePaymentAction
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A round trip's payment is combined on the outbound leg — covers both
|
||||
* legs' price, since the return leg never gets its own Payment
|
||||
* (domain.md §2b). A plain one-way booking just pays its own price.
|
||||
*/
|
||||
private function amount(Booking $booking): string
|
||||
{
|
||||
if ($booking->linked_booking_id === null) {
|
||||
return (string) $booking->price;
|
||||
}
|
||||
|
||||
return bcadd((string) $booking->price, (string) $booking->linkedBooking->price, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* A booking can have more than one payment attempt (retry after
|
||||
* failure), so the merchant order id must be unique per attempt, not
|
||||
|
||||
@@ -35,7 +35,14 @@ class RefundBookingAction
|
||||
throw RefundNotAllowedException::notConfirmed($booking);
|
||||
}
|
||||
|
||||
$payment = $booking->payments()->where('status', PaymentStatus::Completed->value)->latest()->first();
|
||||
// Round trip: payment is combined on the outbound leg, so a return
|
||||
// leg has no Payment of its own — refund against its linked leg's
|
||||
// Payment instead (domain.md §2b). The Confirmed check above still
|
||||
// applies to $booking itself, not the payment holder, so each leg
|
||||
// remains independently cancellable/refundable.
|
||||
$paymentBooking = $booking->is_return_leg ? ($booking->linkedBooking ?? $booking) : $booking;
|
||||
|
||||
$payment = $paymentBooking->payments()->where('status', PaymentStatus::Completed->value)->latest()->first();
|
||||
|
||||
if ($payment === null) {
|
||||
throw RefundNotAllowedException::noCompletedPayment($booking);
|
||||
@@ -45,9 +52,10 @@ class RefundBookingAction
|
||||
|
||||
$result = $this->paymentService->refund($payment->gateway, $payment->gateway_transaction_id, $amount, $reason);
|
||||
|
||||
$refund = DB::transaction(function () use ($payment, $amount, $reason, $result, $requestedBy) {
|
||||
$refund = DB::transaction(function () use ($booking, $payment, $amount, $reason, $result, $requestedBy) {
|
||||
$refund = Refund::create([
|
||||
'payment_id' => $payment->id,
|
||||
'booking_id' => $booking->id,
|
||||
'status' => $result->status,
|
||||
'amount' => $amount,
|
||||
'reason' => $reason,
|
||||
|
||||
Reference in New Issue
Block a user