Online Booking Payment Implementation
Online payment during booking solves two tasks: confirms customer intent and reduces no-show rate. Options: full prepayment, deposit (fixed amount or percentage), or payment after with saved card.
Payment Models
| Model | Description | Suitable For |
|---|---|---|
| Full prepayment | Entire amount at booking | Masterclasses, tours |
| Deposit | Fixed amount or % | Restaurants, salons |
| Card hold | Amount frozen, charged later | Rentals, leasing |
| Pay later | Card saved, payment after service | Regular customers |
Stripe Integration (Full Prepayment)
class BookingPaymentService
{
public function createPaymentIntent(Booking $booking): array
{
$stripe = new \Stripe\StripeClient(config('services.stripe.secret'));
$intent = $stripe->paymentIntents->create([
'amount' => $booking->total_cents, // in kopecks/cents
'currency' => 'rub',
'metadata' => [
'booking_id' => $booking->id,
'customer_id' => $booking->customer_id,
],
'automatic_payment_methods' => ['enabled' => true],
]);
$booking->update([
'payment_intent_id' => $intent->id,
'payment_status' => 'pending',
]);
return ['client_secret' => $intent->client_secret];
}
}
// Frontend: Stripe Elements
import { loadStripe } from '@stripe/stripe-js';
import { Elements, PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';
const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_KEY);
function BookingPaymentForm({ clientSecret }: { clientSecret: string }) {
const stripe = useStripe();
const elements = useElements();
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!stripe || !elements) return;
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: `${window.location.origin}/bookings/success`,
},
});
if (error) console.error(error.message);
}
return (
<form onSubmit={handleSubmit}>
<PaymentElement />
<button type="submit" className="btn-primary mt-4">Pay</button>
</form>
);
}
Payment Confirmation Webhook
Route::post('/webhooks/stripe', function (Request $request) {
$event = \Stripe\Webhook::constructEvent(
$request->getContent(),
$request->header('Stripe-Signature'),
config('services.stripe.webhook_secret')
);
if ($event->type === 'payment_intent.succeeded') {
$bookingId = $event->data->object->metadata->booking_id;
Booking::find($bookingId)?->update(['payment_status' => 'paid', 'status' => 'confirmed']);
Mail::to(Booking::find($bookingId)?->customer_email)
->send(new BookingConfirmedMail(Booking::find($bookingId)));
}
return response('ok');
});
Russian Payment Systems
For Russian market: YooKassa (Yandex), CloudPayments, Tinkoff Kassa, Sberbank Acquiring. Each has its own API, but pattern is same: payment session creation → provider form or embed → webhook confirmation.
Timeline
Payment on booking via YooKassa or Stripe with webhook confirmation: 4–6 business days.







