Payments
Bring your own provider keys
All third-party integrations use BYO (Bring Your Own) credentials. No keys are bundled; Calbok never touches your providers' APIs using Author or Operator-level credentials on behalf of tenants.
Stripe booking deposits (per business)
Tenants bring their own Stripe keys. The Operator does not configure payment keys
in .env. Each tenant enters their Stripe publishable + secret keys in
Tenant → Settings → Payments.
In the Stripe Dashboard (test or live), create a webhook endpoint at:
https://your-domain.com/webhooks/booking/{tenant}/stripe/test
(or /stripe/live for live mode). Subscribe to payment-intent events your
gateway driver documents. Paste the signing secret into the tenant's gateway row.
The Diagnostics → Payment gateway configuration probe:
- FAIL if the
payment_gatewaystable is absent (run migrations). - WARN if no tenants have configured a gateway yet (expected on a fresh install).
- OK once at least one tenant has activated a gateway.
Separate money flows: Booking deposits go straight to each business's own payment account (Stripe, PayPal, and the other gateways below). Your Calbok subscription billing (what tenants pay you) uses different keys and webhooks — never mix the two.
PayPal sandbox booking deposits (per business)
Tenants can also take deposits via PayPal (Orders v2, capture on return). Like
Stripe, keys are BYO per tenant — never Operator .env.
Sandbox setup (recommended before going live):
- Create a REST app in the PayPal Developer Dashboard (Sandbox → Apps & Credentials).
- Copy the Client ID and Secret for the Sandbox app.
- Under the app, create a Webhook pointing at:
https://your-domain.com/webhooks/booking/{tenant}/paypal/testSubscribe at least to:CHECKOUT.ORDER.APPROVED,PAYMENT.CAPTURE.COMPLETED,PAYMENT.CAPTURE.DENIED,PAYMENT.CAPTURE.REFUNDED,PAYMENT.CAPTURE.REVERSED. - Copy the webhook Webhook ID.
- In Tenant → Settings → Payments, choose PayPal, mode test, paste Client ID / Secret / Webhook ID, set the account currency, and Save. Calbok verifies OAuth (and the webhook id when provided) before activating the row.
- Book a deposit-required appointment on the public booking page. When PayPal is
the chosen method, the customer is redirected to PayPal sandbox; after approval
they return to
/{tenant}/booking/payment/return/paypalwhere Calbok captures the order server-side (the query string is never trusted for status).
Live mode: switch mode to live, use Live credentials + a Live webhook URL
ending in /paypal/live, and re-save.
Razorpay test-mode booking deposits (per business)
Tenants in India (and accounts with international currencies enabled) can take
deposits via Razorpay (Orders API + Checkout). Keys are BYO per tenant — never
Operator .env. Calbok talks to Razorpay over plain HTTPS; do not install a
Razorpay SDK.
Test-mode setup:
- In the Razorpay Dashboard, switch to Test Mode
and open Account & Settings → API Keys. Generate a test key. The Key ID starts
with
rzp_test_(live keys start withrzp_live_and must be saved in live mode). - Under Account & Settings → Webhooks, add:
https://your-domain.com/webhooks/booking/{tenant}/razorpay/testSubscribe to:payment.captured,payment.failed,refund.processed,refund.failed,payment.dispute.created. Set a webhook secret that is different from the API key secret. - In Tenant → Settings → Payments, choose Razorpay, mode test, paste
Key ID, Key secret, and Webhook secret, set the account currency (INR, or another
currency Razorpay has enabled on the account — not a three-decimal currency), and
Save. Calbok checks the key prefix and lists one order (
GET /v1/orders?count=1) before activating the row. No money moves. - Book a deposit-required appointment and choose Razorpay. Checkout opens on the
booking page. After payment, the page posts the signature to
/{tenant}/booking/payment/callback/razorpay, which verifies it with the key secret and fetches the payment before recording the deposit.
Live mode: switch the dashboard to Live Mode, use a rzp_live_ key, point the
webhook at the same path ending in /razorpay/live, and re-save in mode live.
Paystack test-mode booking deposits (per business)
Tenants in Nigeria, Ghana, South Africa, or Kenya can take deposits via Paystack.
Keys are BYO per tenant — never Operator .env. Test and live use the same API host;
the key prefix selects the mode.
Test-mode setup:
- Create a free account at Paystack and open Settings → API Keys & Webhooks.
- Copy the Test Public Key (
pk_test_…) and Test Secret Key (sk_test_…). - Set the webhook URL to:
https://your-domain.com/webhooks/booking/{tenant}/paystack/testPaystack signs the raw body with the secret key (HMAC-SHA512 inX-Paystack-Signature). There is no separate webhook secret to paste. - In Tenant → Settings → Payments, choose Paystack, mode test, paste both
keys, set the settlement currency (
NGN,GHS,ZAR,KES, orUSD), and Save. Calbok checks the key prefix and a no-charge API call before activating the row. A live key saved in test mode (or the reverse) stays inactive. - Book a deposit-required appointment whose location currency matches that settlement
currency. The customer is redirected to Paystack; after payment they return to
/{tenant}/booking/payment/return/paystack. Calbok confirms the deposit from Paystack's verify API — thereferencequery string is not treated as proof of payment.
Live mode: switch mode to live, paste pk_live_… / sk_live_…, point the
webhook at the same path ending in /paystack/live, and re-save.
Mercado Pago test-mode booking deposits (per business)
Tenants in Argentina, Brazil, Chile, Colombia, Mexico, Peru, or Uruguay can take
deposits via Mercado Pago Checkout Pro. Keys are BYO per tenant — never
Operator .env. Calbok talks to Mercado Pago over plain HTTPS; do not install
a Mercado Pago SDK. These keys are booking deposits only. They are not used for
Operator subscription billing.
Test-mode setup:
- In Your integrations, create
an application and open Testing credentials. Copy the Access Token.
Test tokens start with
TEST-. Production tokens do not (they are issued asAPP_USR-…) and must be saved in live mode. - Under Webhooks → Configure notifications, set the test URL to:
https://your-domain.com/webhooks/booking/{tenant}/mercadopago/testSubscribe to Payments (payment). Chargebacks (topic_chargebacks_wh) are optional. Reveal the secret signature. It must be different from the access token. Calbok also sends this URL asnotification_urlwhen it creates a checkout; the application secret still signs those notifications. - In Tenant → Settings → Payments, choose Mercado Pago, mode test,
paste the access token and webhook secret, set the account currency
(
ARS,BRL,CLP,COP,MXN,PEN, orUYU), and Save. Colombia (COP) amounts must be whole pesos. Calbok checks the token prefix and callsGET /users/mebefore activating the row. No money moves. - Book a deposit-required appointment and choose Mercado Pago. The customer is
sent to the sandbox checkout (
sandbox_init_point). After payment they return to/{tenant}/booking/payment/return/mercadopago, where Calbok fetches the payment and does not trust the query-string status.
Payments created with test credentials do not send webhooks. The return URL and the reconcile sweep record the deposit in test mode. Use Webhooks → Simulate in Your integrations to check the signature before going live.
Live mode: use production credentials, point the production webhook at the
same path ending in /mercadopago/live, and re-save in mode live.
Mollie test-mode booking deposits (per business)
Tenants in the EEA or the UK can take deposits via Mollie. Keys are BYO per
tenant — never Operator .env. Calbok talks to Mollie over plain HTTPS; do
not install a Mollie SDK. These keys are booking deposits only. They are not
used for Operator subscription billing.
Test-mode setup:
- In the Mollie Dashboard, open Developers →
API keys and copy the Test API key. Test keys start with
test_. Live keys start withlive_and must be saved in live mode. - Calbok registers the classic webhook itself when it creates the payment
(
webhookUrl), as long as the site URL is publicly reachable. Mollie then POSTs the payment id, and Calbok confirms the payment with your API key. Next-gen webhooks are optional (they are still in Mollie's beta). To use them, open Developers → Webhooks, create a webhook, and set the URL to:https://your-domain.com/webhooks/booking/{tenant}/mollie/testSubscribe to payment.paid, payment.failed, payment.canceled, payment.expired, and dispute.created. Generate a signing secret. It must be different from the API key. If you leave the signing secret blank, only classic webhooks are accepted, and a request that sendsX-Mollie-Signatureis rejected. - In Tenant → Settings → Payments, choose Mollie, mode test, paste
the API key and (only if you use next-gen) the signing secret, set the
account currency (for example
EURorGBP), and Save. Whole units are required forHUF,TWD, andISK. Calbok checks the key prefix and callsGET /v2/payments?limit=1before activating the row. No money moves. - Book a deposit-required appointment and choose Mollie. The customer is sent
to Mollie's checkout. After payment they return to
/{tenant}/booking/payment/return/mollie, where Calbok fetches the payment and does not trust the query string.
Live mode: use the live API key and re-save in mode live. If you use
next-gen webhooks, point a live webhook at the same path ending in /mollie/live
and paste that webhook's signing secret. Live webhook URLs must be HTTPS.
MyFatoorah test-mode booking deposits (per business)
Tenants in Kuwait, the UAE, Bahrain, Jordan, Oman, Saudi Arabia, Qatar, or Egypt
can take deposits via MyFatoorah. Keys are BYO per tenant — never Operator .env.
Test and live are different API hosts. The token has no test/live prefix.
Test-mode setup:
- Register a demo account at registertest.myfatoorah.com (Kuwait is the usual demo country; skip bank details) and ask MyFatoorah to activate it, or copy the public test token from the API key docs. Do not commit that token into the application.
- In the demo portal, open Integration Settings → Webhook, enable Webhook v2,
set a webhook secret, and set the endpoint to:
https://your-domain.com/webhooks/booking/{tenant}/myfatoorah/testSubscribe to payment-status, refund-status, and dispute events. MyFatoorah signs an ordered field list (not the raw body) with HMAC-SHA256 inmyfatoorah-signature. The secret is not the API token. - In Tenant → Settings → Payments, choose MyFatoorah, mode test, paste the
API token, set the country (
KWT,ARE,BHR,JOR,OMN,SAU,QAT, orEGY), paste the webhook secret, and set the settlement currency to that country's currency (KWD,AED,BHD,JOD,OMR,SAR,QAR, orEGP). Save. Calbok checks the pairing and calls InitiatePayment (no charge) before activating the row. A mismatched country and currency stays inactive. - Book a deposit-required appointment whose location currency matches. The customer
is redirected to the MyFatoorah invoice. After payment they return to
/{tenant}/booking/payment/return/myfatoorah/test. Calbok confirms the deposit from GetPaymentStatus — thepaymentIdquery string is not treated as proof of payment. Kuwaiti, Bahraini, Omani, and Jordanian amounts use three decimal places (KWD 1.250).
Live mode: create the token in the live portal for that country, point the webhook
at the same path ending in /myfatoorah/live, and re-save in mode live. Live API
hosts differ by country (for example Saudi Arabia uses api-sa.myfatoorah.com).
Operator subscription billing — Stripe, PayPal, and Razorpay
This is how you (the Operator) charge tenants for Calbok. It is separate from the booking-deposit gateways above: a tenant's PayPal, Razorpay, Paystack, Mercado Pago, MyFatoorah, or Mollie keys take booking deposits, and these keys take SaaS subscriptions. Do not paste one set into the other screen. MyFatoorah and Mollie are booking deposits only.
Configure this in Admin → Settings → Billing providers. You do not need to
edit .env. Values saved there override BILLING_* and the Cashier/Spike keys
at runtime; anything you never save on that page keeps the .env value.
Secrets are encrypted and the form never shows them again — leave the field
blank to keep the saved secret. Each provider shows the webhook URL to paste
into that provider's dashboard, and Test connection checks the keys without
taking a payment.
- Choose the default provider: Offline (you mark a tenant paid by hand; no keys), Stripe, PayPal, or Razorpay.
- Stripe (Spike): enable it, choose test or live, and paste the publishable
key, secret key, and webhook signing secret. Map each Calbok plan's monthly
and yearly price id. The webhook URL on the page is the Cashier endpoint
(
/stripe/webhook). A Stripe subscription still uses the in-app portal at/billing. - PayPal: create a REST app (sandbox first) and a webhook for the URL shown on
the page (
https://your-domain.com/webhooks/billing/paypal). Subscribe toBILLING.SUBSCRIPTION.*,BILLING.SUBSCRIPTION.PAYMENT.FAILED, andPAYMENT.SALE.COMPLETED. Paste the client id, secret, and webhook id, set mode to test, and map each plan's PayPal plan id (monthly and yearly). A self-signup on that plan is sent to PayPal to approve the subscription. The in-app page at/billingshows status, the next charge, and cancel. - Razorpay: webhook
https://your-domain.com/webhooks/billing/razorpay(shown on the page). Use a webhook secret that is not the API key secret. Test key ids start withrzp_test_and must be saved in test mode (rzp_live_with live). Map plan ids the same way, and set billing cycles (12 is one year of monthly billing). - A nightly
billing:reconcilepoll heals a missed webhook. Diagnostics reads the same effective settings and warns if PayPal or Razorpay is enabled without a webhook id/secret or a plan map.
The same keys can still be supplied in .env (BILLING_PAYPAL_*,
BILLING_RAZORPAY_*, Cashier STRIPE_KEY / STRIPE_SECRET /
STRIPE_WEBHOOK_SECRET) before the first visit to the page. After you save
the page, the saved values win.
Bank transfer / offline: enable the Offline gateway in the same Payments screen
(account name, bank, IBAN/account number, optional SWIFT + instructions). Customers
see transfer instructions + a unique reference; staff with
mark_booking_payments_paid mark the deposit paid from the calendar Payments panel.