> For the complete documentation index, see [llms.txt](https://docs.ecommerce.seb.ee/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ecommerce.seb.ee/integration/api-client-sdk/guides/security-mechanics.md).

# Security mechanics

Secure SDK integrations with OAuth, JWE encryption, webhook safeguards, and logging controls.

> Draft · SDK alpha. Applies to all four languages; differences are called out per language. Items marked **\[TBC]** are still to be supplied before publication.

This page covers the three security-sensitive mechanisms the SDK handles for you — OAuth 2.0 authentication, JWE card-data encryption, and webhook notifications — and what you must **not** improvise around them.

## Authentication

The SDK authenticates with **OAuth 2.0 client credentials**. You supply a client ID and client secret at client construction; everything else is automatic:

* The access token is fetched **lazily** on the first API call and **cached in memory**.
* The token is refreshed proactively \~30 seconds before its `expires_in` lifetime ends. No refresh tokens are used.
* You never handle the token yourself, and the SDK never writes credentials or tokens to its logger.

What you must do:

* Keep the client secret in an environment variable or secret store. **Never commit it, never log it.**
* Reuse one client instance; each new instance fetches its own token.

What the SDK does not do: it does not retry a request that fails with HTTP 401 mid-flight. Token expiry is tracked locally, so this occurs only if credentials are revoked or the clock skews; the call surfaces the error and the next call after local expiry fetches a fresh token.

## Card data encryption (JWE)

When you create a **card checkout session**, the card details (`cardNumber`, `csc`, `expiry`, `cardholderName`) are never sent in plaintext. The SDK encrypts them into a **JWE compact serialization** (RSA-OAEP-256 key encryption, A256GCM content encryption) using a **public RSA key in JWK format** fetched automatically from the EGW key endpoint. There is nothing to install or rotate on your side — but encryption is **your explicit step** in building the request:

**Java** (the builder helper serializes and encrypts in one step):

```java
var request = CreateCardCheckoutSessionRequest.builder()
        .encryptedCard(client, data -> data.card(c -> c
                .cardNumber("4111111111111111")
                .csc("123")
                .expiry(e -> e.month("12").year("30"))
                .cardholderName("JOHN DOE")))
        .build();
var session = client.cardSessions().createCardCheckoutSession(paymentId, request);
```

**C# / PHP / Node.js:** build the plaintext data object, encrypt with `client.EncryptToJweCompact(...)` / `$client->encryptToJweCompact(...)` / `await client.encryptToJweCompact(...)`, and set the result as the request's `encryptedCard` field.

Rules:

* **Never** put plaintext card fields anywhere else in a request, in your logs, or in your storage. The JWE string is the only card-data artifact that leaves your process.
* Do not cache the encryption key or the JWE output across payments; encrypt per session.
* C# only: configure the JWK endpoint (`EncryptingWith(...)`) at construction — without it, encryption calls fail immediately rather than sending plaintext. Node.js: `jwkUri` is a required config field.

### Key selection and rotation policy

This is the **normative policy** for all API Client SDKs. When encrypting, the SDK selects the key to use by:

1. taking only keys with `use=enc` and `kty=RSA`;
2. excluding revoked keys;
3. choosing the one with the newest `iat`;
4. **re-evaluating this on every encryption** — the chosen key is never cached across operations.

Re-evaluating each time is deliberate: it means a server-side key rotation takes effect immediately, with no restart and no stale-key failures.

> ⚠️ **In PHP, C# and Node.js, recreate the client periodically — a server-side key rotation will otherwise break long-lived processes.** Those three SDKs currently cache the first key they fetch for the client's lifetime instead of re-evaluating per encryption, and C# additionally takes `keys[0]` without filtering on `use` or `kty` and hardcodes RSA-OAEP-256. Java implements the policy in full; the other three will be brought in line with it before they ship.

## Webhook notifications

EGW can push payment-status notifications to an HTTPS endpoint you expose. The SDK helps you consume them:

* **Java / PHP / C#** — a built-in listener: configure `listeningAt(...)` / `ListeningOn(...)` on the factory, then register a handler:

```java
client.notifications().receiveNotifications(notification -> {
    // notification.getType(), notification.getPaymentId(), notification.getPaymentStatus()
});
```

The listener replies **HTTP 200** when your handler completes and **HTTP 500** if it throws (EGW can then redeliver). Keep handlers fast and idempotent.

* **Node.js** — no built-in listener: expose the endpoint in your own HTTP server and deserialize the body with `client.notifications().parseWebhook(payload)`.

### Authenticity of notifications

> ⚠️ **Always confirm state via the API before acting on a notification — never ship goods, settle, or update an order based on the webhook body alone.** On a payment notification, call `getPaymentInformation(paymentId)` (authenticated) and act on that response. The reason: the platform does not sign notification payloads and does not offer endpoint authentication (no mTLS, no basic auth, no shared secret), so there is nothing for the SDK to verify and anyone who can reach your endpoint can post a well-formed notification. Treat every notification as an untrusted hint, never as a fact — the API confirmation is the only control standing between a forged POST and a fulfilled order.

Run the listener behind a reverse proxy that terminates HTTPS. The built-in listeners (Java/PHP/C#) speak plain HTTP — the Java listener defaults to port 80. Do not expose the listener port directly to the internet; put a proxy in front of it to terminate TLS and forward to the listener on the internal network.

Beyond that, apply your own organization's standard controls for a publicly reachable endpoint.

Notification event-type catalog: **\[TBC — link]**

## Logging

The default logger in every language is a silent no-op — safe by default. If you plug in a real logger:

* The SDK never logs credentials, tokens, or card data.
* Route SDK ERROR logs as if they contain payment metadata, and do not forward them to systems that must stay free of it. On HTTP 4xx/5xx responses the SDK logs the raw error-response body at ERROR level, unredacted, and error bodies can contain order references, amounts, and customer-facing messages. This is a known alpha limitation, not intended behaviour — redaction is planned but not yet implemented.
* Exception messages from failed token requests include the auth server's response body. Handle and log exceptions with the same care.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.ecommerce.seb.ee/integration/api-client-sdk/guides/security-mechanics.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
