Developers
Build once on the payment orchestration layer.
One API pattern across every provider and product behind LILPE. Routing, retries and failover happen on our side; your code sees one payment and one final status. Sandbox access comes with onboarding, and the LILPE team stays with you through go-live.
Works from any language that can make HTTPS requests:
- cURL
- Node.js
- Python
- PHP
- Java
- Go
POST /v1/payments
curl https://api.lilpe.com/v1/payments \
-H "Authorization: Bearer $LILPE_SECRET_KEY" \
-H "Idempotency-Key: order_10234" \
-d amount=124900 \
-d currency=EUR \
-d "customer[country]=DE" \
-d "routing[strategy]=smart"const res = await fetch("https://api.lilpe.com/v1/payments", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LILPE_SECRET_KEY}`,
"Idempotency-Key": "order_10234",
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: 124900,
currency: "EUR",
customer: { country: "DE" },
routing: { strategy: "smart" },
}),
});
const payment = await res.json();import os, requests
payment = requests.post(
"https://api.lilpe.com/v1/payments",
headers={
"Authorization": f"Bearer {os.environ['LILPE_SECRET_KEY']}",
"Idempotency-Key": "order_10234",
},
json={
"amount": 124900,
"currency": "EUR",
"customer": {"country": "DE"},
"routing": {"strategy": "smart"},
},
).json()Response 201 Created 184 ms
{
"id": "pay_8Hk2Qm",
"status": "authorized",
"amount": 124900,
"currency": "EUR",
"provider": "provider_a",
"route": {
"rule": "high_value_eu_cards",
"attempts": 1,
"fallback": "provider_c"
}
}webhook → your endpointsigned
payment.authorized · pay_8Hk2Qm
Developer toolkit
Everything you need from first request to production.
Preview marks dashboard tools shown as a demo of the developer experience. Which tools are available for your business is confirmed during onboarding.
- API documentationOne request and status model across every provider and product.
- API credentialsSeparate sandbox and production keys, kept server-side and never exposed to the browser.
- SandboxTest end to end with sandbox keys before you touch production.
- WebhooksOne normalised, signed event stream for every status change.
- API logsPreviewEvery request with method, path, status and latency.
- Request & response inspectionPreviewThe request you sent and the normalised response you got back.
- Error logsPreviewFailed requests grouped by error code, with the reason and the fix.
- Integration statusPreviewKeys in use, webhook endpoints and their delivery health.
- Provider connection statusPreviewWhich providers are connected, in which environment, and their health.
Quick start
From keys to your first webhook
- Step 1Get sandbox keys
Sandbox and production use separate keys and data. Sandbox credentials come with onboarding.
.env
LILPE_SECRET_KEY=sk_test_•••••••• # server-side only - Step 2Authenticate
Send the secret key from your server as a bearer token. Never ship it to a browser or app.
HTTP header
Authorization: Bearer $LILPE_SECRET_KEY - Step 3Create a payment
One request shape for every provider. Send an Idempotency-Key so a retried request returns the same payment.
Request
POST /v1/payments Idempotency-Key: order_10234 { "amount": 124900, "currency": "INR", "reference_id": "order_10234" } - Step 4Handle the webhook
Verify the signature with your secret, act on the event once, and reply 2xx. Unacknowledged deliveries are retried.
Webhook handler
verify(lilpe-signature, hmac_sha256(secret, timestamp + "." + body)) // then mark the order paid, once
API overview
Core endpoints
POST/v1/payments
Create a payment; routed across the providers connected for your business.
GET/v1/payments/{id}
Status, attempts and provider references for one payment.
POST/v1/refunds
Full or partial refund, back through the original provider.
POST/v1/payouts
Send money to a bank account or UPI ID.
POST/v1/payment_links
Create a payment link with an expiry and reference.
GET/v1/settlements/{id}
Settlement lines, fees and GST for reconciliation.
Base URL and endpoints are illustrative; the full reference comes with sandbox access.
Error handling
One error shape, whichever provider failed
Error object · illustrative
{
"error": {
"type": "payment_failed",
"category": "issuer_unavailable",
"message": "The customer's bank didn't respond.",
"retryable": true,
"provider": "provider_a",
"provider_code": "U30"
}
}| Category | Retry | Meaning |
|---|---|---|
| provider_error | Usually | The provider failed on its side. |
| timeout | After a status check | No answer in time; checked before any retry. |
| issuer_unavailable | Usually | The customer's bank or issuer didn't respond. |
| customer_decline | Usually not | Declined by the customer or their bank for a reason a retry won't fix. |
| risk_decline | Usually not | Stopped by a risk check. |
Technical architecture
What happens between your request and the provider
- Your serverSecret key, server-side
LILPEOne API, one webhook stream- Routing engineRules, health, cost
- ProvidersEach in its own format
- Payment networksCards, real-time rails, banks
Integration Guide
From sandbox to production, with guided support
- 1
Get Sandbox Access
Sandbox credentials are provided during onboarding, after the business evaluation.
- 2
Integrate the API
Use the same request pattern across whichever LILPE products your business needs.
- 3
Test End-to-End
Run test transactions, payouts, or verification calls in the sandbox environment.
- 4
Go Live
Move to production credentials, with the LILPE team on hand for go-live.
Any language that can make HTTPS requests works, for example:
curl -X POST https://api.lilpe.com/v1/payments \
-H "Authorization: Bearer <LILPE_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"amount": 149900,
"currency": "INR",
"order_id": "ORDER_10234",
"payment_methods": ["upi", "card", "netbanking"]
}'API Reference
A consistent request pattern across every product
A sample of the core endpoints businesses integrate against most — collection, payouts, verification, and account data all follow the same authentication and request shape.
curl -X POST https://api.lilpe.com/v1/collections/order \
-H "Authorization: Bearer <LILPE_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"amount": 249900,
"currency": "INR",
"order_id": "ORDER_10234",
"payment_methods": ["upi", "card", "netbanking"]
}'{
"order_id": "order_SP9V21x",
"status": "created",
"amount": "₹2,499.00",
"checkout_url": "checkout.lilpe.com/pay/9V21x"
}API & Security
Security built into every request
Integration practices
What makes a payment integration last
These apply to any payment integration, with LILPE or anyone else. Most production incidents in payments come from skipping one of them.
Treat notices as the source of truth
A customer's browser returning to your site doesn't prove payment. Mark orders paid from the server-to-server notice (webhook) or a status check, never from the redirect alone.
Verify every notice
Check the signature on each webhook with your secret before acting on it, and reject anything that fails. Anyone can send a request to a public URL.
Expect duplicates and disorder
Notices can arrive twice, late or out of order. Make handling idempotent: processing the same event twice must have no extra effect, and a later status must not be overwritten by an earlier one.
Use idempotency keys for money-moving requests
When you retry a payout or refund after a timeout, send the same idempotency key so the request can't be executed twice.
Handle 'pending' as a real state
Some payments take time to reach a final status. Don't show failure, and don't ask the customer to pay again, until the status is final.
Keep secrets on the server
API keys and webhook secrets belong in server-side configuration, never in browser or app code, and are rotated when people with access leave.
| Test | Expect |
|---|---|
| Successful payment | Order paid from the notice; confirmation sent once |
| Declined payment | Clear message; customer can retry without a second order |
| Customer closes the tab after paying | Order still paid from the notice |
| Same notice delivered twice | No duplicate fulfilment or email |
| Refund, full and partial | Customer credited; your records updated |
| Request times out and is retried | No duplicate payout or refund, thanks to the idempotency key |
A worked example of notices and status checks: collection API handbook.
Guides
Guides and explainers
- PlatformPayment Gateway Integration: Hosted Checkout, Redirect or API?There are four common ways to put a payment gateway into a website or app, from a redirect you can set up in an afternoon to a full API integration your developers own. How each one works, what it means for your security and PCI DSS scope, the steps every integration shares, and how to test before going live.Read the guide
- PlatformPayment Gateway Webhooks: Receive, Verify and Never Miss a Payment UpdateA webhook is how your payment provider tells your server that something happened: a payment succeeded, a refund went through, a payout failed. How webhooks differ from the return URL and the status API, how to verify signatures, why you must handle retries, duplicates and out-of-order events, and how to test it all.Read the guide
- PlatformHow a Payout API Works: Business Payouts in India, From Request to UTRA payout API lets your systems send money to bank accounts and UPI IDs automatically: vendor payments, refunds, salaries, claims and seller settlements. How the pieces fit together, funding, beneficiary validation, choosing a rail, statuses and webhooks, failures and returns, controls, reconciliation and a test plan.Read the guide
- PlatformHow a Mobile Recharge API Works: Requests, Operator Responses and Transaction StatusAdding prepaid recharges to an app or retail network means connecting to operators through a recharge API, and most of the work is handling what happens after you press send. The chain from your app to the operator, operator and plan lookup, the request, pending and final statuses, callbacks, wallet holds and reversals, reconciliation and a test plan.Read the guide
- EngineeringDuplicate Payments: Why Customers Get Charged Twice and How to Prevent ItA customer pays once and is charged twice. It's rarely a provider fault: it's a double tap, a timeout followed by a blind retry, or two providers that both said yes. Where duplicates come from, the four layers that stop them (including idempotency keys done properly), and what to do when one slips through.Read the guide
- EngineeringPayment API Timeouts: What to Do When You Don't Know If a Payment Went ThroughA timeout doesn't mean a payment failed. It means you don't know. How timeouts differ from declines and pending payments, what a timeout can and can't tell you, how to set a timeout budget, which errors are safe to retry, and a status-check routine that resolves unknown payments without charging anyone twice.Read the guide
- EngineeringPayment API Versioning: Changing an API Without Breaking IntegrationsPayment integrations live for years, and a change that breaks one stops money moving. What counts as a breaking change in a payments API (including the ones that look harmless), versioning strategies and their trade-offs, how to deprecate properly, and how to integrate with providers' APIs so their changes don't break you.Read the guide
- EngineeringExactly-Once Payment Processing: Outboxes, Retries and Idempotent ConsumersNo network can promise to deliver a message exactly once, yet a payment system must act on each event exactly once. How that's achieved in practice: at-least-once delivery plus idempotent processing, the dual-write problem and the transactional outbox that solves it, idempotent consumers, ordering, and how to test that it all survives crashes.Read the guide
Ready to start building?
Request sandbox access and we'll set you up with test keys and the API reference.
