Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

7 Commits

Repository files navigation

QPay Java SDK

Maven CentralCILicense: MIT

Java SDK for the QPay V2 API. Provides a type-safe client with automatic token management, builder-pattern request construction, and comprehensive error handling.

Requirements

  • Java 17+
  • Maven or Gradle

Installation

Maven

<dependency>
<groupId>io.github.qpay-sdk</groupId>
<artifactId>qpay-java</artifactId>
<version>1.0.0</version>
</dependency>

Gradle

implementation 'io.github.qpay-sdk:qpay-java:1.0.0'

Gradle (Kotlin DSL)

implementation("io.github.qpay-sdk:qpay-java:1.0.0")

Quick Start

importmn.qpay.sdk.*;
importmn.qpay.sdk.models.*;
// Configure the clientQPayConfigconfig = QPayConfig.builder()
.baseURL("https://merchant.qpay.mn")
.username("YOUR_USERNAME")
.password("YOUR_PASSWORD")
.invoiceCode("YOUR_INVOICE_CODE")
.callbackURL("https://yoursite.com/payment/callback")
.build();
// Create the clientQPayClientclient = newQPayClient(config);
// Create an invoiceInvoiceResponseinvoice = client.createSimpleInvoice(
CreateSimpleInvoiceRequest.builder()
.invoiceCode(config.getInvoiceCode())
.senderInvoiceNo("ORDER-001")
.invoiceDescription("Payment for Order #001")
.amount(50000.0)
.callbackURL(config.getCallbackURL())
.build()
);
System.out.println("Invoice ID: " + invoice.getInvoiceId());
System.out.println("QR Image: " + invoice.getQrImage());
System.out.println("Short URL: " + invoice.getQPayShortUrl());

Configuration

Builder Pattern

QPayConfigconfig = QPayConfig.builder()
.baseURL("https://merchant.qpay.mn")
.username("YOUR_USERNAME")
.password("YOUR_PASSWORD")
.invoiceCode("YOUR_INVOICE_CODE")
.callbackURL("https://yoursite.com/payment/callback")
.build();

All fields are required. The builder throws IllegalArgumentException if any field is null or blank.

Environment Variables

QPayConfigconfig = QPayConfig.fromEnv();

Required environment variables:

VariableDescription
QPAY_BASE_URLQPay API base URL (e.g., https://merchant.qpay.mn)
QPAY_USERNAMEQPay merchant username
QPAY_PASSWORDQPay merchant password
QPAY_INVOICE_CODEDefault invoice code
QPAY_CALLBACK_URLPayment callback URL

Throws QPayException if any required variable is missing.

Token Management

The SDK handles token lifecycle automatically:

  • Auto-authentication: The first API call triggers authentication via POST /v2/auth/token.
  • Token reuse: Valid access tokens are reused across requests.
  • Auto-refresh: Expired access tokens are refreshed using the refresh token via POST /v2/auth/refresh.
  • Fallback: If refresh fails, the SDK re-authenticates with username/password.
  • Thread safety: Token access is synchronized for safe concurrent use.

You can also manage tokens manually:

// Manually get a tokenTokenResponsetoken = client.getToken();
// Manually refreshTokenResponserefreshed = client.refreshToken();

Usage Examples

Create Invoice (Full Options)

importmn.qpay.sdk.models.common.*;
CreateInvoiceRequestrequest = CreateInvoiceRequest.builder()
.invoiceCode("YOUR_INVOICE_CODE")
.senderInvoiceNo("ORDER-002")
.invoiceDescription("Detailed order payment")
.amount(100000.0)
.callbackURL("https://yoursite.com/payment/callback")
.allowPartial(false)
.allowExceed(false)
.note("VIP customer order")
.senderBranchCode("BRANCH_01")
.senderBranchData(
CreateInvoiceRequest.SenderBranchData.builder()
.name("Main Branch")
.email("branch@company.mn")
.phone("77001122")
.address(Address.builder()
.city("Ulaanbaatar")
.district("Sukhbaatar")
.street("Peace Avenue 22")
.build())
.build()
)
.invoiceReceiverData(
CreateInvoiceRequest.InvoiceReceiverData.builder()
.register("AA12345678")
.name("Customer Name")
.email("customer@mail.com")
.phone("99112233")
.build()
)
.lines(List.of(
InvoiceLine.builder()
.taxProductCode("TAX001")
.lineDescription("Product A")
.lineQuantity("2")
.lineUnitPrice("50000")
.build()
))
.build();
InvoiceResponseinvoice = client.createInvoice(request);
System.out.println("Invoice ID: " + invoice.getInvoiceId());
System.out.println("QR Text: " + invoice.getQrText());
// Show available payment deeplinksfor (Deeplinkurl : invoice.getUrls()) {
System.out.println(url.getName() + ": " + url.getLink());
}

Create Simple Invoice

CreateSimpleInvoiceRequestrequest = CreateSimpleInvoiceRequest.builder()
.invoiceCode("YOUR_INVOICE_CODE")
.senderInvoiceNo("ORDER-003")
.invoiceDescription("Simple payment")
.amount(25000.0)
.callbackURL("https://yoursite.com/payment/callback")
.build();
InvoiceResponseinvoice = client.createSimpleInvoice(request);

Cancel Invoice

client.cancelInvoice("INVOICE_ID");

Check Payment

importmn.qpay.sdk.models.common.Offset;
PaymentCheckRequestrequest = PaymentCheckRequest.builder()
.objectType("INVOICE")
.objectId("INVOICE_ID")
.offset(newOffset(1, 10))
.build();
PaymentCheckResponseresponse = client.checkPayment(request);
System.out.println("Payment count: " + response.getCount());
System.out.println("Paid amount: " + response.getPaidAmount());
for (PaymentCheckResponse.PaymentCheckRowrow : response.getRows()) {
System.out.println("Payment " + row.getPaymentId() + ": " + row.getPaymentStatus());
}

Get Payment Details

PaymentDetaildetail = client.getPayment("PAYMENT_ID");
System.out.println("Status: " + detail.getPaymentStatus());
System.out.println("Amount: " + detail.getPaymentAmount());
System.out.println("Currency: " + detail.getPaymentCurrency());
System.out.println("Wallet: " + detail.getPaymentWallet());

List Payments

PaymentListRequestrequest = PaymentListRequest.builder()
.objectType("INVOICE")
.objectId("INVOICE_ID")
.startDate("2024-01-01")
.endDate("2024-12-31")
.offset(newOffset(1, 20))
.build();
PaymentListResponseresponse = client.listPayments(request);
System.out.println("Total payments: " + response.getCount());
for (PaymentListResponse.PaymentListItemitem : response.getRows()) {
System.out.println(item.getPaymentId() + " - " + item.getPaymentAmount() + " MNT");
}

Cancel Payment

client.cancelPayment("PAYMENT_ID", "https://yoursite.com/cancel-callback", "Customer requested cancellation");

Refund Payment

client.refundPayment("PAYMENT_ID", "https://yoursite.com/refund-callback", "Product returned");

Create Ebarimt (Electronic Tax Receipt)

CreateEbarimtRequestrequest = CreateEbarimtRequest.builder()
.paymentId("PAYMENT_ID")
.ebarimtReceiverType("individual")
.ebarimtReceiver("AA12345678")
.districtCode("01")
.classificationCode("CL001")
.build();
EbarimtResponseebarimt = client.createEbarimt(request);
System.out.println("Ebarimt ID: " + ebarimt.getId());
System.out.println("Status: " + ebarimt.getBarimtStatus());
System.out.println("QR Data: " + ebarimt.getEbarimtQrData());
System.out.println("Lottery: " + ebarimt.getEbarimtLottery());

Cancel Ebarimt

EbarimtResponsecancelled = client.cancelEbarimt("PAYMENT_ID");
System.out.println("Cancelled status: " + cancelled.getBarimtStatus());

Error Handling

All API errors throw QPayException, which is a RuntimeException:

try {
client.createSimpleInvoice(request);
} catch (QPayExceptione) {
System.err.println("Status code: " + e.getStatusCode());
System.err.println("Error code: " + e.getCode());
System.err.println("Message: " + e.getMessage());
System.err.println("Raw body: " + e.getRawBody());
}

Error Code Constants

Use ErrorCodes constants for reliable error matching:

try {
client.getPayment("invalid-id");
} catch (QPayExceptione) {
if (ErrorCodes.PAYMENT_NOTFOUND.equals(e.getCode())) {
System.out.println("Payment does not exist");
} elseif (ErrorCodes.AUTHENTICATION_FAILED.equals(e.getCode())) {
System.out.println("Authentication failed, check credentials");
} else {
System.out.println("Unexpected error: " + e);
}
}

Common Error Codes

CodeDescription
AUTHENTICATION_FAILEDInvalid username/password
INVOICE_NOTFOUNDInvoice does not exist
INVOICE_PAIDInvoice already paid
INVOICE_ALREADY_CANCELEDInvoice already cancelled
INVOICE_CODE_INVALIDInvalid invoice code
PAYMENT_NOTFOUNDPayment does not exist
PAYMENT_NOT_PAIDPayment not yet completed
PAYMENT_ALREADY_CANCELEDPayment already cancelled
INVALID_AMOUNTInvalid payment amount
PERMISSION_DENIEDInsufficient permissions
MERCHANT_NOTFOUNDMerchant does not exist
MERCHANT_INACTIVEMerchant account is inactive
EBARIMT_NOT_REGISTEREDEbarimt not registered

Utility: asQPayError

try {
// some operation that might throw
} catch (Exceptione) {
QPayExceptionqe = QPayException.asQPayError(e);
if (qe != null) {
// Handle QPay-specific error
} else {
// Handle other errors
}
}

API Reference

QPayClient Methods

MethodHTTPPathDescription
getToken()POST/v2/auth/tokenAuthenticate and get token pair
refreshToken()POST/v2/auth/refreshRefresh access token
createInvoice(CreateInvoiceRequest)POST/v2/invoiceCreate invoice with full options
createSimpleInvoice(CreateSimpleInvoiceRequest)POST/v2/invoiceCreate invoice with minimal fields
cancelInvoice(String)DELETE/v2/invoice/{id}Cancel an invoice
getPayment(String)GET/v2/payment/{id}Get payment details
checkPayment(PaymentCheckRequest)POST/v2/payment/checkCheck payment status
listPayments(PaymentListRequest)POST/v2/payment/listList payments
cancelPayment(String, String, String)DELETE/v2/payment/cancel/{id}Cancel a card payment
refundPayment(String, String, String)DELETE/v2/payment/refund/{id}Refund a card payment
createEbarimt(CreateEbarimtRequest)POST/v2/ebarimt_v3/createCreate electronic tax receipt
cancelEbarimt(String)DELETE/v2/ebarimt_v3/{id}Cancel electronic tax receipt

Custom HttpClient

You can provide a custom HttpClient for advanced configuration (proxy, timeouts, etc.):

HttpClientcustomHttp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
QPayClientclient = newQPayClient(config, customHttp);

Testing

mvn test

The test suite uses JUnit 5 and OkHttp MockWebServer to test all client methods, error handling, and automatic token management without requiring a live QPay API connection.

License

MIT License - see LICENSE for details.

About

QPay V2 Payment API SDK for Java/Kotlin

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages