pl.itemshop:sdk is the official Java SDK (17+, java.net.http; the only dependency is Gson, which Paper and Spigot already ship). The pl.itemshop:sdk-kotlin module adds suspend functions and Flow.
Every method has a blocking and a non-blocking variant (
…Async → CompletableFuture).
Installation
build.gradle.kts
Paper plugin — asynchronous
HTTP requests run onHttpClient threads; return to the server’s main thread with the Bukkit scheduler. Never call blocking methods (list(), create()…) on the main thread — use …Async.
checkout().create… fills in consents.legalVersion from the shop profile and always sends an Idempotency-Key, so a retry after a dropped connection never creates a second order. A cart takes 1–20 lines (repeat .item(...)); two or more form one payment group. After the gateway, read the order with Storefront.returnedOrderFromReturnUrl(url) (?itemshop_order=<number>&itemshop_key=<key> → ReturnedOrder(number, key)), wait for payment with orders().awaitPaymentByNumberAsync(number, key, Duration.ofSeconds(3)), and collect digital content with orders().deliveryAsync(number, key). In Kotlin: statusByNumber, delivery and awaitPaymentByNumber are suspend. The id-based orderIdFromReturnUrl / awaitPaymentAsync serve pre-3.0 orders only.
Shadow and relocation
HttpClient threads are daemons — they don’t block server shutdown. A custom Executor (.executor(...)) also receives async callbacks and retry delays.
Kotlin (coroutines)
Bukkit.getScheduler().runTask(plugin) { … }.
API client (with a key)
shops, servers, products, paymentMethods, paymentErrors, blacklist, discounts, vouchers, stats, uploads, orders. Hand-written: site() (files, draft, revisions, upload, builds, deployments, diff), templates(), workspace(), agents(), skills(). Workspace tokens need a panel session — with an API key you get 401/403. For an endpoint without a typed method: client.request("GET", "/api/v2/…", null). Models (pl.itemshop.sdk.model.*) are records with builders; null is not sent.
Errors
Every failure is an uncheckedItemShopException with code() (the backend’s code, e.g. LEGAL_CONSENT_REQUIRED, or an SDK code: BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, VALIDATION_FAILED, RATE_LIMITED, SERVER_ERROR, NETWORK_ERROR, TIMEOUT, ABORTED…), status(), details(), retryAfter() and body(). Subclasses to catch: BadRequestException (400/413/422), AuthenticationException (401), PermissionDeniedException (403), NotFoundException (404), ConflictException (409), RateLimitException (429), ServerException (5xx), ApiConnectionException (network, timeout, cancellation). In a CompletableFuture the error arrives as the cause of a CompletionException.
Retries, timeouts, idempotency
2 retries, backoff 300 ms·2ⁿ (max 5 s) with jitter,Retry-After replaces the delay. GET/HEAD and requests carrying an Idempotency-Key (orders().create, checkout().create) are retried on network errors, timeouts and 408/425/429/500/502/503/504; others only on 429. timeout(...) limits a single attempt (30 s default). Interrupting the thread waiting for the result cancels the request (ABORTED).
Webhooks
The backend sends no signed webhooks to shop owners (notifications go to Discord — see Webhooks), so the SDK has no signature verifier. For in-game delivery use the plugin protocol.Storefront API
The endpoints under the SDK.
Plugin protocol
Real-time order delivery.