Accept your first payment.
Add Hamro Pay’s hosted checkout to your website or app. Your server creates the payment, your customer chooses how to pay, and your server confirms the result.
Before you start
You’ll need a backend that can make HTTPS requests, a test merchant account, and pages to return customers to after checkout. Create a test merchant account.
1 Set up your test account
Follow these steps to create and verify your test merchant, get your credentials, and reach the checkout endpoints.
1.1. Create and Verify Your Test Merchant Account
Register your merchant account through the UAT Merchant Portal to start your checkout integration.
1.2. Get Credentials and Configure Webhook
Once your merchant account is verified, navigate to:
You will find:
- Client ID
- Client Secret
- Client API Key
- Merchant ID

Configure your Webhook:
- Enter your Target Endpoint URL.
- Save the configuration.
- The Webhook Secret (
merchantWebHookSigningSecret) will be generated/displayed after saving. - Use this secret to verify webhook requests from Hamro Pay.

1.3. Integrate Hamro Pay Checkout
Use the credentials obtained from the Merchant Portal and the following UAT endpoints to connect your backend and frontend:
| Environment Variable | UAT Value | Action |
|---|---|---|
| BASE_URL | https://uat-payclient.hamropatro.com/ | |
| GATEWAY_URL | https://uat-checkout-pay.hamropatro.com/ |
Keep secret values on your server. Collect the credentials and UAT endpoints from the steps above into your backend’s environment configuration.
# .env — your server only; never commit this file
# Replace every REPLACE_ME value with your test configuration.
HAMRO_MERCHANT_ID=REPLACE_ME
HAMRO_CLIENT_ID=REPLACE_ME
HAMRO_API_KEY=REPLACE_ME
HAMRO_CLIENT_SECRET=REPLACE_ME
HAMRO_USER_SECRET=REPLACE_ME
# Obtain full URLs from Hamro Pay during test onboarding.
HAMRO_CREATE_SESSION_URL=REPLACE_ME
HAMRO_GET_TRANSACTION_URL=REPLACE_ME
HAMRO_GATEWAY_URL=REPLACE_ME
# Set these to pages in your application.
CHECKOUT_SUCCESS_URL=https://your-domain.example/payment/success
CHECKOUT_FAILURE_URL=https://your-domain.example/payment/failure2 Create a payment session
Build a request from the order saved on your server. Set the amount in paisa,
and give each order a unique merchantTxnId
of no more than 25 characters. This example charges NPR 125.50 (12550 paisa).
{
"merchantTxnId": "ORDER-1001",
"merchantId": "YOUR_TEST_MERCHANT_ID",
"transactionAmount": "12550",
"failedRedirectionUrl": "https://your-domain.example/payment/failure",
"successRedirectionUrl": "https://your-domain.example/payment/success",
"remarks": "Order ORDER-1001"
}Building a platform that hosts other merchants? Use the selling merchant’s ID and explore Payments for Platforms to allocate your platform’s share automatically.
Sign the request
A signature lets Hamro Pay verify your request. Join the five fields in the order shown below, sign them with your client secret using HMAC-SHA512, and encode the result as Base64.
import { createHmac } from 'node:crypto';
export function signSession(request, clientId, clientApiKey, clientSecret) {
const message = [
request.merchantTxnId,
request.transactionAmount,
request.merchantId,
clientId,
clientApiKey,
].join(',');
return createHmac('sha512', clientSecret)
.update(message, 'utf8')
.digest('base64');
}
// request is the exact JSON object you send to Create Session.
// Read the client id, api key and secret from your server environment.Send the JSON body to your confirmed Create Session endpoint, using its confirmed HTTP method and these headers. Sign the values you actually send; don’t change the amount or field values after signing.
Signature: YOUR_GENERATED_BASE64_SIGNATURE
Client-Id: YOUR_TEST_CLIENT_ID
Client-API-Key: YOUR_TEST_API_KEY
Content-Type: application/jsonsessionId and
merchantId. Save the order’s transaction ID for step 4.
3 Send your customer to checkout
Use sessionId from the
response in step 2, and compute the checkout token
as a form signature. Pass only the checkout fields to your page, never the full session response.
import { createHmac } from 'node:crypto';
// Run on your server after a successful Create Session call.
// session is the parsed response from Hamro Pay.
export function checkoutFields(session, clientId, clientApiKey, clientSecret) {
const tokenMessage = [
session.merchantId,
session.merchantTxnId,
session.sessionId,
session.transactionAmount,
clientId,
clientApiKey,
].join(',');
const token = createHmac('sha512', clientSecret)
.update(tokenMessage, 'utf8')
.digest('base64');
const fields = {
merchant_id: session.merchantId,
session_id: session.sessionId,
token,
merchant_transaction_id: session.merchantTxnId,
};
for (const [name, value] of Object.entries(fields)) {
if (typeof value !== 'string' || value.length === 0) {
throw new Error('Missing checkout field: ' + name);
}
}
return fields;
}
// Render only these fields in the checkout form.
// Never serialize the full session response into the page.The token is the form signature generated
from merchant_id, merchant_transaction_id,
session_id, transaction_amount, client_id, client_api_key using your client secret.
Render these fields in your checkout form. When the customer clicks the button, the browser submits the form to Hamro Pay’s checkout gateway.
<!-- Template: replace these values on your server.
HTML-escape every value before inserting it.
Use only the checkout URL confirmed by Hamro Pay. -->
<form method="POST"
action="{{ HAMRO_GATEWAY_URL }}"
enctype="application/x-www-form-urlencoded">
<input type="hidden" name="merchant_id"
value="{{ merchant_id }}">
<input type="hidden" name="session_id"
value="{{ session_id }}">
<input type="hidden" name="token"
value="{{ token }}">
<input type="hidden" name="merchant_transaction_id"
value="{{ merchant_transaction_id }}">
<button type="submit">Pay with Hamro Pay</button>
</form>The double braces are template variables. Replace them using your backend’s HTML template engine and
its HTML escaping. Keep merchant_transaction_id
identical to the merchantTxnId used to create the session.
4 Confirm payment before fulfilling the order
The return URL includes MerchantTxnId
(capital M and T). Use it to look up your saved order. A success-page visit alone is not proof of payment.
Call your confirmed Get Transaction endpoint from your server with the following body. Generate a new
signature in this order: merchantTxnId,merchantId,client_id,clientApiKey.
Use the same HMAC-SHA512 and Base64 process, and your client secret.
{
"merchantId": "YOUR_TEST_MERCHANT_ID",
"merchantTxnId": "ORDER-1001"
}Fulfill only when the server response reports COMPLETED
and its transaction ID and amount match your saved order. Use merchantTransactionId
from the response—the name differs from the request field.
// Run on your server after calling Get Transaction.
// payment = Hamro Pay's response, not the browser's data.
// order = your saved order, loaded using its transaction ID.
export function isPaid(payment, order) {
if (!payment || !order || typeof order.merchantTxnId !== 'string'
|| !order.merchantTxnId) return false;
if (!['number', 'string'].includes(typeof payment.amount)
|| !['number', 'string'].includes(typeof order.amount)) return false;
const amount = Number(payment.amount);
const expected = Number(order.amount);
return payment.status === 'COMPLETED'
&& payment.merchantTransactionId === order.merchantTxnId
&& Number.isFinite(amount)
&& amount > 0
&& Number.isFinite(expected)
&& amount === expected;
}
// Fulfill only after isPaid returns true, and save the paid state
// atomically so repeated checks or webhooks cannot fulfill twice.Keep PENDING or
PROCESSING orders unpaid while awaiting
confirmation. Show a retry option for FAILED.
Return-page checks work when the customer returns; add verified webhooks to handle customers who close the page.
Test the whole flow
Create a test user, complete the checkout with the test credentials below, then run through these cases before switching to live credentials.
Test Your Checkout Integration
Perform end-to-end Checkout transactions to ensure your backend and webhooks respond correctly:
Use these test credentials to complete the checkout:
Test the complete lifecycle scenarios:
- Successful Payment Flow
Verify browser redirect and webhook payload with
status: COMPLETED. - Failed / Cancelled Flow Verify error handling when transaction is aborted by user.
- Pending / Timeout Flow Verify polling with Get Transaction API or asynchronous webhook delivery.
Run through these cases
- Successful payment: the correct amount is shown and your server confirms the order as paid.
- Failed or canceled payment: the customer can try again and the order stays unpaid.
- Pending payment: the order stays unpaid until a later server check confirms completion.
- Repeated return or notification: the order is fulfilled only once.
- Changed transaction ID or amount: your server rejects the mismatch.
- Customer closes checkout: verified webhooks or server reconciliation still update the order.
Ready for real payments?
Ask Hamro Pay to approve live onboarding and confirm your live credentials, endpoints, payment methods, and transaction rate. Replace test settings, verify your production return URLs and webhook, and confirm a live payment with your team.
Contact the integration teamNeed field details?
They’re all in the API reference.
Company
Code of Conduct
Hamro Pay Support
Chat Support: (24x7)
Call support: (6am-10pm)
Grievance officer
Follow Us
© 2026 HamroPay. All Rights Reserved.