Java

Pine Labs Server SDK for Java.

pinelabs-java is the official Java SDK for the Pine Labs Online Payment Gateway (Plural). It is generated from the same OpenAPI spec that powers this documentation, so every operation, request, and response model is fully typed.

Install

XML
<dependency>
  <groupId>com.pinelabsonline</groupId>
  <artifactId>pinelabs-java</artifactId>
  <version>0.1.0</version>
</dependency>

Quickstart

The Pine Labs API uses OAuth2 with the client_credentials grant. Exchange your <client-id> / <client-secret> for an access token, then pass it to PinelabsApiClient.

Java
import com.pinelabsonline.api.PinelabsApiClient;
import com.pinelabsonline.api.resources.authentication.requests.GenerateTokenRequest;
import com.pinelabsonline.api.resources.orders.requests.CreateOrderRequest;
import com.pinelabsonline.api.types.Amount;

public class Main {
    public static void main(String[] args) {
        String baseUrl = "https://pluraluat.v2.pinepg.in"; // UAT
        // String baseUrl = "https://api.pluralpay.in";     // Production

        // 1. Bootstrap client used only to fetch a token. The /token endpoint
        //    does not require authentication, so we pass an empty token here.
        PinelabsApiClient auth = PinelabsApiClient.builder()
            .token("")
            .url(baseUrl)
            .build();

        var tokenResponse = auth.authentication().generateToken(
            GenerateTokenRequest.builder()
                .grantType("client_credentials")
                .clientId(System.getenv("PINELABS_CLIENT_ID"))
                .clientSecret(System.getenv("PINELABS_CLIENT_SECRET"))
                .build()
        );

        // 2. Build an authenticated client
        PinelabsApiClient client = PinelabsApiClient.builder()
            .token(tokenResponse.getAccessToken())
            .url(baseUrl)
            .build();

        // 3. Call any operation
        var order = client.orders().createOrder(
            CreateOrderRequest.builder()
                .merchantOrderReference("order-001")
                .orderAmount(Amount.builder()
                    .value(50000)        // ₹500.00
                    .currency("INR")
                    .build())
                .build()
        );

        System.out.println(order);
    }
}

Environments

EnvironmentBase URL
UAThttps://pluraluat.v2.pinepg.in
Productionhttps://api.pluralpay.in

Pass the URL via .url(baseUrl) on the builder. The exported Environment enum currently only contains PRODUCTION; for UAT, set the URL explicitly.

Java
import com.pinelabsonline.api.core.Environment;

PinelabsApiClient client = PinelabsApiClient.builder()
    .token("<access-token>")
    .environment(Environment.PRODUCTION)
    .build();

Auto-refreshing token

For long-running services, cache the token and refresh it before it expires. The builder accepts a Supplier<String> for token — it is invoked on every request. Use a separate bootstrap auth client (without a supplier) to fetch the token, so the supplier never recurses into itself.

Java
import com.pinelabsonline.api.PinelabsApiClient;
import com.pinelabsonline.api.resources.authentication.requests.GenerateTokenRequest;

import java.time.Instant;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;

public class PinelabsClientFactory {

    private static final String BASE_URL = "https://pluraluat.v2.pinepg.in";

    // Bootstrap client used only to fetch tokens. It has no supplier, so calling
    // generateToken() from inside the supplier cannot recurse.
    private static final PinelabsApiClient AUTH = PinelabsApiClient.builder()
        .token("")
        .url(BASE_URL)
        .build();

    private static final ReentrantLock LOCK = new ReentrantLock();
    private static volatile String cachedToken;
    private static volatile Instant expiresAt = Instant.EPOCH;

    private static final Supplier<String> tokenSupplier = () -> {
        // Refresh ~30s before expiry
        if (cachedToken != null && Instant.now().isBefore(expiresAt.minusSeconds(30))) {
            return cachedToken;
        }
        LOCK.lock();
        try {
            if (cachedToken != null && Instant.now().isBefore(expiresAt.minusSeconds(30))) {
                return cachedToken;
            }
            var r = AUTH.authentication().generateToken(
                GenerateTokenRequest.builder()
                    .grantType("client_credentials")
                    .clientId(System.getenv("PINELABS_CLIENT_ID"))
                    .clientSecret(System.getenv("PINELABS_CLIENT_SECRET"))
                    .build()
            );
            cachedToken = r.getAccessToken();
            expiresAt = Instant.now().plusSeconds(r.getExpiresIn());
            return cachedToken;
        } finally {
            LOCK.unlock();
        }
    };

    public static PinelabsApiClient client() {
        return PinelabsApiClient.builder()
            .token(tokenSupplier)
            .url(BASE_URL)
            .build();
    }
}
Use a separate bootstrap client

Always create a dedicated auth client (with token("")) to fetch tokens. Calling generateToken() on a client that itself uses the supplier callable will recurse and hang.

Sub-clients

PinelabsApiClient exposes one sub-client per API tag via accessor methods:

Sub-clientPurpose
client.authentication()OAuth token generation
client.orders()Create, capture, cancel, and fetch orders
client.refunds()Create and look up refunds
client.settlements()Settlement reports + UTR lookup
client.checkout()Hosted-checkout related operations
client.paymentLinks()Single + bulk payment links
client.cardPayments()Direct card payment + OTP flow
client.bnpl()Buy-Now-Pay-Later eligibility and flows
client.convenienceFee()Convenience-fee config and computation
client.eChallans()Government e-challan integration
client.applePay()Apple Pay session + decryption
client.internationalPayments()Cross-border (DCC / MCC) payments
client.customers()Customer profile management
client.tokenization()Card / network tokenization
client.payouts()Payouts: balance, create, cancel, list
client.subscriptionsPlans()Recurring-billing plans
client.subscriptionsSubscriptions()Subscription lifecycle
client.subscriptionsPresentations()Subscription debit presentations
client.payByPoints()Loyalty / points-based payments
client.affordabilitySuite()EMI / offer eligibility
client.splitSettlements()Split settlements between sub-merchants

For the full operation list and request/response schemas, see the API reference.

Recipes

Create a payment link

Java
import com.pinelabsonline.api.resources.paymentlinks.requests.CreatePaymentLinkRequest;
import com.pinelabsonline.api.types.Amount;

var link = client.paymentLinks().createPaymentLink(
    CreatePaymentLinkRequest.builder()
        .merchantPaymentLinkReference("link-001")
        .amount(Amount.builder().value(50000).currency("INR").build())
        .description("Order #001")
        .build()
);
System.out.println(link.getShortUrl());

Fetch an order

Java
import com.pinelabsonline.api.resources.orders.requests.GetOrderByIdRequest;

var order = client.orders().getOrderById(
    GetOrderByIdRequest.builder()
        .orderId("v1-241010055924-aa-AHbN0s")
        .build()
);
System.out.println(order);

Refund an order

Java
import com.pinelabsonline.api.resources.refunds.requests.CreateRefundRequest;
import com.pinelabsonline.api.resources.refunds.types.CreateRefundRequestOrderAmount;

var refund = client.refunds().createRefund(
    CreateRefundRequest.builder()
        .orderId("v1-241010055924-aa-AHbN0s")
        .merchantOrderReference("refund-001")
        .orderAmount(CreateRefundRequestOrderAmount.builder()
            .value(400)
            .currency("INR")
            .build())
        .build()
);
System.out.println(refund);

Error handling

All HTTP errors derive from PinelabsApiApiException. The SDK also exposes typed subclasses per status code so you can handle specific failures cleanly.

Java
import com.pinelabsonline.api.core.PinelabsApiApiException;
import com.pinelabsonline.api.core.PinelabsApiException;
import com.pinelabsonline.api.errors.NotFoundError;
import com.pinelabsonline.api.errors.UnauthorizedError;

try {
    client.orders().getOrderById(
        GetOrderByIdRequest.builder().orderId("missing").build()
    );
} catch (NotFoundError e) {
    System.err.println("order does not exist: " + e.body());
} catch (UnauthorizedError e) {
    System.err.println("token expired or invalid");
} catch (PinelabsApiApiException e) {
    System.err.println("HTTP " + e.statusCode() + ": " + e.body());
} catch (PinelabsApiException e) {
    System.err.println("Error: " + e.getMessage());
}

PinelabsApiApiException exposes statusCode(), body() (parsed when the server returns JSON), and headers().

Per-request options

Every operation accepts a RequestOptions argument for ad-hoc overrides — useful for per-call timeouts, propagating trace IDs, or disabling retries on idempotency-sensitive flows.

Java
import com.pinelabsonline.api.core.RequestOptions;
import java.time.Duration;

var options = RequestOptions.builder()
    .timeout(Duration.ofSeconds(10))
    .addHeader("x-correlation-id", "req-abc")
    .build();

client.orders().createOrder(
    CreateOrderRequest.builder()
        .merchantOrderReference("order-002")
        .orderAmount(Amount.builder().value(1000).currency("INR").build())
        .build(),
    options
);

Timeouts

The default timeout is 60 seconds. Override at the client or per-request level:

Java
PinelabsApiClient client = PinelabsApiClient.builder()
    .token("<access-token>")
    .timeout(20)   // seconds
    .build();

Custom HTTP client

Java
import com.pinelabsonline.api.PinelabsApiClient;
import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;

OkHttpClient http = new OkHttpClient.Builder()
    .connectTimeout(5, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .build();

PinelabsApiClient client = PinelabsApiClient.builder()
    .token("<access-token>")
    .httpClient(http)
    .build();

Source & support

New chat
Responses are generated using AI and may contain mistakes.
Hi! I'm Pine, your AI developer assistant. Ask me anything about Pine Labs APIs, integrations, or troubleshooting.

Tip: you can create a new chat with + E