Platform capability
One API across every provider
One request format, one status model and one error model, whichever provider processes the payment. Provider-specific detail is kept for when you need it.
- Status values that mean the same thing everywhere
- Errors grouped by cause, with the raw code kept
- Idempotent by design
Three providers report the same successful payment with different field names and status values. Peneu returns one payment object with a single status, the provider name and the provider's own reference kept for traceability.
Status model
One set of statuses
Every provider has its own vocabulary for the same states. Your code only has to deal with these.
| Peneu status | Meaning | What providers call it |
|---|---|---|
| created | The payment exists, with no attempt yet | — |
| processing | With a provider, waiting on the customer or the bank | PENDING, initiated, in_progress |
| authorized | Approved. For cards, capture may follow | SUCCESS, captured, 00 |
| failed | Final failure, with a category that says why | FAILED, declined, 05 |
| refunded / partially_refunded | One or more refunds completed | REFUNDED, refund.processed |
The provider examples are illustrative. Mappings are maintained per provider.
Requests and responses
The same shapes, whichever provider is behind them
- Amounts are integers in paise, so there are no floating-point surprises
- Illustrative. The full reference is shared with sandbox access
POST /v1/payments
Idempotency-Key: order-10234-1
{
"amount": 245000,
"currency": "INR",
"method": "card",
"reference_id": "order-10234",
"metadata": { "cart_id": "c_8812" }
}GET /v1/payments/pay_7Hq2
{
"id": "pay_7Hq2",
"status": "authorized",
"amount": 245000,
"reference_id": "order-10234",
"provider": "provider_b",
"provider_ref": "9913…",
"method_details": { "network": "rupay", "last4": "4242" }
}{
"error": {
"type": "payment_failed",
"category": "issuer_unavailable",
"message": "The issuing bank did not respond.",
"retryable": true,
"provider": "provider_a",
"provider_code": "91"
}
}Design choices
Built for the edge cases
- 01
Idempotency keys
Send the same key twice and you get the same payment back, not a second one. Retries on your side are safe.
- 02
Your references, everywhere
reference_id and metadata travel with the payment into every webhook, export and report.
- 03
Provider references kept
Bank RRNs, UTRs and provider IDs stay on the payment. You'll need them for disputes and support tickets.
- 04
Errors grouped by cause
customer, issuer, provider or risk, plus a retryable flag, so your checkout can decide what to tell the customer.
- 05
Separate environments
Sandbox and production have separate keys and separate data. A test payment can never touch real money.
- 06
Provider changes stay on our side
When a provider changes its API or response codes, the mapping is updated in Peneu. Your integration keeps the same shapes.
Worth knowing
What stays method-specific
The API normalises the lifecycle, not the rules of each payment rail. UPI still needs an app switch or a QR, cards still need authentication, and netbanking still redirects to the bank. The API tells you which next step the method needs, and your checkout shows it.
Some fields only exist for some methods, like a UPI VPA or a card network. They live under method_details instead of being forced into a shape they don't fit.
Evaluating it
Questions to ask about this, of any provider
Use these with any platform, including Peneu. For Peneu, what's available for your business is confirmed during onboarding.
- Is there one status model for every provider?
- Mapping each provider's statuses yourself is where integration bugs hide.
- Are errors normalised, with the original reason kept?
- You need a common error to act on and the provider's reason to investigate.
- Are money-moving requests idempotent?
- Retries after timeouts must not create duplicate charges, refunds or payouts.
- How are API keys scoped and rotated?
- Keys per environment, kept server-side, and rotated when people leave.
FAQ
Questions about unified api
Can I still see what the provider actually returned?
Yes. The provider name, its reference and its raw response code are kept on every payment and error.
Do I need an idempotency key on every request?
On every request that creates something, like a payment or a refund, yes. It's what makes network retries on your side safe.
How are refunds handled across providers?
Through one refund call against the Peneu payment ID. The refund goes back through the provider that processed the original payment, as it has to.
Works together with
Read the full reference
Request sandbox access and you'll get test keys and the complete API reference, including the status and error mappings per provider.
Last reviewed . Samples on this page are illustrative.

normalise