Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

surepay-java-sdk

Official Java client library for the SurePay Merchant API.

CI Maven

Requirements

  • Java 17+
  • Jackson Databind (only runtime dependency)

Install

Maven

Add the GitHub Packages repository and dependency to your pom.xml:

<repositories>
  <repository>
    <id>github</id>
    <url>https://maven.pkg.github.com/surepay-one/surepay-java-sdk</url>
  </repository>
</repositories>

<dependencies>
  <dependency>
    <groupId>one.surepay</groupId>
    <artifactId>surepay-java-sdk</artifactId>
    <version>0.1.0</version>
  </dependency>
</dependencies>

Gradle

repositories {
    maven {
        url = uri("https://maven.pkg.github.com/surepay-one/surepay-java-sdk")
        credentials {
            username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")
            password = project.findProperty("gpr.token") ?: System.getenv("GITHUB_TOKEN")
        }
    }
}

dependencies {
    implementation 'one.surepay:surepay-java-sdk:0.1.0'
}

GitHub Packages requires authentication even for public packages. Create a personal access token with read:packages scope.

Quick start

import one.surepay.sdk.SurePay;
import one.surepay.sdk.model.*;

SurePay client = SurePay.builder(
    System.getenv("SUREPAY_API_KEY"),    // tpay_live_... or tpay_test_...
    System.getenv("SUREPAY_API_SECRET")  // tpay_sec_... — enables auto HMAC signing
).build();

// Check wallet balance
Balance bal = client.balance.get();
System.out.printf("Available: %,d %s%n", bal.available(), bal.currency());

// Create a deposit order (thu hộ)
Deposit dep = client.deposits.create(
    CreateDepositRequest.builder(100_000L)
        .requestId("ORD-20260610-001")
        .build()
);
System.out.println("Checkout URL: " + dep.checkoutUrl());

// Create a payout (chi hộ)
try {
    Payout pay = client.payouts.create(
        CreatePayoutRequest.builder(500_000L, "VCB", "1234567890", "NGUYEN VAN A", "Salary June 2026")
            .build()
    );
    System.out.println("Payout ID: " + pay.id());
} catch (SurePayException e) {
    if (e.isInsufficientBalance()) {
        System.out.println("Not enough balance — top up first");
    }
}

Configuration

SurePay client = SurePay.builder(apiKey, apiSecret)
    .baseUrl("https://api.surepay.one/merchant/v1")  // override for local/staging
    .timeout(Duration.ofSeconds(15))
    .maxRetries(3)   // retries on 5xx and network errors
    .build();
Option Default Description
baseUrl(String) https://api.surepay.one/merchant/v1 Override base URL for local dev or staging
timeout(Duration) 30s HTTP request timeout
maxRetries(int) 3 Retry attempts on 5xx and network errors

Authentication

Every request requires an API key sent as an X-API-Key header. When apiSecret is provided to builder(), every outgoing request is automatically signed with HMAC-SHA256 — the X-Signature and X-Timestamp headers are attached with no extra code needed.

API reference

Balance

client.balance.get()

Get current wallet balance. Requires: balance:read scope.

Balance bal = client.balance.get();
// bal.balance()   — total wallet balance in VND
// bal.hold()      — reserved for in-flight transactions
// bal.available() — bal.balance() - bal.hold()
// bal.currency()  — always "VND"

Deposits

client.deposits.list(params)

Paginated list of deposit (thu hộ) orders. Requires: deposits:read scope.

ListResult<Deposit> result = client.deposits.list(
    ListDepositsParams.create()
        .page(1)
        .pageSize(20)
        .status("success")       // pending|processing|success|failed|expired|cancelled
        .search("ORD-2026")
        .fromDate("2026-06-01")  // YYYY-MM-DD
        .toDate("2026-06-30")
);
// result.items()      — List<Deposit>
// result.total()      — total matching records
// result.totalPages() — total pages

client.deposits.create(req)

Create a new deposit order. Returns a checkoutUrl (redirect) and qrCode (VietQR). Requires: deposits:write scope.

Deposit dep = client.deposits.create(
    CreateDepositRequest.builder(100_000L)   // amount in VND, required
        .requestId("ORD-20260610-001")       // your order ID — optional, for idempotency
        .currency("VND")                     // optional, default "VND"
        // Chính chủ verification — all optional:
        .senderBankId("970436")
        .senderBankName("Vietcombank")
        .senderAccount("1234567890")
        .senderName("NGUYEN VAN A")
        .build()
);
System.out.println(dep.checkoutUrl());
System.out.println(dep.qrCode());

Response fields:

Field Type Description
id() String SurePay transaction UUID
requestId() String Your order ID
amount() long Amount in VND
status() DepositStatus pending, processing, success, failed, expired, cancelled
fee() long Transaction fee in VND
checkoutUrl() String Redirect URL for payer
qrCode() String VietQR data string
accountNumber() String Receiving bank account number
accountName() String Receiving account holder name
bankBin() String Receiving bank BIN
expiresAt() Instant Order expiry time
isOwnerVerified() Boolean Chính chủ result — null until transfer received

client.deposits.get(id)

Fetch a single deposit order by UUID. Requires: deposits:read scope.

Deposit dep = client.deposits.get("uuid-here");
// dep.status() == DepositStatus.PENDING | DepositStatus.SUCCESS | ...

Payouts

client.payouts.list(params)

Paginated list of payout (chi hộ) orders. Requires: payouts:read scope.

ListResult<Payout> result = client.payouts.list(
    ListPayoutsParams.create()
        .page(1)
        .pageSize(20)
        .status("success")      // pending|processing|success|failed
        .fromDate("2026-06-01")
        .toDate("2026-06-30")
);

client.payouts.create(req)

Initiate a payout bank transfer. Funds are deducted from your wallet immediately on success. Requires: payouts:write scope.

Payouts are irreversible once status moves past pending. Verify bank details with client.bankInquiry.verify() first.

Payout pay = client.payouts.create(
    CreatePayoutRequest.builder(
            500_000L,        // amount in VND, required
            "VCB",           // bank code, required (VCB, MB, TCB, ACB, ...)
            "1234567890",    // bank account, required
            "NGUYEN VAN A",  // full name in UPPERCASE, required
            "Salary June"    // description / transfer memo, required
        )
        .bankName("Vietcombank")  // optional
        .build()
);

client.payouts.get(id)

Fetch a single payout by UUID. Requires: payouts:read scope.

Payout pay = client.payouts.get("uuid-here");
// pay.status() == PayoutStatus.PENDING | PayoutStatus.SUCCESS | ...

Bank Inquiry

client.bankInquiry.verify(req)

Look up the account holder name for a bank account. Call this before creating a payout to confirm the recipient. Requires: payouts:read scope.

BankInquiryResult info = client.bankInquiry.verify(
    BankInquiryRequest.of("VCB", "1234567890")
);
System.out.println("Account name: " + info.accountName());

Idempotency

Pass an idempotency key as a second argument to any create() method. The key is forwarded as an Idempotency-Key header — safe to retry on network errors without risk of duplicate transactions.

Deposit dep = client.deposits.create(
    CreateDepositRequest.builder(100_000L).requestId("ORD-20260610-001").build(),
    "ORD-20260610-001"  // idempotency key
);

Webhook verification

Every inbound webhook event from SurePay is HMAC-signed. Pass the raw request body bytes (before any JSON parsing) to client.webhooks.verify():

// Spring Boot example
@PostMapping("/webhook/surepay")
public ResponseEntity<Void> handleWebhook(HttpServletRequest request) throws IOException {
    byte[] body = request.getInputStream().readAllBytes();

    if (!client.webhooks.verify(body)) {
        return ResponseEntity.status(401).build();
    }

    // Parse event type
    Map<String, Object> event = new ObjectMapper().readValue(body, Map.class);
    String eventType = (String) event.get("event");

    switch (eventType) {
        case "deposit.success", "deposit.failed" -> {
            // handle deposit event
        }
        case "payout.success", "payout.failed" -> {
            // handle payout event
        }
    }
    return ResponseEntity.ok().build();
}

Or pass the X-Surepay-Signature header value explicitly:

String sig = request.getHeader("X-Surepay-Signature");
boolean valid = client.webhooks.verify(body, sig);

Error handling

try {
    Payout pay = client.payouts.create(req);
} catch (SurePayException e) {
    // Convenience helpers
    if (e.isNotFound())            { /* 404 not_found */ }
    if (e.isRateLimit())           { /* 429 rate_limit_exceeded */ }
    if (e.isInsufficientBalance()) { /* 422 insufficient_balance */ }
    if (e.isDuplicate())           { /* 409 duplicate_request */ }

    // Full details
    System.out.printf("HTTP %d  code=%s  %s%n",
        e.getStatusCode(), e.getCode(), e.getMessage());
}

Error codes:

HTTP getCode() Meaning
400 validation_error Invalid request body or parameters
401 unauthorized Missing or invalid API key
401 signature_invalid HMAC signature failed or timestamp > 5 min
403 permission_denied API key lacks required scope
403 ip_not_allowed Request IP not in allowlist
404 not_found Resource not found
409 duplicate_request Idempotency key conflict
422 insufficient_balance Top up wallet first
422 invalid_state_transition Operation not allowed for current status
429 rate_limit_exceeded Slow down — back off and retry
500 internal_error Server error

Use ErrorCode.* constants to compare codes without hard-coding strings:

if (ErrorCode.INSUFFICIENT_BALANCE.equals(e.getCode())) { ... }

HMAC signing

When apiSecret is set, all requests are signed automatically. The signing algorithm for manual use:

signingString = UNIX_TIMESTAMP + "\n" + METHOD + "\n" + PATH + "\n" + hex(sha256(body))
signature     = "sha256=" + hex(hmac-sha256(apiSecret, signingString))

Attach as headers: X-Signature: <signature> and X-Timestamp: <unix_timestamp>.

Signatures expire after 300 seconds — generate per-request, never cache or reuse.

License

MIT

About

Official Java SDK for the SurePay Payment Gateway API

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages