Use Wavy on Your Website
This guide explains the end-to-end developer integration in detail: what to build, where to put each piece of logic, and how to make the flow reliable in production.
Prerequisites
- You have an integration public key created from the Wavy integration setup flow.
- You have a backend endpoint that can call Wavy APIs server-to-server.
- You have a webhook URL that is reachable publicly via HTTPS.
- Your order/subscription database has fields for status, payment_reference, payment_id, and last_event.
Full Payment Sequence
POST /api/create-payment
+ payment_id
to hosted checkout
via Flutterwave
merchant updates records
Step 1: Create Payment (Backend)
Create a backend route like POST /checkout/start in your app. This route calls Wavy POST /api/create-payment.
Required Request Fields
- amount: numeric value greater than 0
- title: customer-visible payment title
- description: payment purpose or invoice memo
- callback_url: URL Wavy redirects customer to after checkout
- metadata: structured context for your internal reconciliation
- vendor_id: optional when payment should be linked to a specific vendor profile
Expected Response Fields
- payment_id
- checkout_url
- payment_link
Step 2: Redirect to Hosted Checkout (Frontend)
Your frontend should call your own backend start-payment route, receive checkout_url, then redirect.
Step 3: Handle Callback Return
After checkout, customer returns to your callback_url. Show a "processing" or "verifying" state first, then confirm status from your backend.
What To Do
- Read reference/payment_id from callback params.
- Call backend to fetch latest order/payment state.
- Render success, failed, or pending state.
What To Avoid
- Do not trust frontend query params as final proof.
- Do not update order to paid from callback alone.
- Do not skip webhook validation.
Step 4: Receive and Process Webhooks
Wavy sends webhook events to your backend. Handle each event type explicitly and log event + reference.
Events To Handle
- payment.processing
- payment.success
- payment.failed
Step 5: Idempotent Business State Updates
Use payment reference as your idempotency key when applying order/subscription updates.
Practical Rule Set
- If event is payment.processing and order is unpaid: mark order status "pending_payment".
- If event is payment.success and order not yet paid: mark paid, store paid_at, unlock value delivery.
- If event is payment.failed and order unpaid: mark failed and allow retry path.
- If event reference already processed: return 200 and do nothing else.
Split-Payment Behavior
Split payment is determined by link/vendor integration configuration. Frontend should communicate clearly when payment is routed via vendor split setup.
- Guest links: simple payer flow, vendor split handled by backend/payment config.
- Vendor dashboard links: split enabled based on vendor subaccount readiness.
- Developer create-payment: optional vendor_id allows explicit vendor routing context.
Security Checklist
- Never expose secret values in frontend bundles or HTML.
- Use HTTPS callback and webhook endpoints only.
- Validate and sanitize metadata before storing.
- Implement request timeouts and retries for non-terminal errors.
- Log payment_id, reference, event, and timestamp for audit traceability.
Error Handling and Retry Strategy
API Errors
- 400: malformed request or validation failure
- 401: invalid/expired token or key
- 429: rate limit, retry with backoff
- 503: temporary service issue, retry with backoff
Recommended Retry
- Attempt 1: immediate
- Attempt 2: +500ms
- Attempt 3: +1000ms
- Attempt 4: +2000ms then fail gracefully
Testing and Go-Live Plan
- Create a low-value test payment and verify redirect works.
- Confirm callback page renders expected status.
- Verify webhook receives event and updates order idempotently.
- Replay same webhook payload and confirm no duplicate updates.
- Simulate failed and processing events for fallback UX.
- Run QA checklist before production rollout.