Maleku System’s payment layer uses Stripe Checkout for customer-facing payment pages and Stripe Connect for automated vendor payouts. When a customer completes checkout, Stripe routes the full amount to the platform account; the platform then transfers the vendor’s share (90%) directly to the vendor’s Connect Express account while retaining a 10% commission. Webhook events drive booking status updates, keeping the database in sync with Stripe’s payment lifecycle without polling.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/IvanchoDev89/maleku-system/llms.txt
Use this file to discover all available pages before exploring further.
The following environment variables are required for payments to function:
STRIPE_SECRET_KEY— your Stripe secret key (sk_live_...orsk_test_...)STRIPE_PUBLISHABLE_KEY— your Stripe publishable key (pk_live_...orpk_test_...)STRIPE_WEBHOOK_SECRET— the webhook signing secret from the Stripe dashboard or Stripe CLI (whsec_...)
GET /api/v1/stripe/config.Payment Flow
Customer Creates a Booking
The customer calls
POST /api/v1/bookings/property or POST /api/v1/bookings/tour. A booking record is created in PENDING status with a confirmation_code but no charge is made yet.Checkout Session Created
The frontend calls
POST /api/v1/stripe/checkout with the booking_id, a success_url, and a cancel_url. The backend calls create_checkout_session() from stripe_service.py, which builds a Stripe Checkout Session with line items and — when the vendor has a connected Stripe account — configures payment_intent_data.transfer_data to automatically split the payment.Customer Completes Payment on Stripe
The customer is redirected to Stripe’s hosted checkout page (
CheckoutResponse.checkout_url). Card details are entered entirely on Stripe’s infrastructure; Maleku System never handles raw card data.Stripe Calls the Webhook
On payment completion, Stripe sends a
checkout.session.completed event (or payment_intent.succeeded) to POST /api/v1/stripe/webhook. The signature is validated using STRIPE_WEBHOOK_SECRET before any processing occurs.Booking Status Updated to Confirmed
The webhook handler calls
update_booking_payment_status(), which sets booking.status = BookingStatus.CONFIRMED and stores the stripe_payment_intent_id. A payment receipt email is then dispatched to the customer.Vendor Receives Payout Minus Commission
If the vendor has a connected Stripe Express account (
vendor.stripe_connected = True), Stripe automatically executes the transfer defined in payment_intent_data.transfer_data, sending the vendor 90% of the booking total. Vendors without a Connect account are flagged as manual_payout: true in the payment intent metadata for manual reconciliation.Commission Model
The platform charges a 10% commission on every completed booking. The rate is set bySTRIPE_COMMISSION_RATE=0.10 in the backend environment and can be overridden per vendor via the commission_rate column on the Vendor model.
Webhook Handling
All incoming webhook events are received atPOST /api/v1/stripe/webhook.
Signature Validation
Every request must carry a Stripe-Signature header. The handler calls construct_webhook_event() from stripe_service.py, which wraps stripe.Webhook.construct_event() with the configured STRIPE_WEBHOOK_SECRET. Requests with an invalid or missing signature raise a 400 Bad Request.
Idempotency
To prevent replay attacks, the event_id from every processed event is stored in the ProcessedWebhook table. If the same event_id arrives again, the handler returns {"status": "already_processed"} without re-executing any business logic.
Handled Events
| Stripe Event | Action |
|---|---|
checkout.session.completed | Extracts client_reference_id (booking UUID) and payment_intent, calls update_booking_payment_status() → CONFIRMED, sends payment receipt email |
payment_intent.succeeded | Updates booking status to CONFIRMED, stores stripe_payment_intent_id |
payment_intent.payment_failed | Updates booking status to CANCELLED, logs the failure reason from last_payment_error |
charge.refunded | Looks up the booking by stripe_payment_intent_id, sets status to REFUNDED |
Refunds
Full or partial refunds are processed by therefund_payment() function in stripe_service.py:
POST /api/v1/stripe/bookings/{booking_id}/refund accepts an optional amount (defaults to the full booking total) and an optional reason. Only vendors and admins may issue refunds. After a full refund, the booking status is immediately set to REFUNDED; partial refunds leave the status unchanged.
Local Testing
Use the Stripe CLI to forward webhook events to your local backend during development:Vendor Connect Accounts
Vendors must complete Stripe Express onboarding before automatic payouts can be sent to them. Two functions instripe_service.py manage this lifecycle:
create_vendor_connect_account(vendor, refresh_url, return_url)
Creates a Stripe Express account for the vendor (country CR, business type individual, card payments and transfers enabled) and returns an onboarding_url that the vendor must visit to submit their KYC information.
get_connect_account_status(account_id)
Retrieves the account’s charges_enabled, payouts_enabled, details_submitted, and requirements fields. The GET /api/v1/stripe/vendor/connect endpoint calls this to determine if the vendor needs to complete additional verification steps before payouts are enabled.
Once charges_enabled and payouts_enabled are both True, the vendor’s stripe_connected flag is set to True in the database and future bookings will route payments automatically.