Open-source Agent Skill · MIT
Nepali payments your coding agent gets right.
One skill teaches Claude Code, Codex, Cursor and other agents to integrate eSewa, Khalti, Fonepay, connectIPS, CyberSource, NPS and IME Pay. It ships tested adapters in six languages, so the agent copies working code instead of guessing at signatures.
$ npx skills add broisnees/nepal-payments-skill> Add eSewa and Khalti payments to my Hono API on Cloudflare Workers with D1.
Skill loaded: nepal-payments
- Pick providerseSewa ePay v2 + Khalti KPG-2
- Detect stackHono on Workers, D1
- Install adapterscore.ts, esewa.ts, khalti.ts
- Routes + attempts tablereturn handler, decideTransition
- Reconciliationscheduled status re-checks
- Testsignature vectors + sandbox checks
Providers
Every major Nepali gateway, one interface.
Wallets, bank QR, direct debit and cards all expose initiate, verify and orderIdFromCallback.
eSewa
ePay v2 web checkout, Intent app-to-app
WalletKhalti
KPG-2 web checkout, refunds
Bank QRFonepay
Web redirect, Dynamic QR over WebSocket
BankconnectIPS
Direct bank debit, RSA-signed with your .pfx
CardsCyberSource
Secure Acceptance cards via Nepali banks
AggregatorNPS OnePG
About 29 banks, wallets and cards in one API
WalletIME Pay
Wallet checkout for existing merchants
Docs onlyHBL, Prabhu Pay, Moru and more
Notes and the path to an adapter on the same interface.
How it works
From one sentence to a verified integration.
Ask in plain words
Tell your agent what you need: providers, framework, database. The skill loads on its own when the task mentions Nepali payments.
It follows a tested recipe
The agent copies the adapter for your language, adds the attempts table, the return route and reconciliation, and keeps every secret on the server.
You ship with proof
Signature vectors and sandbox checks run in your repo, so the integration is tested before real money touches it.
Adapters
The same two calls in six languages.
Copy the folder for your language into your server. Start a payment with initiate, then confirm it server-to-server with verify using the amount you stored. Integer paisa everywhere.
- TypeScript49 tests · Zero dependencies
- Python33 tests · Stdlib (cryptography for connectIPS)
- Java111 tests · No dependencies
- Kotlin84 tests · kotlin-stdlib only
- PHP69 tests · ext-curl and ext-openssl, no Composer deps
- Go68 tests · Stdlib only
import { createPayments, paymentsConfigFromEnv, newOrderId } from "./payments/index.ts";
const payments = createPayments(paymentsConfigFromEnv());
const init = await payments.get("khalti").initiate({
orderId: newOrderId(), amountPaisa: 150000, description: "Order #42",
returnUrl: "https://shop.example.com/payments/khalti/return",
});
// later, in the return handler: verify with values from YOUR database
const result = await payments.get("khalti").verify({
orderId: attempt.id, amountPaisa: attempt.amountPaisa, providerRef: attempt.providerRef,
});from nepal_payments import create_payments, config_from_env, new_order_id
payments = create_payments(config_from_env())
init = payments["khalti"].initiate(
order_id=new_order_id(), amount_paisa=150000, description="Order #42",
return_url="https://shop.example.com/payments/khalti/return",
)
# later, in the return handler: verify with values from YOUR database
result = payments["khalti"].verify(attempt.id, attempt.amount_paisa, provider_ref=attempt.provider_ref)p, err := payments.New(payments.ConfigFromEnv())
if err != nil {
log.Fatal(err)
}
init, err := p.Khalti.Initiate(ctx, payments.InitiateInput{
OrderID: payments.NewOrderID(), AmountPaisa: 150000, Description: "Order #42",
ReturnURL: "https://shop.example.com/payments/khalti/return",
})
// later, in the return handler: verify with values from YOUR database
res, err := p.Khalti.Verify(ctx, payments.VerifyInput{
OrderID: a.ID, AmountPaisa: a.AmountPaisa, ProviderRef: a.ProviderRef,
})Payments payments = Payments.create(Payments.configFromEnv());
InitiateResult init = payments.khalti().initiate(InitiateInput.of(
Payments.newOrderId(), 150_000, "Order #42", "https://shop.example.com/payments/khalti/return"));
// later, in the return handler: verify with values from YOUR database
VerifyResult result = payments.khalti().verify(
new VerifyInput(attempt.id(), attempt.amountPaisa(), attempt.providerRef(), null));val payments = Payments.create(paymentsConfigFromEnv())
val init = payments.khalti.initiate(InitiateInput(
orderId = newOrderId(), amountPaisa = 150_000, description = "Order #42",
returnUrl = "https://shop.example.com/payments/khalti/return",
))
// later, in the return handler (suspend): verify with values from YOUR database
val result = payments.khalti.verify(VerifyInput(attempt.id, attempt.amountPaisa, providerRef = attempt.providerRef))use NepalPayments\{InitiateInput, Payments, VerifyInput};
$payments = Payments::create(Payments::configFromEnv());
$init = $payments->khalti()->initiate(new InitiateInput(
orderId: Payments::newOrderId(), amountPaisa: 150000, description: 'Order #42',
returnUrl: 'https://shop.example.com/payments/khalti/return',
));
// later, in the return handler: verify with values from YOUR database
$result = $payments->khalti()->verify(new VerifyInput($attempt->id, $attempt->amountPaisa, $attempt->providerRef));Guarantees
Rules the skill won't let your agent skip.
Secrets stay on the server
Signing keys never reach the browser or the app. Clients only ever send an order id.
A redirect is not a payment
Return URLs and callbacks are hints. The provider's status API decides whether money moved.
Your numbers, not theirs
Verification uses the amount and ids stored in your database, never the values in the callback.
One state machine
decideTransition handles early return hits, late payments, missing or mismatched amounts and duplicates.
Reconciliation built in
A scheduled job settles payments where the customer paid and closed the tab.
Vectors before code
Every adapter is tested against the provider's own signature test vectors in all six languages.
Install
Works with the agent you already use.
Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot and any other agent that reads Agent Skills.
Install guideClaude Code
/plugin marketplace add broisnees/nepal-payments-skill
/plugin install nepal-payments@nepal-payments-skillAny agent
npx skills add broisnees/nepal-payments-skillFAQ
Questions
What is an Agent Skill?
A folder of instructions, references and scripts that a coding agent loads when a task matches. This one teaches the agent how Nepali payment gateways actually behave, so it follows a tested recipe instead of guessing signatures and callback formats.
Which agents does it work with?
Any agent that supports the Agent Skills format: Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot and others. Claude Code can also install it from the plugin marketplace.
Can I use the adapters without an agent?
Yes. Copy the folder for your language into your server code. The adapters have no runtime dependencies beyond each language's standard library (connectIPS in Python also needs the cryptography package).
Which provider should I start with?
eSewa and Khalti cover the largest wallets. Add NPS or Fonepay to reach most banks through one integration, connectIPS for direct bank debit, and CyberSource only if a bank is acquiring cards for you.
Is it affiliated with eSewa, Khalti or the other providers?
No. It is an independent open-source project under the MIT license. The sandbox credentials it uses are the public ones from each provider's documentation.
Does it handle mobile apps?
Yes. The mobile guide covers Flutter, React Native, Expo, Android and iOS, including eSewa Intent app-to-app payments, with all signing kept on your server.
Take your first sandbox payment today.
eSewa and Fonepay sandboxes need no signup. The demo server runs both.