Unified payment gateway library supporting eSewa, Khalti, ConnectIPS and more
Project description
Valoris Payment Gateway
A unified, reusable Python payment gateway library supporting eSewa, Khalti, ConnectIPS, and FonePay. Built for FastAPI apps but framework-agnostic.
Installation
# Base (eSewa, Khalti, FonePay)
pip install git+https://github.com/Techore/valoris-payment-gateway.git
# With Connect IPS support (requires cryptography for RSA signing)
pip install "valoris-payment-gateway[connectips] @ git+https://github.com/Techore/valoris-payment-gateway.git"
# All providers
pip install "valoris-payment-gateway[all] @ git+https://github.com/Techore/valoris-payment-gateway.git"
Supported Providers
| Provider | Status | Signature Method |
|---|---|---|
| eSewa | Ready | HMAC-SHA256 |
| Khalti | Ready | API Key Header |
| ConnectIPS | Ready | RSA (SHA256withRSA) |
| FonePay | Ready | HMAC-SHA512 |
Provider Configuration Reference
eSewa
| Parameter | Type | Required | Description |
|---|---|---|---|
secret_key |
str | Yes | HMAC secret key from eSewa merchant dashboard |
product_code |
str | Yes | Merchant product code (e.g. EPAYTEST for sandbox) |
sandbox |
bool | No | Use sandbox environment (default: True) |
Environment variables (recommended):
ESEWA_SECRET_KEY=8gBm/:&EnhH.1/q
ESEWA_PRODUCT_CODE=EPAYTEST
Sandbox test credentials:
- eSewa IDs:
9806800001-9806800005 - Password:
Nepal@123 - MPIN:
1122 - OTP:
123456
Verify kwargs: total_amount (float, NPR)
provider = create_provider(
"esewa",
secret_key=os.getenv("ESEWA_SECRET_KEY"),
product_code=os.getenv("ESEWA_PRODUCT_CODE"),
sandbox=True,
)
Khalti
| Parameter | Type | Required | Description |
|---|---|---|---|
secret_key |
str | Yes | Live secret key from Khalti admin dashboard |
sandbox |
bool | No | Use sandbox environment (default: True) |
website_url |
str | No | Your website URL (required by Khalti API) |
Environment variables (recommended):
KHALTI_SECRET_KEY=your_secret_key_here
KHALTI_WEBSITE_URL=https://yourapp.com
Sandbox: Get test keys from test-admin.khalti.com Production: Get live keys from admin.khalti.com
Sandbox test credentials:
- Khalti IDs:
9800000000-9800000005 - Password: (not needed for ePayment)
- MPIN:
1111 - OTP:
987654
Note: Amount in PaymentRequest is in NPR. The library auto-converts to paisa (x100) for the Khalti API.
Verify kwargs: pidx (str, from Khalti initiation response)
provider = create_provider(
"khalti",
secret_key=os.getenv("KHALTI_SECRET_KEY"),
website_url=os.getenv("KHALTI_WEBSITE_URL"),
sandbox=True,
)
ConnectIPS
| Parameter | Type | Required | Description |
|---|---|---|---|
merchant_id |
str | Yes | Merchant ID from NCHL |
app_id |
str | Yes | Application ID from NCHL |
app_name |
str | Yes | Application display name |
app_password |
str | Yes | Password for validation API (Basic Auth) |
pfx_path |
str | Yes | Path to CREDITOR.pfx certificate file |
pfx_password |
str | Yes | Password for the .pfx file |
sandbox |
bool | No | Use sandbox environment (default: True) |
Environment variables (recommended):
CONNECTIPS_MERCHANT_ID=550
CONNECTIPS_APP_ID=MER-550-APP-1
CONNECTIPS_APP_NAME=YourAppName
CONNECTIPS_APP_PASSWORD=your_password
CONNECTIPS_PFX_PATH=/path/to/CREDITOR.pfx
CONNECTIPS_PFX_PASSWORD=your_pfx_password
Important: Requires cryptography package. Install with pip install valoris-payment-gateway[connectips].
Note:
- CREDITOR.pfx is provided by NCHL for testing. You get your own certificate for production.
- Success/failure URLs must be pre-configured with NCHL support (not passed in the API).
- Amount is in NPR — auto-converted to paisa (x100) for the API.
Verify kwargs: amount (float, NPR)
provider = create_provider(
"connectips",
merchant_id=os.getenv("CONNECTIPS_MERCHANT_ID"),
app_id=os.getenv("CONNECTIPS_APP_ID"),
app_name=os.getenv("CONNECTIPS_APP_NAME"),
app_password=os.getenv("CONNECTIPS_APP_PASSWORD"),
pfx_path=os.getenv("CONNECTIPS_PFX_PATH"),
pfx_password=os.getenv("CONNECTIPS_PFX_PASSWORD"),
sandbox=True,
)
FonePay
| Parameter | Type | Required | Description |
|---|---|---|---|
merchant_code |
str | Yes | Merchant ID/PID from FonePay (via your bank) |
secret_key |
str | Yes | HMAC secret key from FonePay (via your bank) |
sandbox |
bool | No | Use sandbox environment (default: True) |
Environment variables (recommended):
FONEPAY_MERCHANT_CODE=your_merchant_code
FONEPAY_SECRET_KEY=your_secret_key
Note: Contact your bank to obtain merchant credentials. Separate keys for dev/production.
Sandbox test credentials:
- Mobile:
9801111111 - Password:
123 - MPIN:
123
Verify kwargs: amount (float), bid (str, bank/transaction ID), uid (str, FonePay unique ID)
provider = create_provider(
"fonepay",
merchant_code=os.getenv("FONEPAY_MERCHANT_CODE"),
secret_key=os.getenv("FONEPAY_SECRET_KEY"),
sandbox=True,
)
Usage
Common Flow (all providers)
import os
from valoris_payment import create_provider, PaymentRequest
# 1. Create provider
provider = create_provider("esewa", secret_key="...", product_code="...", sandbox=True)
# 2. Initiate payment
request = PaymentRequest(
amount=1000.0, # NPR
transaction_id="TXN-001", # your unique ID
product_name="Premium Plan",
success_url="https://yourapp.com/payment/success",
failure_url="https://yourapp.com/payment/failure",
)
result = await provider.initiate(request)
# result.payment_url -> redirect user here
# result.raw -> form data (for providers that need POST form)
# 3. Handle callback (on redirect back)
callback = await provider.handle_callback(query_params)
# callback.status, callback.transaction_id, callback.provider_ref
# 4. Verify payment (server-side confirmation)
verification = await provider.verify("TXN-001", total_amount=1000.0)
# verification.status -> PaymentStatus.COMPLETED / FAILED / etc.
FastAPI Integration Example
from fastapi import FastAPI, Query, Request
from fastapi.responses import RedirectResponse
from valoris_payment import create_provider, PaymentRequest, PaymentStatus
import os
app = FastAPI()
esewa = create_provider(
"esewa",
secret_key=os.getenv("ESEWA_SECRET_KEY"),
product_code=os.getenv("ESEWA_PRODUCT_CODE"),
sandbox=True,
)
khalti = create_provider(
"khalti",
secret_key=os.getenv("KHALTI_SECRET_KEY"),
website_url=os.getenv("KHALTI_WEBSITE_URL", "https://yourapp.com"),
sandbox=True,
)
@app.post("/payments/initiate")
async def initiate_payment(amount: float, transaction_id: str, provider_name: str = "esewa"):
providers = {"esewa": esewa, "khalti": khalti}
provider = providers[provider_name]
request = PaymentRequest(
amount=amount,
transaction_id=transaction_id,
product_name="Order Payment",
success_url="https://yourapp.com/payments/success",
failure_url="https://yourapp.com/payments/failure",
)
result = await provider.initiate(request)
return {"payment_url": result.payment_url, "provider_ref": result.provider_ref}
@app.get("/payments/success")
async def payment_success(request: Request):
params = dict(request.query_params)
# Detect provider from callback params and verify
if "data" in params: # eSewa
callback = await esewa.handle_callback(params)
elif "pidx" in params: # Khalti
callback = await khalti.handle_callback(params)
return {"status": callback.status, "transaction_id": callback.transaction_id}
PaymentRequest Extra Fields
Provider-specific fields can be passed via the extra dict:
eSewa
PaymentRequest(
...,
extra={
"tax_amount": "50",
"product_service_charge": "10",
"product_delivery_charge": "20",
}
)
Khalti
PaymentRequest(
...,
extra={
"website_url": "https://yourapp.com",
"customer_info": {"name": "John", "email": "john@example.com", "phone": "9800000000"},
}
)
ConnectIPS
PaymentRequest(
...,
extra={
"currency": "NPR",
"reference_id": "REF-001",
"remarks": "Order payment",
"particulars": "Product purchase",
}
)
FonePay
PaymentRequest(
...,
extra={
"currency": "NPR",
"r1": "Additional info",
"r2": "More info",
}
)
Adding a Custom Provider
from valoris_payment import PaymentProvider, register_provider
class MyProvider(PaymentProvider):
name = "myprovider"
async def initiate(self, request):
...
async def verify(self, transaction_id, **kwargs):
...
async def handle_callback(self, payload):
...
register_provider("myprovider", MyProvider)
Development
git clone git@github.com:Techore/valoris-payment-gateway.git
cd valoris-payment-gateway
pip install -e ".[dev]"
pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file valoris_payment_gateway-0.1.2.tar.gz.
File metadata
- Download URL: valoris_payment_gateway-0.1.2.tar.gz
- Upload date:
- Size: 14.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f2c73f0ccdb70478ba16a545c41e99ab793e72183223e8175b3ebd532b6347d
|
|
| MD5 |
5e8797f00c0ce361e330d4816548f475
|
|
| BLAKE2b-256 |
b175c67efe096f5d98648c58515bf44b034c81e60c67d76b126034de1ead923e
|
File details
Details for the file valoris_payment_gateway-0.1.2-py3-none-any.whl.
File metadata
- Download URL: valoris_payment_gateway-0.1.2-py3-none-any.whl
- Upload date:
- Size: 16.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c60dc2a4bb4ab9af5fb3b55fff7f3cdb854c4cc22754fb9bd15b606f1c8746f
|
|
| MD5 |
d4deabbaef76852ebe0a0ac16dff1189
|
|
| BLAKE2b-256 |
81ca9ec7327b9dad55e6bba17320924174e87e4cba5a4297c36b91981101d6d3
|