
September 08, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Building a Payment Gateway API from Scratch is not a weekend side project. It is a systems problem that spans money movement, fraud controls, reconciliation, and compliance boundaries most product teams underestimate until the first failed settlement run. Merchants do not want another CRUD app. They want an API that authorizes NPR or USD charges, survives duplicate POST requests, and tells their backend exactly when money actually moved. If you are evaluating whether to build or integrate, start with the contract merchants will code against — not your internal database schema. This guide walks through the architecture, endpoints, and production patterns I rely on when shipping REST API development work for eCommerce and client portals.
What is a payment gateway API and who should build one?
A payment gateway API sits between a merchant application and the financial rails that move money. The merchant sends a charge request. Your API validates it, routes it to an acquirer or local switch, stores immutable audit records, and returns a normalized response. Webhooks push asynchronous updates when authorization settles, fails, or disputes later.
Most teams should not build a full gateway. Stripe, PayPal, eSewa, Khalti, and ConnectIPS already solve card and wallet acceptance for Nepal and global merchants. You build from scratch when you operate a marketplace that must split payouts, white-label payments for resellers, or embed treasury logic a third party cannot expose. On production Laravel carts and legal-tech portals I have maintained, integration was the right default. Custom gateway work appeared only when settlement rules were the product.
Before writing code, draw three boundaries. First, what card data touches your servers — that defines PCI scope. Second, which currency and settlement paths you support — NPR wallets differ from USD card capture. Third, how merchants authenticate — API keys for server-to-server, publishable keys for client-side tokenization only. Compare your needs against existing options in our eCommerce payment gateway comparison for Nepal before committing six months of engineering time.
Build versus integrate decision matrix
| Scenario | Integrate existing gateway | Build gateway API |
|---|---|---|
| Single-store WooCommerce or Shopify checkout | Yes — plugin or hosted fields | No — compliance cost exceeds benefit |
| Marketplace with split payouts to vendors | Sometimes — Stripe Connect-style products | Yes — if local rails lack split APIs |
| White-label payments for agency clients | Rarely — branding limits hurt | Yes — API is the product |
| Nepal wallet + bank transfer only | Yes — eSewa, Khalti, ConnectIPS | Only if you are the wallet operator |
| Cross-border NPR and USD settlement | Often — multi-currency acquirer | Yes — when FX rules are proprietary |
Custom gateway work belongs in your roadmap when settlement logic is competitive advantage. For a standard checkout, read Laravel payment integrations and ship faster.
How do you design the core endpoints for a payment gateway API?
Design the public contract first. Merchants think in charges, refunds, customers, and payment methods — not your internal transaction table. Follow REST nouns, version the base path, and never break v1 without a sunset plan described in Laravel API versioning strategy.
A minimal v1 surface looks like this:
- POST /v1/charges — create an authorization or sale
- GET /v1/charges/{id} — retrieve status and receipt fields
- POST /v1/charges/{id}/capture — capture a prior authorization
- POST /v1/refunds — partial or full refund against a charge
- POST /v1/payment_methods — attach tokenized instrument to customer
- GET /v1/balance_transactions — settlement lines for reconciliation
Return consistent JSON envelopes. Use HTTP status codes correctly: 201 for creates, 402 for declined payments, 409 for idempotency conflicts, 422 for validation errors. Document every field in OpenAPI and publish it — merchants treat undocumented keys as bugs.
Example charge request and response
Merchants send amounts in the smallest currency unit. Rs 1,500.00 NPR becomes 150000 paisa if you follow minor-unit conventions, or 1500 if you document whole rupees — pick one and never mix them. Use the same approach as our Nepal EMI calculator docs: explicit currency metadata on every object.
POST /v1/charges HTTP/1.1
Host: api.example.com
Authorization: Bearer sk_live_...
Idempotency-Key: ord_9f3a-20260908-001
Content-Type: application/json
{
"amount": 150000,
"currency": "npr",
"payment_method": "pm_tok_abc123",
"capture": true,
"metadata": {
"order_id": "ORD-8842",
"merchant_ref": "shop-checkout-44"
}
} {
"id": "ch_7Km2pL9x",
"object": "charge",
"amount": 150000,
"currency": "npr",
"status": "succeeded",
"captured": true,
"failure_code": null,
"created": 1757315520,
"metadata": {
"order_id": "ORD-8842"
}
} Validate payloads with Form Requests in Laravel 13 on PHP 8.3 or higher. Persist the raw request body before calling external systems. If the acquirer times out, you need the original payload to reconcile — not a reconstructed guess from logs.
Laravel route and controller sketch
// routes/api.php
Route::prefix('v1')->middleware(['auth:sanctum', 'throttle:payments'])->group(function () {
Route::post('/charges', [ChargeController::class, 'store']);
Route::get('/charges/{charge}', [ChargeController::class, 'show']);
Route::post('/charges/{charge}/capture', [ChargeController::class, 'capture']);
Route::post('/refunds', [RefundController::class, 'store']);
}); Keep controllers thin. A CreateCharge action should orchestrate: idempotency lookup, ledger insert, acquirer adapter call, state transition, and domain event dispatch for webhook workers. Patterns from building RESTful APIs with Laravel apply directly — payments just add stricter invariants.
How do you implement idempotency and webhooks reliably?
Duplicate POST requests are guaranteed in production. Mobile clients retry. Load balancers replay. Merchants double-click. Without idempotency, you double-charge — the fastest way to destroy trust.
Require an Idempotency-Key header on all POST endpoints that move money. Store a hash of the merchant ID, key, route, and request body. Return the original response for repeats within 24 hours. Return 409 if the same key arrives with a different body. Redis 8.10 handles hot lookups; MySQL holds the authoritative record. Read the dedicated guide on API idempotency keys for TTL and cleanup jobs.
- Merchant sends POST with idempotency key.
- Gateway checks
idempotency_keystable inside a database transaction. - If new, insert ledger row as
pendingand call acquirer. - On response, update charge status and cache serialized HTTP response.
- Duplicate request returns cached response without second acquirer call.
Webhook design merchants can trust
Webhooks are your real-time contract. Merchants should never poll GET /charges/{id} every second during checkout. Emit events: charge.succeeded, charge.failed, refund.created, dispute.opened.
Sign every payload with HMAC-SHA256 using a per-merchant secret. Include a timestamp header and reject events older than five minutes to block replay attacks. Retry with exponential backoff across at least 72 hours. Log delivery attempts in a webhook_deliveries table with response codes.
X-Gateway-Signature: t=1757315520,v1=8f4e2b...
X-Gateway-Event: charge.succeeded
{
"id": "evt_3Nx8q",
"type": "charge.succeeded",
"data": { "object": { "id": "ch_7Km2pL9x", "status": "succeeded" } }
} Merchants verify signatures before updating order status. On a real client project, the bug I see most often is updating the cart on HTTP 200 from the charge endpoint instead of waiting for the webhook — treat synchronous responses as provisional until settlement confirms.
Publish a merchant SDK or copy-paste verifier. Good SDK design reduces support tickets — see SDK design for your public API. Provide a CLI to replay dead-letter events after the merchant fixes their endpoint.
What security and PCI scope rules apply to a custom gateway?
Security failures end companies. You do not store raw PANs unless you pursue PCI DSS Level 1 certification — audit costs alone can exceed Rs 40 lakh (~USD 3,000) annually before infrastructure hardening. Default architecture: hosted fields or JS tokenization so card numbers never touch your application servers.
Follow the PCI Security Standards Council document library for SAQ types. Most custom gateways target SAQ A or SAQ A-EP by isolating card data in a PCI-compliant vault provider. Your API stores only tokens like pm_tok_abc123.
- Transport: TLS 1.2+ everywhere, HSTS on all API hosts.
- Authentication: Separate publishable and secret keys; rotate secrets without downtime.
- Authorization: Scope keys — refund-capable keys are not the same as charge-only keys.
- Rate limiting: Per-key and per-IP throttles on charge endpoints.
- Audit: Append-only ledger; never delete charge rows — soft-delete with tombstone flags.
- PII: Encrypt sensitive metadata at rest; mask card last-four in logs.
Authentication patterns mirror general API work covered in building secure authentication systems and Passport vs Sanctum for API auth. Use Sanctum for first-party merchant dashboards and hashed API keys for server integrations.
For Nepal bank transfers via ConnectIPS, the pattern differs — users redirect to bank apps and callbacks carry signed transaction references. Study ConnectIPS integration for bank payments before abstracting a unified charge endpoint across cards and bank rails.
How do you test, reconcile, and deploy a payment gateway API?
Payments require test modes that mirror production exactly. Issue sk_test_ and sk_live_ key prefixes. Simulate acquirer outcomes with deterministic test PANs: one ends in 4242 for success, another triggers insufficient funds, another triggers timeout.
Reconciliation is where amateur gateways die. Nightly batch jobs compare your ledger against acquirer settlement files. Mismatches flag ops review — never auto-delete. On eCommerce projects like Quick And Easy Nepalese Grocery, reconciliation reports saved hours when a gateway CSV did not match order totals after a partial refund bug.
Deployment checklist
- Run migrations on MySQL 9.7 with strict mode enabled.
- Configure queue workers for webhooks on a dedicated Redis queue.
- Set
APP_KEYrotation procedure without invalidating stored secrets. - Enable slow-query logging on ledger tables before launch.
- Ship OpenAPI docs via Scribe — see API documentation with Scribe.
- Run contract tests on charge and refund JSON schemas using JSON formatter fixtures in CI.
- Load-test idempotency under concurrent duplicate POSTs.
Deploy with zero-downtime releases on Ubuntu 24 and PHP-FPM 8.5. Reload FPM after symlink swap so opcache picks up new code. Use GitLab CI to lint, run PHPUnit, and deploy through Deployer 7 — the same pipeline pattern I use on sister legal-tech sites. External reference: the Stripe API reference is the best public example of consistent idempotency, pagination, and error objects — study it even if Stripe is your competitor.
Magento and WooCommerce merchants expect plugin interfaces. If you white-label, plan adapter modules early — read Magento 2 custom payment gateway integration and WooCommerce custom payment gateway development for checkout expectations.
Operational monitoring belongs in testing and optimization retainers: alert on charge latency p95, webhook failure rate, and reconciliation drift. A gateway that silently stops settling is worse than one that throws loud 500 errors.
Key Takeaways
- Building a Payment Gateway API from Scratch is justified only when settlement or white-label logic is core product — otherwise integrate.
- Version your REST contract, require idempotency keys on every money-moving POST, and cache duplicate responses.
- Never store raw card numbers on application servers; tokenize through a PCI-scoped vault.
- Deliver signed webhooks with retries and dead-letter replay — merchants should confirm orders on events, not synchronous charge responses.
- Run nightly reconciliation between your ledger and acquirer files before you trust dashboard totals.
- Document with OpenAPI, ship test keys and deterministic fixtures, and load-test concurrent duplicate charges.
People Also Ask
How long does it take to build a payment gateway API?
A minimal charge-and-refund API with webhooks takes three to six months for a senior team. PCI certification, multi-acquirer routing, dispute handling, and merchant dashboards add another six to twelve months. Integration with existing gateways often ships in two to four weeks for Laravel or WooCommerce stores.
Do I need a banking license to run a payment gateway?
Regulation depends on jurisdiction. In Nepal, payment service operations fall under Nepal Rastra Bank oversight for licensed payment service providers. Most software agencies build tech for licensed partners rather than becoming the licensed entity. Legal review is mandatory before handling customer funds.
What database schema do payment gateways use?
At minimum: merchants, api_keys, customers, payment_methods (tokens only), charges, refunds, idempotency_keys, ledger_entries, webhook_endpoints, and webhook_deliveries. Treat charges and ledger_entries as append-heavy tables with indexed status and created_at columns for reporting.
Can Laravel handle a production payment gateway API?
Yes. Laravel 13 on PHP 8.3+ provides queues, Sanctum, Form Requests, and Eloquent transactions suited to gateway workloads. Pair it with Redis 8.10 for idempotency caches and webhook jobs. High-volume card routing may later split acquirer adapters into dedicated services, but Laravel remains a solid control plane.
Ship the gateway — or integrate wisely
Building a Payment Gateway API from Scratch demands ledger discipline, idempotent endpoints, and PCI-aware tokenization before you write a single acquirer adapter. Most businesses should integrate proven rails and spend engineering budget on checkout conversion and reconciliation tooling instead. If your product genuinely needs custom settlement, start with a versioned charge contract and webhook spec — then iterate toward certification and bank partnerships. For architecture review or eCommerce development with Nepal payment rails, contact us or explore custom software development options on kokil.com.np.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

