Payper is an unofficial Java SDK for the PayPal REST API. It is tested and fully supported on current Java LTS releases (17, 21 and 25) and compatible with later Java 17+ runtimes. The library is built for multithreaded, high-concurrency environments: it uses immutable domain models and exposes a fluent API for concise, readable code.
A concise, scannable summary of Payper's main capabilities. The table below helps you scan features quickly — expand the details if you want more context.
| Feature | Summary |
|---|---|
| 🧠Java LTS compatibility (17, 21, 25) | Tested and supported on current Java LTS releases — 17, 21 and 25 — and compatible with Java 17+ runtimes. |
| ⚡ Modern Java HTTP client | Uses the platform's modern HTTP client for HTTP/2, improved TLS and efficient connection handling. |
| đź”’ Immutable, thread-safe models | Domain objects are immutable and safe to share across threads. |
| ✨ Fluent, expressive API | Builder patterns and method chaining for concise, readable call flows. |
| 🔑 Automatic OAuth 2.0 token management | Automatic acquisition, caching and refresh of OAuth 2.0 tokens. |
| 🚀 Asynchronous & non-blocking support | CompletableFuture-based async APIs for non-blocking integrations. |
| ⚙️ Flexible configuration | Configure via env vars, system properties or builders; specify executor, timeouts and proxies. |
| 🏎️ Performance-oriented design | Low-overhead, connection management and retry policies for high concurrency. |
| 🛠️ Response helpers & centralized error handling | Utilities to map responses, inspect status codes and centralize error handling. |
| 📚 Hands-on examples | Ready-to-run examples and quickstarts: https://github.com/eealba/payper-examples |
More about key features
Payper leverages the platform HTTP client to benefit from HTTP/2, modern TLS improvements, connection pooling and non-blocking I/O. This yields better performance, reduced resource usage and simpler integration with Java networking features.
You can tune networking and concurrency for your environment by providing a custom executor for request dispatching, configuring connection/read timeouts, and routing requests through a proxy. These options make it easy to adapt Payper to different deployment constraints.
The SDK provides helpers to convert raw HTTP responses into domain entities, inspect status codes and extract error details so you can centralize how your application handles API errors.
| API Name | Version | Links |
|---|---|---|
| Catalog Products | v1 | API Reference |
| Subscriptions | v1 | API Reference |
| Orders | v2 | API Reference |
| Payments | v2 | API Reference |
| Invoices | v2 | API Reference |
| Webhooks Management | v1 | API Reference |
To use Payper in your project, add the appropriate dependency for the paypal service you want to consume
in your pom.xml, in the table below the corresponding payper module appears for each service:
<dependency>
<groupId>io.github.eealba.payper</groupId>
<artifactId>payper-subscriptions-v1</artifactId>
<version>1.0.0</version>
</dependency><dependency>
<groupId>io.github.eealba.payper</groupId>
<artifactId>payper-catalog-products-v1</artifactId>
<version>1.0.0</version>
</dependency><dependency>
<groupId>io.github.eealba.payper</groupId>
<artifactId>payper-orders-v2</artifactId>
<version>1.0.0</version>
</dependency><dependency>
<groupId>io.github.eealba.payper</groupId>
<artifactId>payper-payments-v2</artifactId>
<version>1.0.0</version>
</dependency><dependency>
<groupId>io.github.eealba.payper</groupId>
<artifactId>payper-invoices-v2</artifactId>
<version>1.0.0</version>
</dependency><dependency>
<groupId>io.github.eealba.payper</groupId>
<artifactId>payper-webhooks-v1</artifactId>
<version>1.0.0</version>
</dependency>| API Name | Version | Links |
|---|---|---|
| Add Tracking | v1 | API Reference |
| Disputes | v1 | API Reference |
| Partner Referrals | v2 | API Reference |
| Payment Experience | v1 | API Reference |
| Payment Method Tokens | v3 | API Reference |
| Payouts | v1 | API Reference |
| Transaction Search | v1 | API Reference |
This project maintains a separate repository with runnable examples showing how to use the Payper SDK: https://github.com/eealba/payper-examples
| Example | Summary | Link |
|---|---|---|
| payper-5-minutes | 5-minute quickstart to validate sandbox credentials and perform a simple Catalog Products API call. | Open |
| payper-orders-basic | Demonstrates Orders API v2: create, retrieve, confirm payment source, capture. | Open |
| subscriptions-app | Demonstrates product and subscription plan creation (sync App.java and async AppAsync.java). | Open |
| webstore | Spring Boot demo webstore showcasing catalog, checkout and backoffice integrations (OpenAPI included). | Open |
import io.github.eealba.payper.catalog.products.v1.api.CatalogProductsApiClient;
import io.github.eealba.payper.catalog.products.v1.model.ProductCategory;
import io.github.eealba.payper.catalog.products.v1.model.ProductRequestPOST;
public class PayperExample {
public static void main(String[] args) {
var catalogProductsApiClient = CatalogProductsApiClient.create();
var productRequest = ProductRequestPOST.builder()
.name("Product Name")
.description("Product Description")
.type(ProductRequestPOST.Type.PHYSICAL)
.category(ProductCategory.ACCESSORIES)
.imageUrl("https://example.com/image.jpg")
.build();
var product = catalogProductsApiClient.products()
.create()
.withBody(productRequest)
.retrieve()
.toEntity();
System.out.println("Created product ID: " + product.id());
}
}import io.github.eealba.payper.catalog.products.v1.api.CatalogProductsApiClient;
public class PayperExample {
public static void main(String[] args) {
var catalogProductsApiClient = CatalogProductsApiClient.create();
var product = catalogProductsApiClient.products()
.get()
.withId("1")
.retrieve()
.toEntity();
System.out.println("Retrieved product ID: " + product.id());
}
}import io.github.eealba.payper.subscriptions.v1.api.SubscriptionsApiClient;
public class PayperExample {
public static void main(String[] args) {
var subscriptionsApiClient = SubscriptionsApiClient.create();
var plan = subscriptionsApiClient.billingPlans()
.get()
.withId("1")
.retrieve()
.toEntity();
System.out.println("Retrieved plan ID: " + plan.id());
}
}import io.github.eealba.payper.subscriptions.v1.api.SubscriptionsApiClient;
public class PayperExample {
public static void main(String[] args) {
var subscriptionsApiClient = SubscriptionsApiClient.create();
var futurePlan = subscriptionsApiClient.billingPlans()
.get()
.withId("1")
.retrieve()
.toFuture();
futurePlan.thenAccept( response -> {
if (response.statusCode() == 200) {
System.out.println("Plan retrieved successfully");
} else {
System.out.println("Failed to retrieve plan");
}
});
}
}import io.github.eealba.payper.invoices.v2.api.InvoicesApi;
import io.github.eealba.payper.invoices.v2.api.InvoicingApiClient;
import io.github.eealba.payper.invoices.v2.model.Invoice;
public class PayperExample {
public static void main(String[] args) {
InvoicesApi invoicesApi = InvoicingApiClient.create().invoices();
// Create an invoice
var invoice = invoicesApi.create()
.withBody(Invoice.builder().build())
.retrieve()
.toEntity();
// List invoices
var listInvoices = invoicesApi.list()
.withPage(1)
.withPageSize(10)
.withTotalRequired(true)
.retrieve()
.toEntity();
// Get an invoice
invoice = invoicesApi.get().withId("invoice-id").retrieve().toEntity();
// Update an invoice
var updateInvoice = invoicesApi.update().withId("invoice-id")
.withBody(Invoice.builder().build())
.retrieve()
.toEntity();
// Delete an invoice
invoicesApi.delete().withId("invoice-id").retrieve().toVoid();
}
}import io.github.eealba.payper.webhooks.v1.model.EventType;
import io.github.eealba.payper.webhooks.v1.model.Patch;
import io.github.eealba.payper.webhooks.v1.model.PatchRequest;
import io.github.eealba.payper.webhooks.v1.model.Webhook;
import java.util.List;
import static io.github.eealba.payper.webhooks.v1.model.Patch.Op.REPLACE;
public class PayperExample {
public static void main(String[] args) {
var webhooksApi = WebhooksApiClient.create().webhooks();
// Create a webhook
EventType eventType = EventType.builder().name("PAYMENT.AUTHORIZATION.CREATED").build();
Webhook webhookRequest = Webhook.builder().url("https://example.com/webhook")
.eventTypes(List.of(eventType)).build();
var webhook = webhooksApi.create().withBody(webhookRequest).retrieve().toEntity();
System.out.println("Created webhook ID: " + webhook.id());
// List webhooks
var webhooksList = webhooksApi.list().retrieve().toEntity();
System.out.println("First webhook ID: " + webhooksList.webhooks().get(0).id());
// Get a webhook
var webhookDetails = webhooksApi.get().withId("WH-1234567890").retrieve().toEntity();
System.out.println("Webhook URL: " + webhookDetails.url());
// Update a webhook
Patch patch = Patch.builder().op(REPLACE).path("/url").value("https://example" + ".com/new-webhook").build();
PatchRequest patchRequest = new PatchRequest(List.of(patch));
var updatedWebhook = webhooksApi.update().withId("WH-1234567890").withBody(patchRequest).retrieve().toEntity();
System.out.println("Updated webhook URL: " + updatedWebhook.url());
// Delete a webhook
webhooksApi.delete().withId("WH-1234567890").retrieve().toVoid();
// List event types for a webhook
var eventTypes = webhooksApi.listEventTypes().withId("WH-1234567890").retrieve().toEntity();
System.out.println("First event type: " + eventTypes.eventTypes().get(0).name());
}
}PayPal REST APIs use OAuth 2.0 access tokens to authenticate requests. Your access token authorizes you to use the PayPal REST API server.
Payper is able to obtain the OAuth 2.0 access tokens before calling the Paypal Rest API, it is only necessary to pass the corresponding CLIENT_ID and CLIENT_SECRET.
Refer to the PayPal Developer Documentation for more information.
The easy way to pass the CLIENT_ID and CLIENT_SECRET is to set the following environment variables or system properties:
export PAYPAL-CLIENT-ID=YOUR_CLIENT_ID
export PAYPAL-CLIENT-SECRET=YOUR_CLIENT_SECRET
export PAYPAL-BASE-URL=https://api-m.sandbox.paypal.com export PAYPAL-CLIENT-ID=YOUR_CLIENT_ID
export PAYPAL-CLIENT-SECRET=YOUR_CLIENT_SECRET
export PAYPAL-BASE-URL=https://api-m.paypal.com Otherwise, you can pass the credentials directly to the Payper client using supplier functions:
public static void main(String[] args) {
PayperConfig config = PayperConfig.builder()
.authenticator(PayperAuthenticator.PayperAuthenticators
.ofSandBox(() -> "CLIENT_ID".toCharArray(),
() -> "CLIENT_SECRET".toCharArray()))
.build();
var subscriptionsApiClient = SubscriptionsApiClient.create(config);
var plan = subscriptionsApiClient.billingPlans()
.get()
.withId("1")
.retrieve()
.toEntity();
System.out.println("Retrieved plan ID: " + plan.id());
}Note: code snippets and examples target Java 17+ (LTS: 17, 21, 25). If you're running an older JVM, some examples may use APIs introduced in Java 9+.