A unified payment SDK for Nepal supporting multiple payment providers with a consistent API.
- Unified API - Same interface for all providers
- Type-safe - Strongly typed requests and responses
- Production-ready - Proper error handling, timeouts, and logging
- Well-tested - Comprehensive test coverage
- Zero dependencies - Uses only Go standard library (except
golang.org/x/cryptofor ConnectIPS)
| Provider | Flow Type | Authentication | Status |
|---|---|---|---|
| eSewa | Form POST | HMAC-SHA256 | ✅ Ready |
| Khalti | Redirect | API Key | ✅ Ready |
| ConnectIPS | Form POST | RSA-SHA256 Certificate | ✅ Ready |
| FonePay | Redirect | HMAC-SHA512 | ✅ Ready |
go get github.com/voidarchive/nepal-payment-goimport "github.com/voidarchive/nepal-payment-go/providers/esewa"
provider, _ := esewa.New(esewa.Config{
SecretKey: "8gBm/:&EnhH.1/q", // Sandbox key
ProductCode: "EPAYTEST",
Sandbox: true,
})
// Initiate payment
resp, _ := provider.InitiatePayment(ctx, &core.PaymentRequest{
Amount: 100,
OrderID: "ORDER-123",
CallbackURL: "https://yoursite.com/callback",
})
// resp.IsFormPOST = true
// Generate HTML form with resp.FormFields, POST to resp.TargetURLimport "github.com/voidarchive/nepal-payment-go/providers/khalti"
provider, _ := khalti.New(khalti.Config{
SecretKey: os.Getenv("KHALTI_SECRET_KEY"),
WebsiteURL: "https://yoursite.com",
Sandbox: true,
})
resp, _ := provider.InitiatePayment(ctx, &core.PaymentRequest{
Amount: 10000, // In paisa (NPR 100)
OrderID: "ORDER-123",
CallbackURL: "https://yoursite.com/callback",
})
// resp.IsFormPOST = false
// Redirect user to resp.TargetURLimport "github.com/voidarchive/nepal-payment-go/providers/connectips"
provider, _ := connectips.New(connectips.Config{
MerchantID: os.Getenv("CONNECTIPS_MERCHANT_ID"),
AppID: os.Getenv("CONNECTIPS_APP_ID"),
AppPassword: os.Getenv("CONNECTIPS_APP_PASSWORD"),
CertPath: "/path/to/creditor.pfx",
CertPassword: os.Getenv("CONNECTIPS_CERT_PASSWORD"),
Sandbox: true,
})
resp, _ := provider.InitiatePayment(ctx, &core.PaymentRequest{
Amount: 100,
OrderID: "ORDER-123",
CallbackURL: "https://yoursite.com/callback",
})
// resp.IsFormPOST = true
// Generate HTML form with resp.FormFields, POST to resp.TargetURLimport "github.com/voidarchive/nepal-payment-go/providers/fonepay"
provider, _ := fonepay.New(fonepay.Config{
MerchantCode: "NBQM", // Sandbox
SecretKey: "a7e3512f5032480a83137793cb2021dc",
Sandbox: true,
})
resp, _ := provider.InitiatePayment(ctx, &core.PaymentRequest{
Amount: 100,
OrderID: "ORDER-123",
CallbackURL: "https://yoursite.com/callback",
})
// resp.IsFormPOST = false
// Redirect user to resp.TargetURLGenerate QR codes for retail/in-person payments. Requires additional credentials from your bank.
provider, _ := fonepay.New(fonepay.Config{
MerchantCode: "NBQM",
SecretKey: "a7e3512f5032480a83137793cb2021dc",
Username: os.Getenv("FONEPAY_USERNAME"), // QR API credentials
Password: os.Getenv("FONEPAY_PASSWORD"),
Sandbox: true,
})
// Generate QR code
qr, _ := provider.GenerateQR(ctx, &fonepay.QRRequest{
Amount: 500,
OrderID: "TABLE-42",
Description: "Coffee Shop Order",
})
// Display QR to customer
// qr.QRImageData contains PNG bytes
// Save to file: os.WriteFile("payment.png", qr.QRImageData, 0644)All providers use the same verification pattern:
result, err := provider.VerifyPayment(ctx, &core.VerifyRequest{
EncodedParams: callbackParams, // Query params from callback
ExpectedAmount: 100,
})
if err != nil {
if errors.Is(err, core.ErrAmountMismatch) {
// Handle amount tampering
}
if errors.Is(err, core.ErrSignatureMismatch) {
// Handle signature failure
}
}
if result.Success {
fmt.Printf("Payment complete! TxnID: %s", result.TransactionID)
}go run ./examples/demo/go run ./examples/esewa/ # eSewa demo
go run ./examples/khalti/ # Khalti demo (needs KHALTI_SECRET_KEY)
go run ./examples/connectips/ # ConnectIPS demo (needs credentials)
go run ./examples/fonepay/ # FonePay demo| Field | Value |
|---|---|
| Test ID | 9806800001 - 9806800005 |
| Password | Nepal@123 |
| Token | 123456 |
| Secret Key | 8gBm/:&EnhH.1/q |
| Product Code | EPAYTEST |
| Field | Value |
|---|---|
| Test ID | 9800000001 - 9800000005 |
| MPIN | 1111 |
| OTP | 987654 |
| Secret Key | Get from Khalti Dashboard |
Requires merchant onboarding with Nepal Clearing House (NCHL).
You'll receive: Merchant ID, App ID, and .pfx certificate.
| Field | Sandbox Value |
|---|---|
| Merchant Code | NBQM |
| Secret Key | a7e3512f5032480a83137793cb2021dc |
Note: FonePay sandbox may be unreliable. Contact FonePay for production credentials.
# eSewa (optional - has sandbox defaults)
ESEWA_SECRET_KEY=8gBm/:&EnhH.1/q
ESEWA_PRODUCT_CODE=EPAYTEST
# Khalti (required)
KHALTI_SECRET_KEY=your_key_here
# ConnectIPS (required for ConnectIPS)
CONNECTIPS_MERCHANT_ID=your_merchant_id
CONNECTIPS_APP_ID=your_app_id
CONNECTIPS_APP_PASSWORD=your_password
CONNECTIPS_CERT_PATH=/path/to/creditor.pfx
CONNECTIPS_CERT_PASSWORD=cert_password
# FonePay (optional - has sandbox defaults)
FONEPAY_MERCHANT_CODE=NBQM
FONEPAY_SECRET_KEY=a7e3512f5032480a83137793cb2021dc.
├── core/ # Core contracts (dependency-free)
│ ├── models.go # Request/Response types
│ ├── errors.go # Sentinel errors & status constants
│ ├── provider.go # Provider interface
│ └── mock.go # Mock provider for testing
├── providers/
│ ├── esewa/ # eSewa implementation
│ ├── khalti/ # Khalti implementation
│ ├── connectips/ # ConnectIPS implementation
│ └── fonepay/ # FonePay implementation
└── examples/
├── demo/ # Combined demo (all providers)
├── esewa/ # eSewa standalone demo
├── khalti/ # Khalti standalone demo
├── connectips/ # ConnectIPS standalone demo
└── fonepay/ # FonePay standalone demo
// PaymentRequest initiates a payment
type PaymentRequest struct {
Amount int64 // Amount in smallest unit
Currency string // Usually "NPR"
OrderID string // Your unique order ID
Description string // Payment description
CallbackURL string // Where to redirect after payment
CustomerEmail string // Optional
CustomerPhone string // Optional
}
// PaymentResponse from InitiatePayment
type PaymentResponse struct {
IsFormPOST bool // true = POST form, false = redirect
TargetURL string // Where to send user
FormFields map[string]string // Form fields (if IsFormPOST)
TransactionID string // Provider's transaction ID
}
// VerifyResponse from VerifyPayment
type VerifyResponse struct {
Success bool
TransactionID string
OrderID string
VerifiedAmount int64
Currency string
Status string // StatusComplete, StatusPending, etc.
}import "github.com/voidarchive/nepal-payment-go/core"
// Sentinel errors
core.ErrInvalidConfig // Invalid provider configuration
core.ErrInvalidRequest // Invalid payment request
core.ErrProviderError // Provider returned an error
core.ErrAmountMismatch // Verified amount doesn't match expected
core.ErrSignatureMismatch // Signature verification failed
core.ErrPaymentNotFound // Transaction not found
core.ErrTimeout // Request timed out
core.ErrRateLimited // Rate limit exceeded
// Status constants
core.StatusComplete // "complete"
core.StatusPending // "pending"
core.StatusFailed // "failed"
core.StatusCancelled // "cancelled"
core.StatusExpired // "expired"
core.StatusRefunded // "refunded"# Run all tests
go test ./... -v
# Run specific provider tests
go test ./providers/esewa/... -v
go test ./providers/khalti/... -v
go test ./providers/connectips/... -v
go test ./providers/fonepay/... -v- Never expose secret keys in client-side code
- Always verify payments server-side before fulfilling orders
- Validate amounts to prevent price manipulation
- Use HTTPS in production for callback URLs
- Store certificates securely (ConnectIPS .pfx files)
Contributions are welcome! Feel free to submit a Pull Request.
MIT License - see LICENSE file for details.