Java SDK
Artefakt: dev.msv3:msv3-sdk
Das Java SDK bietet typsicheren Zugriff auf das MSV3 Smart Gateway ab Java 17+. Keine externen Abhaengigkeiten -- nur die Java-Standardbibliothek.
Installation
Maven
<dependency>
<groupId>dev.msv3</groupId>
<artifactId>msv3-sdk</artifactId>
<version>1.0.0</version>
</dependency> Gradle
implementation 'dev.msv3:msv3-sdk:1.0.0' Erfordert Java 17+. Das SDK hat keine Laufzeit-Abhaengigkeiten ueber die Standardbibliothek hinaus (nutzt java.net.http.HttpClient).
Grundeinrichtung
import dev.msv3.sdk.Msv3Client;
Msv3Client client = Msv3Client.builder()
.apiKey(System.getenv("MSV3_API_KEY")) // "pk_live_..." oder "pk_test_..."
.build(); | Builder-Methode | Typ | Standard | Beschreibung |
|---|---|---|---|
apiKey(String) | String | erforderlich | Ihr Gateway-API-Schluessel |
baseUrl(String) | String | https://api.msv3gateway.example.com | Ueberschreibung fuer Tests |
timeout(int) | int | 30 | Anfrage-Timeout in Sekunden |
Zugangsdaten
MSV3-Zugangsdaten werden pro Aufruf uebergeben, nie im Client gespeichert:
import dev.msv3.sdk.Msv3Credentials;
Msv3Credentials creds = new Msv3Credentials(
"noweda",
"Now00079800",
System.getenv("NOWEDA_PASS")
); Msv3Credentials ist ein Java-Record mit den Feldern wholesaler, username und password.
Ressourcen
| Ressource | Zugriff | Beschreibung |
|---|---|---|
ConnectionResource | client.connection() | Grosshaendler-Konnektivitaet testen |
WholesalersResource | client.wholesalers() | Grosshaendler auflisten und registrieren |
AvailabilityResource | client.availability() | Echtzeit-Bestandsabfragen |
OrdersResource | client.orders() | Bestellungen aufgeben und verfolgen |
ContractsResource | client.contracts() | Vertragsdaten und Lieferfenster |
DeliveriesResource | client.deliveries() | Lieferbenachrichtigungen |
ReturnsResource | client.returns() | Retouren-Autorisierung und Ankuendigungen |
DocumentsResource | client.documents() | PDF-Dokumente herunterladen |
WebhooksResource | client.webhooks() | Webhook-Abonnement-Verwaltung |
Verbindung testen
import dev.msv3.sdk.model.ConnectionTestResponse;
ConnectionTestResponse result = client.connection().test(creds);
System.out.println("Verbunden: " + result.connected()); // true
System.out.println("Grosshaendler: " + result.wholesaler()); // "noweda"
System.out.println("Latenz: " + result.latencyMs() + "ms"); // 142 Verfuegbarkeit
client.availability().check(creds, request)
import dev.msv3.sdk.enums.DemandType;
import dev.msv3.sdk.model.AvailabilityRequest;
import dev.msv3.sdk.model.AvailabilityResponse;
import dev.msv3.sdk.model.AvailabilityItemResponse;
AvailabilityResponse result = client.availability().check(creds,
AvailabilityRequest.builder()
.addItem("761271", 5)
.addItem("10203595", 1, DemandType.DIRECT)
.build()
);
for (AvailabilityItemResponse item : result.items()) {
if (item.available()) {
var d = item.deliveries().get(0);
System.out.printf("PZN %s: %d Stueck, ETA %s%n",
item.pzn(), d.quantity(), d.estimatedAt());
}
if (item.substitution() != null) {
System.out.printf(" Ersetzt durch PZN %s%n",
item.substitution().replacementPzn());
}
} client.availability().bulk(creds, pzns)
import dev.msv3.sdk.model.BulkAvailabilityResponse;
BulkAvailabilityResponse bulk = client.availability().bulk(creds,
List.of("761271", "4211896", "10203595", "99999999")
);
System.out.println("Auf Lager: " + bulk.availablePzns()); Bestellungen
client.orders().create(creds, request)
import dev.msv3.sdk.enums.DeliveryPreference;
import dev.msv3.sdk.model.OrderRequest;
import dev.msv3.sdk.model.OrderResponse;
OrderResponse order = client.orders().create(creds, OrderRequest.builder()
.addItem("761271", 3, DeliveryPreference.BACKORDER)
.addItem("10203595", 1)
.build()
);
System.out.println("Status: " + order.status()); // "confirmed"
System.out.println("Request ID: " + order.requestId());
for (var item : order.items()) {
System.out.printf("PZN %s: %d/%d bestaetigt%n",
item.pzn(), item.quantityConfirmed(), item.quantityOrdered());
} Liefervorgaben: NORMAL, BACKORDER, GROUPED, DISPOSITION
Dry Run
OrderResponse order = client.orders().create(creds,
OrderRequest.builder()
.addItem("761271", 3)
.build(),
true // dryRun
);
// order.status() == "dry_run" Nachtmodus
if ("queued_night_mode".equals(order.status())) {
System.out.println("Grosshaendler im Nachtmodus. Bestellung vorgemerkt.");
} client.orders().status(creds, orderId)
import dev.msv3.sdk.model.OrderStatusResponse;
OrderStatusResponse status = client.orders().status(creds, order.requestId());
switch (status.status()) {
case "available" ->
System.out.printf("Bestellung %s: %d Artikel%n",
status.orderId(), status.items().size());
case "expired" ->
System.out.println("Bestellantwort nicht mehr verfuegbar");
case "unknown" ->
System.out.println("Grosshaendler kennt diese Bestell-ID nicht");
} Vertraege
import dev.msv3.sdk.model.ContractsResponse;
ContractsResponse contract = client.contracts().get(creds);
System.out.println("Kunden-ID: " + contract.customerId());
System.out.println("Bulk-Verfuegbarkeit: " + contract.capabilities().bulkAvailability());
System.out.println("Substitution: " + contract.capabilities().substitution());
for (var window : contract.deliveryWindows()) {
System.out.printf(" %s schliesst um %s%n", window.day(), window.closesAt());
} Lieferungen
import dev.msv3.sdk.model.DeliveryListResponse;
// Unbestaetigte Lieferavise abrufen
DeliveryListResponse response = client.deliveries().list(creds);
for (var delivery : response.deliveries()) {
System.out.printf("Lieferung %s (%s)%n",
delivery.trackingNumber(), delivery.date());
for (var item : delivery.items()) {
System.out.printf(" PZN %s: %d geliefert%n",
item.pzn(), item.quantityDelivered());
}
}
// Bestaetigen
List<String> trackingNumbers = response.deliveries().stream()
.map(d -> d.trackingNumber())
.toList();
client.deliveries().confirm(creds, trackingNumbers); Retouren
import dev.msv3.sdk.enums.ReturnReason;
import dev.msv3.sdk.model.ReturnRequest;
import dev.msv3.sdk.model.ReturnResponse;
ReturnResponse ret = client.returns().request(creds, ReturnRequest.builder()
.addItem("TRACK-001", "761271", 2, ReturnReason.DAMAGED)
.build()
);
System.out.println("Return ID: " + ret.returnId());
for (var pos : ret.positions()) {
System.out.printf(" PZN %s: %s%n", pos.pzn(), pos.status());
} Webhooks
import dev.msv3.sdk.model.WebhookSubscription;
// Webhook erstellen
WebhookSubscription webhook = client.webhooks().create(
"https://example.com/hooks/msv3",
"noweda",
List.of("delivery.received", "order.status_changed")
);
// WICHTIG: signingSecret jetzt speichern - wird nicht erneut angezeigt
System.out.println("Webhook ID: " + webhook.id());
System.out.println("Secret: " + webhook.signingSecret()); Signatur-Verifizierung
import dev.msv3.sdk.Msv3WebhookVerifier;
// Servlet / Spring-Controller
String payload = request.getReader().lines().collect(Collectors.joining());
String signature = request.getHeader("X-MSV3-Signature");
String secret = System.getenv("MSV3_WEBHOOK_SECRET");
if (!Msv3WebhookVerifier.verify(payload, signature, secret)) {
response.sendError(401, "Invalid signature");
return;
}
// Payload ist verifiziert - jetzt parsen und verarbeiten Die Methode Msv3WebhookVerifier.verify() akzeptiert sowohl String als auch byte[] als Payload.
Fehlerbehandlung
import dev.msv3.sdk.exception.*;
try {
OrderResponse order = client.orders().create(creds,
OrderRequest.builder().addItem("761271", 3).build());
} catch (Msv3AuthException e) {
System.err.println("Authentifizierung fehlgeschlagen: " + e.getMessage());
} catch (Msv3BadRequestException e) {
System.err.println("Ungueltige Anfrage: " + e.getMessage());
} catch (Msv3ProductUnavailableException e) {
System.err.printf("Abgelehnt (Code %s): %s%n", e.errorCode(), e.getMessage());
} catch (Msv3RateLimitException e) {
int wait = e.retryAfter() != null ? e.retryAfter() : 60;
System.err.printf("Rate-Limit. Warte %ds...%n", wait);
} catch (Msv3WholesalerUnavailableException e) {
System.err.printf("Grosshaendler %s nicht erreichbar%n", e.wholesaler());
} catch (Msv3ApiException e) {
System.err.printf("API-Fehler %d [%s]: %s%n",
e.status(), e.errorType(), e.getMessage());
} | Exception | HTTP-Status | Beschreibung |
|---|---|---|
Msv3AuthException | 401 | Ungueltiger API-Key oder MSV3-Zugangsdaten |
Msv3BadRequestException | 400 | Ungueltige Anfrageparameter |
Msv3NotFoundException | 404 | Grosshaendler oder Ressource nicht gefunden |
Msv3ProductUnavailableException | 422 | Bestellung vom Grosshaendler abgelehnt |
Msv3RateLimitException | 429 | Rate-Limit ueberschritten |
Msv3WholesalerUnavailableException | 502 | Grosshaendler nicht erreichbar |
Alle Exceptions erweitern Msv3ApiException (ein RuntimeException). Gemeinsame Methoden:
| Methode | Typ | Beschreibung |
|---|---|---|
status() | int | HTTP-Statuscode |
errorType() | String | Maschinenlesbarer Fehlercode |
getMessage() | String | Menschenlesbare Beschreibung |
wholesaler() | String | Grosshaendler-ID (kann null sein) |
errorCode() | String | Roher MSV3-Fehlercode (kann null sein) |
requestId() | String | Request-ID aus dem X-Request-Id-Header |
Retry-Strategie
Das SDK wiederholt nicht automatisch. Implementieren Sie Retry-Logik in Ihrer Anwendung:
import dev.msv3.sdk.exception.Msv3WholesalerUnavailableException;
static OrderResponse createOrderWithRetry(
Msv3Client client, Msv3Credentials creds,
OrderRequest request, int maxAttempts) {
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return client.orders().create(creds, request);
} catch (Msv3WholesalerUnavailableException e) {
if (attempt < maxAttempts) {
long delay = (long) Math.pow(2, attempt - 1) * 1000;
System.err.printf("Versuch %d fehlgeschlagen, erneut in %dms...%n",
attempt, delay);
try { Thread.sleep(delay); } catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw e;
}
} else {
throw e;
}
}
}
throw new IllegalStateException("unreachable");
} Vollstaendiges Beispiel
import dev.msv3.sdk.Msv3Client;
import dev.msv3.sdk.Msv3Credentials;
import dev.msv3.sdk.enums.DeliveryPreference;
import dev.msv3.sdk.exception.Msv3ApiException;
import dev.msv3.sdk.model.*;
public class QuickStart {
public static void main(String[] args) {
Msv3Client client = Msv3Client.builder()
.apiKey(System.getenv("MSV3_API_KEY"))
.build();
Msv3Credentials creds = new Msv3Credentials(
"noweda",
System.getenv("MSV3_USERNAME"),
System.getenv("MSV3_PASSWORD")
);
try {
// 1. Verbindung testen
var connection = client.connection().test(creds);
System.out.printf("Verbunden (%dms)%n", connection.latencyMs());
// 2. Verfuegbarkeit pruefen
var availability = client.availability().check(creds,
AvailabilityRequest.builder()
.addItem("761271", 3)
.build()
);
if (!availability.items().get(0).available()) {
System.out.println("PZN 761271 nicht verfuegbar");
return;
}
// 3. Bestellung mit Nachlieferung aufgeben
var order = client.orders().create(creds, OrderRequest.builder()
.addItem("761271", 3, DeliveryPreference.BACKORDER)
.build()
);
System.out.println("Bestellstatus: " + order.status());
System.out.println("Request ID: " + order.requestId());
} catch (Msv3ApiException e) {
System.err.printf("MSV3-Fehler %d [%s]: %s%n",
e.status(), e.errorType(), e.getMessage());
}
}
}