> 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/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/gaires/sdk-adapteriu-perrasymas.md).

# SDK adapterių perrašymas

Pakeiskite SDK infrastruktūros adapterius vietiniais HTTP, žurnalinimo, serializavimo, šifravimo ir webhook įgyvendinimais.

Naudokite šį vadovą, jei norite pakeisti vieną iš SDK įdiegtų adapterių — nukreipti HTTP per savo klientą, siųsti SDK diagnostiką į savo registravimo sistemą, pakeisti JSON serializatorių arba testuose naudoti prievado stub'ą.

Autorizacijos [SDK architektūra](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/gaires/architektura-ir-pletinumas.md) puslapis paaiškina „ports-and-adapters“ dizainą ir išvardija penkis prievadus bei jų numatytuosius adapterius. Šis puslapis yra praktinis tęsinys: kokį svertą traukti ir tiksli sintaksė kiekvienai kalbai.

## Kaip veikia perrašymas

Kiekviena kalba siūlo tuos pačius tris svertus ta pačia prioritetų tvarka:

1. **Paduokite fabrikui (arba `EgwClient` konstruktoriui) paruoštą egzempliorių.** Aiškiai pateiktas adapteris visada turi pirmenybę — fabriko atsarginis kelias į aptikimą pereina tik tiems prievadams, kurių nepalikote nenustatytų. Tai tiesiausias ir deterministiškiausias būdas, tinkamas vienkartiniams pakeitimams.
2. **Užregistruokite teikėją per SDK paslaugų teikėjų sąsajos (SPI) mechanizmą.** A *teikėjas* yra mažas fabriko objektas, kuris moka sukurti jūsų adapterį; SDK įkrovos mechanizmas jį aptinka ir naudoja visur, kur sukuriamas klientas. SPI terminas ateina iš Java pasaulio, tačiau SDK šią sąvoką taiko visose keturiose kalbose — kiekvienas SDK savo teikėjų sutartis laiko atskiroje `spi` vardų srityje, aptinkamoje pagal platformai būdingą idiomą. Jei žinote klasikinį .NET teikėjų modelį, Laravel paslaugų teikėjus arba Angular/NestJS priklausomybių injekcijos teikėjus, tai ta pati idėja. Tai tinkamas kelias, kai pakeitimas yra viso stako politika („visi šio serviso EGW klientai logina per Serilog“), o ne sprendimas kiekvienam egzemplioriui atskirai.
3. **Išplėskite kliento fabriką.** Fabrikai laikosi šabloninio metodo (template method) modelio: vienas perrašomas `newXxx()` sukūrimo metodas kiekvienam prievadui (Java/PHP). Paveldėjimas suteikia jums visišką kūrimo kontrolę neliečiant SPI.

Autorizacijos *mechanizmas* už 2 sverto skiriasi tarp kalbų, kiekviena naudoja savo platformai būdingą idiomą. Eikite tiesiai prie savosios:

* [**Java**](#java) — `ServiceLoader` aptikimas, registruojamas per `META-INF/services` failą
* [**PHP**](#php) — sąsajos į klasę žemėlapis `providers.php` kurį perduodate fabrikui
* [**C#**](#c) — „assembly“ nuskaitymas; deklaruojant klasę *yra* registracija
* [**Node.js / TypeScript**](#nodejs) — aiškus `registerProvider()` kvietimas

Viena asimetrija, kurią verta žinoti Java ir PHP: fabrikas turi aiškius API kliento, API serverio ir registravimo priemonės (logger) nustatytuvus, tačiau serializatorius ir duomenų šifruotuvas visada kuriami per SPI — norėdami pakeisti šiuos du, naudokite 2 arba 3 svirtį.

## Java

* **Aptikimas** — `java.util.ServiceLoader`
* **Registracija** — `META-INF/services/<provider FQN>` failą arba JPMS `provides` sąlyga
* **Jei sutampa keli teikėjai** — pirmasis classpath'e laimi

Įkrovos mechanizmas yra `DefaultEgwClientFactory`, kuris kiekvieną prievadą išsprendžia per `java.util.ServiceLoader` prieš teikėjų sąsajas `...sdk.application.bootstrap.spi`: `ApiClientProvider`, `ApiServerProvider`, `DataEncrypterProvider`, `SerializerProvider`, ir `EgwLoggerProvider`.

**1 svirtis — fabriko nustatymai.** `connectingWith(ApiClient)`, `listeningWith(ApiServer)`, ir `loggingWith(EgwLogger)` priima paruoštus egzempliorius:

```java
var client = new DefaultEgwClientFactory()
        .connectingTo(baseApiUri)
        .authorizingAs(System.getenv("EGW_CLIENT_ID"), System.getenv("EGW_CLIENT_SECRET"))
        .loggingWith(new Slf4jEgwLogger(LoggerFactory.getLogger("egw-sdk")))
        .createEgwClient();
```

**2 svirtis — ServiceLoader teikėjas.** Įgyvendinkite teikėjo sąsają su viešu konstruktoriu be argumentų, tada užregistruokite ją `META-INF/services`. Loggeris yra švariausias SPI taikinys — SDK nepateikia jokios `EgwLoggerProvider` savos registracijos, todėl jūsų teikėjas parenkamas be konkurencijos:

```java
public final class Slf4jEgwLoggerProvider implements EgwLoggerProvider {
    @Override
    public EgwLogger create() {
        return new Slf4jEgwLogger(LoggerFactory.getLogger("egw-sdk"));
    }
}
```

```
# src/main/resources/META-INF/services/com.tieto.banking.egw.merchantapi.sdk.application.bootstrap.spi.EgwLoggerProvider
com.example.shop.egw.Slf4jEgwLoggerProvider
```

Kitų keturių prievadų atveju SDK pagrindiniame JAR faile registruoja savo teikėjus, todėl jūsų teikėjas kovoja dėl pirmos vietos classpath'e, o ne ką nors pakeičia — ten SPI registracija negarantuoja perrašymo. Tiems naudokite 1 arba 3 svirtį.

**3 svirtis — fabriko antrinė klasė.** `DefaultEgwClientFactory` yra atviras plėtimui; perrašykite `newXxx()` metodą tam prievadui, kurį norite pakeisti. Tai patikimas būdas pakeisti serializatorių arba šifruotuvą:

```java
public class ShopEgwClientFactory extends DefaultEgwClientFactory {
    @Override
    protected Serializer newSerializer() {
        return new ShopJsonbSerializer();
    }
}
```

## PHP

* **Aptikimas** — providers konfigūracijos failas
* **Registracija** — sąsajos → klasės žemėlapis `providers.php` kurį perduodate fabrikui
* **Jei sutampa keli teikėjai** — negali įvykti; žemėlapyje yra po vieną įrašą kiekvienam prievadui

Įkrovos mechanizmas iš paprasto PHP konfigūracijos failo nuskaito sąsajos į klasę žemėlapį. Teikėjų sąsajos yra `...\Application\Bootstrap\Spi`: `ApiClientProviderInterface`, `ApiServerProviderInterface`, `DataEncrypterProviderInterface`, `SerializerProviderInterface`, `EgwLoggerProviderInterface`.

**1 svirtis — fabriko nustatymai.** Tokia pati forma kaip Java („`connectingWith()`, `listeningWith()`, `loggingWith()`):

```php
$client = (new DefaultEgwClientFactory())
    ->connectingTo($baseApiUri)
    ->authorizingAs(getenv('EGW_CLIENT_ID'), getenv('EGW_CLIENT_SECRET'))
    ->loggingWith(new Psr3EgwLogger($psrLogger))
    ->createEgwClient();
```

**2 svirtis — jūsų pačių teikėjų failas.** Nukopijuokite SDK `config/providers.php`, pakeiskite įrašus, kuriuos norite keisti, ir perduokite failo kelią fabriko konstruktoriui. Neredaguokite failo `vendor/` — numatytasis kelias ten nusisuka, ir Composeris jį perrašys:

```php
<?php // config/egw-providers.php
// use sakiniai SPI sąsajoms, įtrauktiems teikėjams ir jūsų teikėjams praleisti

return [
    ApiClientProviderInterface::class => GuzzleHttpClientProvider::class,
    ApiServerProviderInterface::class => ReactPhpHttpServerProvider::class,
    DataEncrypterProviderInterface::class => WebTokenDataEncrypterProvider::class,
    SerializerProviderInterface::class => ShopJsonSerializerProvider::class,   // jūsų perrašymas
    EgwLoggerProviderInterface::class => Psr3EgwLoggerProvider::class,         // jūsų perrašymas
];
```

```php
$factory = new DefaultEgwClientFactory(__DIR__ . '/config/egw-providers.php');
```

Teikėjas yra klasė su viešu konstruktoriu be argumentų, įgyvendinanti sąsają:

```php
final class ShopJsonSerializerProvider implements SerializerProviderInterface
{
    public function create(EgwLoggerInterface $logger): SerializerInterface
    {
        return new ShopJsonSerializer();
    }
}
```

**3 svirtis — fabriko antrinė klasė.** Išplėskite `DefaultEgwClientFactory` (arba `AbstractEgwClientFactory`) ir perrašykite atitinkamą `newXxx()` metodą, lygiai taip pat kaip Java.

## C\#

* **Aptikimas** — „assembly“ nuskaitymas (refleksija virš įkeltų assembly)
* **Registracija** — jokio; tiesiog deklaruokite viešą klasę, įgyvendinančią teikėjo sąsają
* **Jei sutampa keli teikėjai** — laimi pirmasis pagal assembly įkėlimo tvarką

Įkrovos mechanizmas nuskaito dabartinio `AppDomain` įkeltus assembly'us ieškodamas konkrečių klasių, įgyvendinančių teikėjų sąsajas `Egw.Sdk.Application.Bootstrap.Spi`: `IApiClientProvider`, `IApiServerProvider`, `IDataEncrypterProvider`, `ISerializerProvider`, `IEgwLoggerProvider`. Nieko nereikia registruoti — klasės deklaravimas ir yra registracija. Teikėjai sukuriami refleksijos būdu, todėl jiems reikia viešo konstruktoriaus be parametrų.

Loggeris vėl yra švariausias SPI taikinys, nes SDK nepateikia jokio numatytojo `IEgwLoggerProvider`:

```csharp
public sealed class SerilogEgwLoggerProvider : IEgwLoggerProvider
{
    public IEgwLogger Create() => new SerilogEgwLogger(Log.Logger);
}
```

Du praktiniai pastebėjimai. Nuskaitymas mato tik assembly'us, kurie jau įkelti į AppDomain — jei jūsų teikėjas yra assembly'yje, kurio dar niekas nelietė, prieš kuriant klientą priverstinai jį įkelkite (nuoroda į jo tipą). Ir prievadams, kuriems SDK pateikia numatytąjį teikėją, jūsų teikėjas kovoja dėl pirmos vietos pagal assembly įkėlimo tvarką, o ne ką nors pakeičia — tai negarantuotas perrašymas.

Garantuotas perrašymas C# kalboje yra tiesioginis kūrimas — `EgwClient`konstruktorius priima visus penkis prievadus:

```csharp
var apiClient = new HttpApiClient(myHttpClient, authentication, logger); // jūsų pačių HttpClient egzempliorius
using var client = new EgwClient(
    baseUri, apiClient, apiServer: null,
    new JoseDataEncrypter(apiClient, jwkEndpoint, logger),
    new JsonSerializer(logger),
    logger);
```

Atkreipkite dėmesį į pirmą eilutę: `HttpApiClient` priima įterptą `HttpClient`, todėl galite taikyti savo antraštes, laiko limitus ir proxy nustatymus visai neįgyvendindami prievado.

## Node.js

Taikoma ir JavaScript, ir TypeScript.

* **Aptikimas** — jokio; aiškus registras
* **Registracija** — kvietimas `registerProvider({...})` prieš kuriant klientą
* **Jei sutampa keli teikėjai** — laimi paskutinė registracija

JS SDK nėra implicitinio aptikimo — registracija yra aiškus kvietimas, atitinkantis platformos modulių idiomą. Registre laikomas po vieną teikėją kiekvienam prievadui, ir `registerProvider()` priima dalinį objektą ir jį sulieja, todėl perrašote tik tai, ką nurodote:

```ts
// Alpha pastaba: deep import'as tol, kol registerProvider bus eksportuotas iš paketo šaknies — žr. žemiau
import { registerProvider } from "@tieto/egw-sdk/dist/application/bootstrap/spi/ProviderRegistry.js";

registerProvider({
  loggerProvider: { create: () => new PinoEgwLogger(pino()) }
});

const client = new DefaultEgwClientFactory().create({
  baseUri: new URL("https://sandbox-api.example.com/merchant"),
  authentication: {
    tokenEndpoint: new URL(process.env.EGW_TOKEN_ENDPOINT!),
    clientId: process.env.EGW_CLIENT_ID!,
    clientSecret: process.env.EGW_CLIENT_SECRET!
  },
  jwkUri: new URL(process.env.EGW_JWK_URI!)
});
```

Teikėjai yra paprasti objektų literalai su `create()` metodu — uždarosios funkcijos, ne klasės — todėl perduoti konfigūraciją į savo adapterį galima be jokios ceremonijos. Iškvieskite `registerProvider()` prieš kurdami fabriką; SDK registruoja savo numatytuosius nustatymus, kai fabriko modulis įkeliamas, o jūsų vėlesnė registracija juos pakeičia pagal raktą.

Šioje dabartinėje alpha versijoje, `registerProvider` dar nėra pereksportuotas iš paketo šaknies, todėl reikia deep import'o iš SDK `ProviderRegistry` modulio — žr. [Žinomi apribojimai Node.js greitajame pradžios vadove](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/greitos-pradzios/node.js-typescript.md).

Tiesioginis kūrimas taip pat veikia — `EgwClient`konstruktorius priima visus penkis prievadus, ir `AxiosApiClient` priima įterptą `AxiosInstance` (trečias konstruktoriaus parametras) jūsų interceptoriams, laiko limitams ir proxy konfigūracijai.

## Susiję puslapiai

* [SDK architektūra](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/gaires/architektura-ir-pletinumas.md) — ports-and-adapters dizainą, penkis prievadus ir jų numatytuosius adapterius
* [Apžvalga](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/apzvalga.md) — kalbų matrica, paketai, alfa būsena
* [Klaidų tvarkymas](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/gaires/klaidu-valdymas.md) — `ECommException` hierarchija, kurioje dalyvauja jūsų adapteriai
* [Saugumo mechanizmuose](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/gaires/saugumo-mechanizmai.md) — ką adapteris turi užtikrinti (žetonų valdymą, JWE, žurnalų maskavimą)
* Greitos pradžios kiekvienai kalbai: [Java](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/greitos-pradzios/java.md) · [PHP](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/greitos-pradzios/php.md) · [C#](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/greitos-pradzios/c-.net.md) · [Node.js](/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/greitos-pradzios/node.js-typescript.md)


---

# 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/seb-docs-draft/seb-docs-public-lt/integracija/api-kliento-sdk/gaires/sdk-adapteriu-perrasymas.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.
