Requirements

StarterSpring BootJava
nepal-pay-spring-boot-3-starter 3.2.x or higher 17 or higher
nepal-pay-spring-boot-4-starter 4.0.x or higher 21 or higher

Install

Step 1 โ€” Add JitPack repository to pom.xml:

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

Step 2 โ€” Add dependency for your Spring Boot version:

Spring Boot 3.2+ (Java 17+):

<dependency>
    <groupId>com.github.sujankim.nepal-pay-spring-boot-starter</groupId>
    <artifactId>nepal-pay-spring-boot-3-starter</artifactId>
    <version>v0.5.0</version>
</dependency>

Spring Boot 4.x (Java 21+):

<dependency>
    <groupId>com.github.sujankim.nepal-pay-spring-boot-starter</groupId>
    <artifactId>nepal-pay-spring-boot-4-starter</artifactId>
    <version>v0.5.0</version>
</dependency>

Step 1 โ€” Add JitPack to settings.gradle:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

Step 2 โ€” Add to build.gradle:

Spring Boot 3.2+:

dependencies {
    implementation 'com.github.sujankim.nepal-pay-spring-boot-starter:nepal-pay-spring-boot-3-starter:v0.5.0'
}

Spring Boot 4.x:

dependencies {
    implementation 'com.github.sujankim.nepal-pay-spring-boot-starter:nepal-pay-spring-boot-4-starter:v0.5.0'
}

Step 1 โ€” Add JitPack to settings.gradle.kts:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}

Step 2 โ€” Add to build.gradle.kts:

Spring Boot 3.2+:

dependencies {
    implementation("com.github.sujankim.nepal-pay-spring-boot-starter:nepal-pay-spring-boot-3-starter:v0.5.0")
}

Spring Boot 4.x:

dependencies {
    implementation("com.github.sujankim.nepal-pay-spring-boot-starter:nepal-pay-spring-boot-4-starter:v0.5.0")
}
โ„น๏ธ The public API is identical in both starters. Only the internal Jackson version differs. You can switch starters without changing any application code.

Configure

Add to application.yml:

nepalpay:
  khalti:
    secret-key: ${KHALTI_SECRET_KEY}
    return-url:  ${KHALTI_RETURN_URL}
    website-url: ${YOUR_WEBSITE_URL}
    sandbox: true

  esewa:
    secret-key:   ${ESEWA_SECRET_KEY}
    product-code: ${ESEWA_PRODUCT_CODE}
    success-url:  ${ESEWA_SUCCESS_URL}
    failure-url:  ${ESEWA_FAILURE_URL}
    sandbox: true

  fonepay:
    merchant-code: ${FONEPAY_MERCHANT_CODE}
    secret-key:    ${FONEPAY_SECRET_KEY}
    return-url:    ${FONEPAY_RETURN_URL}
    sandbox: true

   connectips:
     merchant-id:  ${CONNECTIPS_MERCHANT_ID}
     app-id:       ${CONNECTIPS_APP_ID}
     app-name:     ${CONNECTIPS_APP_NAME}
     app-password: ${CONNECTIPS_APP_PASSWORD}
     pfx-path:     ${CONNECTIPS_PFX_PATH}
     pfx-password: ${CONNECTIPS_PFX_PASSWORD}
     sandbox: true

That is all. Spring Boot auto-configures the client beans automatically when keys are present. No @EnableNepalPay, no @Bean method, no config class.

Khalti Quickstart

@Service
@RequiredArgsConstructor
public class PaymentService {

    private final KhaltiClient khaltiClient; // โ† auto-injected

    // Step 1: Initiate
    public String startKhalti(String orderId, long amountNPR) {
        var res = khaltiClient.initiatePayment(
            KhaltiInitiateRequest.builder()
                .amount(amountNPR * 100L)        // NPR โ†’ paisa
                .purchaseOrderId(orderId)
                .purchaseOrderName("Your Product")
                .build()
        );
        // IMPORTANT: save res.pidx() to DB before returning!
        return res.paymentUrl();                  // redirect user here
    }

    // Step 2: Verify after callback
    public boolean verifyKhalti(String pidx) {
        return khaltiClient.lookupPayment(pidx).isPaymentSuccessful();
    }
}

See the full Khalti guide โ†’

eSewa Quickstart

@Service
@RequiredArgsConstructor
public class PaymentService {

    private final EsewaClient esewaClient; // โ† auto-injected

    // Step 1: Build signed form payload
    public EsewaFormPayload startEsewa(BigDecimal amountNPR) {
        String uuid = EsewaClient.generateTransactionUuid();
        // IMPORTANT: save uuid to DB before returning!
        return esewaClient.buildFormPayload(amountNPR, uuid);
        // Frontend POSTs form fields to payload.formActionUrl()
    }

    // Step 2: Verify after callback
    public boolean verifyEsewa(String encodedData) {
        return esewaClient.verifyCallback(encodedData).isPaymentSuccessful();
    }
}

See the full eSewa guide โ†’

Fonepay Quickstart

@Service
@RequiredArgsConstructor
public class PaymentService {

    private final FonepayClient fonepayClient; // โ† auto-injected

    // Step 1: Build signed redirect URL
    public String startFonepay(String orderId, double amountNPR) {
        String prn = "FP-" + orderId;
        // IMPORTANT: save prn to DB before returning!
        FonepayRedirectParams params = fonepayClient.buildRedirectParams(
            FonepayPaymentRequest.builder()
                .prn(prn)
                .amount(amountNPR)              // NPR directly โ€” not paisa!
                .remarks1("Your Product")
                .build()
        );
        return params.redirectUrl();            // redirect user here
    }

    // Step 2: Verify after callback
    public boolean verifyFonepay(FonepayCallbackResponse callback) {
        return fonepayClient.verifyCallback(callback).isPaymentSuccessful();
    }
}

See the full Fonepay guide โ†’

ConnectIPS Quickstart

โš ๏ธ Requires NCHL merchant registration. Contact connectips@nchl.com.np.
// Step 1: Build RSA-signed form payload
ConnectIpsFormPayload payload = connectIpsClient.buildFormPayload(
    ConnectIpsPaymentRequest.builder()
        .txnId("TXN-" + orderId)
        .amountNPR(100L)          // auto-converts to paisa
        .referenceId(orderId)
        .build()
);
// Frontend POSTs form to payload.formActionUrl()

// Step 2: Validate after callback
ConnectIpsValidateResponse res =
    connectIpsClient.validateTransaction(txnId, referenceId, txnAmtPaisa);
if (res.isPaymentSuccessful()) { /* mark as paid */ }

See the full ConnectIPS guide โ†’

Sandbox Credentials

eSewa Sandbox

FieldValue
eSewa ID9806800001
PasswordNepal@123
MPIN1122
Token123456
Secret Key8gBm/:&EnhH.1/q
Product CodeEPAYTEST

Khalti Sandbox

Get your test secret key from test-admin.khalti.com.

Fonepay Sandbox

Sandbox URL: https://dev.fonepay.com/api/merchantRequest
Merchant code and secret key are provided after registration with Fonepay.

Amount Units Per Gateway

This is the most common source of confusion. Each gateway uses different units.

Khalti
Paisa
NPR 100 โ†’ 10000L
eSewa
NPR
NPR 100 โ†’ "100.00"
Fonepay
NPR
NPR 100 โ†’ 100.0
ConnectIPS
Paisa
NPR 100 โ†’ 10000L