Zodiac Tech CRM Docs

Payment providers (Razorpay / Cashfree)

Configure Razorpay or Cashfree once — encrypted keys, webhook URLs, test vs live, and the health check that proves it works.

super-admin developer Advanced ⏱ 15 min Updated 2026-10-05

Payment providers (Razorpay / Cashfree)

Commercial billing talks to one active provider at a time. Configure it at Billing → Providers & Settings (/super-admin/billing/commercial-providers). Keys are encrypted at rest and never echoed back — this page shows the flow, not the secrets.

Who this is for

Super Admins and the person who owns the payment provider account.

  • Difficulty: Advanced
  • Time: 15 min
  • Prerequisites: A Razorpay or Cashfree merchant account; access to set webhooks in that account; the platform's public URL.

Step 1 — Choose the provider and environment

Under Commercial Billing:

Setting Options Notes
Enable commercial billing On / Off When off, AutoPay is not offered and no provider API calls are made
Provider Razorpay / Cashfree The single active provider for commercial billing
Environment Sandbox / Production Sandbox for testing; switch after go-live
Grace period (days) default 7 (max 90) How long a failed payment waits before escalating — see Payment failures & restrictions

Test with sandbox keys first Sandbox credentials cannot move real money, and production credentials can. Complete the full agree → authorize → renew → fail-recover cycle in Sandbox before switching to Production.

Step 2 — Enter the credentials

Razorpay

The Razorpay fields use the same credentials as the platform's checkout — they are shared, not duplicated. Enter them here or on the Billing Settings page (/super-admin/billing-settings-page); both read the same stored values:

  • Key ID
  • Key Secret
  • Webhook Secret

Cashfree

  • App ID
  • Secret Key
  • Webhook Secret

All secret fields behave the same way: stored encrypted at rest, never rendered back, and a blank field keeps the stored value. The helper text says it exactly: "Already saved — leave blank to keep it unchanged."

No secrets are echoed — on purpose You cannot view a saved key or secret from this page. If one is lost, generate a new one at the provider and paste it in. Never paste credentials into support tickets or docs.

Step 3 — Register the webhook URLs at the provider

Both endpoints are on the API (no auth, signature-verified, throttled):

Provider Webhook URL
Razorpay https://<your-domain>/api/webhooks/razorpay
Cashfree https://<your-domain>/api/webhooks/cashfree

Signature schemes (register the matching secret above):

  • Razorpay — header X-Razorpay-Signature: hex HMAC-SHA256 of the raw request body, keyed with your webhook secret.
  • Cashfree — header x-webhook-signature: Base64 HMAC-SHA256 of x-webhook-timestamp . rawBody, keyed with your secret.

How webhooks are processed Request → signature verification → idempotent event row (unique provider + event ID, SHA-256 payload hash) → queued processing → billing service. Invalid signature returns 400 and stores nothing. An unconfigured provider returns 200 and logs. Unknown event types are stored as processed with an unmapped error and never dispatched.

Canonical events understood by the platform: subscription.authorized, subscription.charged, subscription.cancelled, subscription.halted, payment.captured, payment.failed, mandate.revoked, mandate.expired.

Step 4 — Run the health check

Click Health check → confirm. The platform performs a lightweight server-side check against the selected provider with the saved credentials. Green means the keys work and the API is reachable; a failure message tells you which side is wrong.

Step 5 — Verify activation end to end

The only proof that everything is wired: a client approves an agreement, authorizes AutoPay at the provider, and the agreement turns Active — driven by the provider webhook, not by the browser coming back. If the health check is green but activation never happens, the webhook URL or webhook secret is wrong.

Expected result

The provider is enabled, keys validate, webhooks arrive, and an agreement can reach Active through a provider-confirmed authorization.

Common mistakes

  • Enabling Production with Sandbox keys (or vice versa) — authorization will fail.
  • Setting only the API key pair and forgetting the webhook secret — payments succeed but agreements never activate.
  • Duplicating Razorpay credentials on the Commercial page thinking they are separate from checkout. They are shared.
  • Lowering the grace period to 0 days. Escalation can then restrict a client the day after a failure; the default 7 exists for a reason.
  • Testing webhooks without a public URL. Localhost callbacks need a tunnel; on production the webhook URL must match the live domain.

Troubleshooting

Symptom What to check
Health check fails Environment, key pair, provider account status
Client authorizes but agreement stays pending Webhook URL reachable + webhook secret correct
Webhook returns 400 Signature mismatch — secret from the wrong environment
Duplicate payment records Should be impossible by event ID uniqueness; check for two different event IDs from the provider
Switching providers affects old agreements Each agreement stores its provider; existing mandates stay with their provider

Technical details

  • Page: App\Filament\SuperAdmin\Pages\Commercial\CommercialProviders (slug billing/commercial-providers); settings via App\Settings\BillingSettings — secrets #[ShouldBeEncrypted], never echoed.
  • Controller: App\Http\Controllers\Api\CommercialBillingWebhookController → App\Jobs\ProcessCommercialWebhook → BillingService::handleProviderEvent().
  • Provider adapters: app/Billing/Commercial/*; manager CommercialProviderManager::healthCheck().
  • Routes: POST /api/webhooks/razorpay (name commercial.webhook.razorpay) and POST /api/webhooks/cashfree (name commercial.webhook.cashfree), throttle:240,1.
  • Environment: commercial_environment (sandbox | production) on the same settings page.

Was this page helpful?