Skip to content

Java SDK — OpenTelemetry

Both dev.paylera:paylera-sdk and dev.paylera:paylera-spring-boot-starter emit OpenTelemetry spans when an OTel SDK is on the host process classpath. With no SDK present the calls are zero-cost no-ops — the API-only dep keeps the abstraction free.

Tracer names

The protocol contract pins three tracer names so spans can be filtered by source:

Tracer nameWhere it’s used
Paylera.SdkTyped client (PayleraClient) — one client-kind span per API call.
Paylera.RelaySpring starter’s relay controller — one server-kind span per relay route.
Paylera.WebhookSpring starter’s webhook receiver — one server-kind span per delivery.

Span names are Paylera.<Op> (typed client), Paylera.Relay.<Op> (relay), and Paylera.Webhook.<event> (webhook). The <Op> value matches the OpenAPI operationId, so traces line up with the spec.

Span attributes

Every span carries at least:

AttributeValue
paylera.endpointAPI path, e.g. /v1/check or /api/paylera/me.
paylera.api_mode"live" / "test" / "custom" (typed client only).

Webhook spans add paylera.problem.type when a handler throws.

Typed client

opentelemetry-api is an optional dependency on paylera-sdk. Pass a Tracer to the builder and the SDK emits one client-kind span per public operation:

import dev.paylera.sdk.PayleraClient;
import io.opentelemetry.api.GlobalOpenTelemetry;
var client = PayleraClient.builder()
.apiToken(System.getenv("PAYLERA_API_TOKEN"))
.tracer(GlobalOpenTelemetry.getTracer("Paylera.Sdk"))
.build();

Skip the tracer(...) call to opt out at zero cost — internally the SDK uses a no-op tracer when none is supplied.

The span hierarchy:

your.app.span
├── Paylera.Check (client kind)
│ ├── http.client (OkHttp instrumentation)
│ └── (server-side Paylera spans, when sampled)
└── ...

If your app has OkHttp HTTP-client instrumentation registered, you’ll see an http.client child span per Paylera call — the SDK’s client-span captures the operation context, the HTTP span captures wire details.

Spring Boot starter

The starter declares opentelemetry-api as <optional>true</optional> so merchant builds that already pin a different OTel version aren’t surprised by transitive resolution.

To wire spans:

  1. Add the OpenTelemetry SDK to your host app’s POM. For example:

    <dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
    </dependency>
    <dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-sdk</artifactId>
    </dependency>

    Or use the Spring Boot OpenTelemetry starter:

    <dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-spring-boot-starter</artifactId>
    </dependency>
  2. Configure an exporter — OTLP to your collector, Zipkin, Jaeger, etc. The Paylera starter is transport-agnostic; whatever OTel SDK is registered as the global instance picks up the spans.

That’s it. The autoconfig registers two Tracer beans (payleraRelayTracer, payleraWebhookTracer) via GlobalOpenTelemetry.getTracer(...). With no SDK installed those return no-op tracers — span emission is silently free.

To wire a custom tracer (different exporter, different sampler):

@Bean(name = "payleraRelayTracer")
Tracer payleraRelayTracer(OpenTelemetry openTelemetry) {
return openTelemetry.getTracer("MyApp.PayleraRelay");
}

@ConditionalOnMissingBean(name = "payleraRelayTracer") in the autoconfig backs off to your bean. The same pattern works for payleraWebhookTracer.

Cross-tier trace propagation

The relay forwards inbound traceparent / tracestate headers to Paylera, and Paylera echoes them on the response. End-to-end a single trace spans:

React SDK fetch span
└── Spring Boot starter Paylera.Relay.Check span
└── Paylera Java client Paylera.Check span
└── http.client span
└── (Paylera server-side spans, when sampled)

The React SDK populates traceparent from the global OTel context if @paylera/react’s provider sees an active span; without one, the client passes through whatever the upstream sent.

Sampling

The SDK does not configure a sampler — that’s the host app’s job. For a high-volume check workload, consider:

  • Ratio sampling (e.g. 10% of Paylera.Check spans).
  • Tail-based sampling at the collector — keep 100% of error traces, 10% of OK traces.
  • A custom sampler that always keeps spans with paylera.problem.type set.

Disabling

To turn off Paylera spans without uninstalling the OTel SDK, configure a sampler that drops everything from the Paylera tracers, or override the bean with a no-op tracer:

@Bean(name = "payleraRelayTracer")
Tracer payleraRelayTracer() {
return io.opentelemetry.api.trace.TracerProvider.noop().get("paylera");
}

See also