From 1d7285bfc843210c82d71af8d157ed0ca7efa5d6 Mon Sep 17 00:00:00 2001 From: Michal Landsman Date: Tue, 10 Nov 2026 16:54:54 +0100 Subject: [PATCH 1/4] docs: open each module's readme with its gradle and maven install and a framework example --- .docs/install.md | 4 +- modules/moneta-api-micronaut/README.md | 58 +++++++++++++++++++ modules/moneta-api-play/README.md | 45 ++++++++++++++ modules/moneta-api-quarkus/README.md | 58 +++++++++++++++++++ .../moneta-api-spring-boot-starter/README.md | 58 +++++++++++++++++++ modules/moneta-api/README.md | 39 +++++++++++++ 6 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 modules/moneta-api/README.md diff --git a/.docs/install.md b/.docs/install.md index 91f9a28..3972320 100644 --- a/.docs/install.md +++ b/.docs/install.md @@ -36,7 +36,7 @@ repositories { } dependencies { - implementation("cz.insuit:moneta-api-spring-boot-starter:0.1.0") // or -play, -micronaut, -quarkus, or moneta-api alone + implementation("cz.insuit:moneta-api-spring-boot-starter:0.2.0") // or -play, -micronaut, -quarkus, or moneta-api alone } // gh's token, or nothing when gh is missing or signed out: the build then gets GitHub's 401, not a crash. @@ -71,7 +71,7 @@ fun gh(vararg args: String): Provider = cz.insuit moneta-api-spring-boot-starter - 0.1.0 + 0.2.0 ``` diff --git a/modules/moneta-api-micronaut/README.md b/modules/moneta-api-micronaut/README.md index ac8636d..1ab8ae6 100644 --- a/modules/moneta-api-micronaut/README.md +++ b/modules/moneta-api-micronaut/README.md @@ -3,6 +3,32 @@ Micronaut wiring for the client: add the dependency, set `moneta.*`, inject `MonetaClient`. One jar for Micronaut 4.10 and 5; [testing/micronaut](../../testing/README.md) checks each. +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api-micronaut:0.2.0") +} +``` + +
Maven + +```xml + + cz.insuit + moneta-api-micronaut + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). + +## Configure + ```yaml moneta: token: ${MONETA_TOKEN} @@ -12,6 +38,38 @@ That is the whole setup for an application reading one account: inject `MonetaCl serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. +```java +@Singleton +class Payments { + private final AccountHolder me; + + Payments(MonetaClient moneta, AccessToken token) { + this.me = moneta.with(token); + } + + List paidToday() { + var today = LocalDate.now(ZoneId.of("Europe/Prague")); + return me.transactions().between(today, today).incoming().list(); + } +} +``` + +
Kotlin + +```kotlin +@Singleton +class Payments(moneta: MonetaClient, token: AccessToken) { + private val me = moneta.with(token) + + fun paidToday(): List { + val today = LocalDate.now(ZoneId.of("Europe/Prague")) + return me.transactions().between(today, today).incoming().list() + } +} +``` + +
+ A `MonetaClient` bean of the application's own replaces the one built here. Like every Micronaut bean, the client is built when first injected, not at startup, and closed with the context. A token or partner value it would refuse fails when `moneta.*` is bound; to have that stop the start, without building the client, opt in as Micronaut diff --git a/modules/moneta-api-play/README.md b/modules/moneta-api-play/README.md index 7ce178e..8575eba 100644 --- a/modules/moneta-api-play/README.md +++ b/modules/moneta-api-play/README.md @@ -4,6 +4,32 @@ A Play Framework module for the client: add the dependency, set `moneta.*`, inje `reference.conf` enables it; nothing else to register. Works on Play 3.0 (Scala 2.13 and 3) and 2.9; [testing/play](../../testing/README.md) checks each. +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api-play:0.2.0") +} +``` + +
Maven + +```xml + + cz.insuit + moneta-api-play + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). + +## Configure + ```hocon # application.conf moneta.token = ${?MONETA_TOKEN} @@ -13,6 +39,25 @@ That is the whole setup for an application reading one account: inject `MonetaCl serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. +```java +@Singleton +class Payments { + private final AccountHolder me; + + @Inject + Payments(MonetaClient moneta, AccessToken token) { + this.me = moneta.with(token); + } + + List paidToday() { + var today = LocalDate.now(ZoneId.of("Europe/Prague")); + return me.transactions().between(today, today).incoming().list(); + } +} +``` + +The call blocks: run it on the pool below, not Play's default one. + The client is a singleton built when the application starts, so a token it would refuse stops the start, and it is closed when the application stops, a dev-mode reload included. diff --git a/modules/moneta-api-quarkus/README.md b/modules/moneta-api-quarkus/README.md index be90e69..f9f01fc 100644 --- a/modules/moneta-api-quarkus/README.md +++ b/modules/moneta-api-quarkus/README.md @@ -4,6 +4,32 @@ A Quarkus extension for the client: add the dependency, set `quarkus.moneta.*`, mode and as a native image. Compiled against Quarkus 3.33 LTS; [testing/quarkus](../../testing/README.md) checks 3.33 and 3.40. +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api-quarkus:0.2.0") +} +``` + +
Maven + +```xml + + cz.insuit + moneta-api-quarkus + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). Quarkus fetches `moneta-api-quarkus-deployment` from the same repository at build time. + +## Configure + ```properties quarkus.moneta.token=${MONETA_TOKEN} ``` @@ -12,6 +38,38 @@ That is the whole setup for an application reading one account: inject `MonetaCl serving many merchants leaves `quarkus.moneta.token` unset and passes each one's token with the call; injecting `AccessToken` then fails, naming the property. Keep the token in the environment, never in a committed file. +```java +@ApplicationScoped +class Payments { + private final AccountHolder me; + + Payments(MonetaClient moneta, AccessToken token) { + this.me = moneta.with(token); + } + + List paidToday() { + var today = LocalDate.now(ZoneId.of("Europe/Prague")); + return me.transactions().between(today, today).incoming().list(); + } +} +``` + +
Kotlin + +```kotlin +@ApplicationScoped +class Payments(moneta: MonetaClient, token: AccessToken) { + private val me = moneta.with(token) + + fun paidToday(): List { + val today = LocalDate.now(ZoneId.of("Europe/Prague")) + return me.transactions().between(today, today).incoming().list() + } +} +``` + +
+ | Property | Default | |----------|---------| | `quarkus.moneta.token` | none: no `AccessToken` bean | diff --git a/modules/moneta-api-spring-boot-starter/README.md b/modules/moneta-api-spring-boot-starter/README.md index d77ee53..811a94c 100644 --- a/modules/moneta-api-spring-boot-starter/README.md +++ b/modules/moneta-api-spring-boot-starter/README.md @@ -3,6 +3,32 @@ Spring Boot auto-configuration for the client: add the dependency, set `moneta.*`, inject `MonetaClient`. Works on Spring Boot 3.5 and 4; [testing/spring-boot](../../testing/README.md) checks each. +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api-spring-boot-starter:0.2.0") +} +``` + +
Maven + +```xml + + cz.insuit + moneta-api-spring-boot-starter + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). + +## Configure + ```yaml moneta: token: ${MONETA_TOKEN} @@ -12,6 +38,38 @@ That is the whole setup for an application reading one account: inject `MonetaCl serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. +```java +@Service +class Payments { + private final AccountHolder me; + + Payments(MonetaClient moneta, AccessToken token) { + this.me = moneta.with(token); + } + + List paidToday() { + var today = LocalDate.now(ZoneId.of("Europe/Prague")); + return me.transactions().between(today, today).incoming().list(); + } +} +``` + +
Kotlin + +```kotlin +@Service +class Payments(moneta: MonetaClient, token: AccessToken) { + private val me = moneta.with(token) + + fun paidToday(): List { + val today = LocalDate.now(ZoneId.of("Europe/Prague")) + return me.transactions().between(today, today).incoming().list() + } +} +``` + +
+ A `MonetaClient` bean of the application's own replaces the auto-configured one. A token the client would refuse stops the application at startup, not at the first call. IntelliJ completes the properties from the metadata the starter ships. diff --git a/modules/moneta-api/README.md b/modules/moneta-api/README.md new file mode 100644 index 0000000..ff35818 --- /dev/null +++ b/modules/moneta-api/README.md @@ -0,0 +1,39 @@ +# moneta-api + +The MONETA API client for Java 21+ and Kotlin, without a framework. With Spring Boot, Play, Micronaut or Quarkus, +take that module instead; it brings this one. + +## Install + +```kotlin +// build.gradle.kts +dependencies { + implementation("cz.insuit:moneta-api:0.2.0") +} +``` + +
Maven + +```xml + + cz.insuit + moneta-api + 0.2.0 + +``` + +
+ +GitHub Packages asks for a token even for a public package: the repository block and the token are in +[Installing](../../.docs/install.md). + +## Use + +```java +var moneta = new MonetaClient(); +var me = moneta.with(new AccessToken(System.getenv("MONETA_TOKEN"))); + +me.transactions().between(LocalDate.of(2026, 11, 1), LocalDate.of(2026, 11, 30)).incoming().booked().list(); +``` + +The rest is in [Using the client](../../.docs/usage.md). From 850756d0052b95faddb104c3545ec4dfe5d72817 Mon Sep 17 00:00:00 2001 From: Michal Landsman Date: Tue, 10 Nov 2026 17:00:42 +0100 Subject: [PATCH 2/4] docs: make the module examples read booked money only, with imports, and sbt for play --- .docs/install.md | 2 +- build.gradle.kts | 2 +- modules/moneta-api-micronaut/README.md | 39 +++++++++++--- modules/moneta-api-play/README.md | 51 ++++++++++++++----- modules/moneta-api-quarkus/README.md | 39 +++++++++++--- .../moneta-api-spring-boot-starter/README.md | 39 +++++++++++--- modules/moneta-api/README.md | 9 ++-- 7 files changed, 137 insertions(+), 44 deletions(-) diff --git a/.docs/install.md b/.docs/install.md index 3972320..83565aa 100644 --- a/.docs/install.md +++ b/.docs/install.md @@ -80,4 +80,4 @@ and in `~/.m2/settings.xml` a `` with the same `id`, your GitHub user an ## Snapshots None are published: only a tagged release is. To try what is not released yet, `make publish-local` in a checkout -installs every jar into `~/.m2` as `0.1.0-SNAPSHOT`, where `mavenLocal()` finds them. +installs every jar into `~/.m2` as `0.3.0-SNAPSHOT`, where `mavenLocal()` finds them. diff --git a/build.gradle.kts b/build.gradle.kts index cf2d489..391856e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ plugins { val testsJdk = providers.gradleProperty("testsJdk") // A tag vX.Y.Z releases X.Y.Z (-PreleaseVersion, set by `make publish`); anything else is the next snapshot. -val releaseVersion = providers.gradleProperty("releaseVersion").orElse("0.1.0-SNAPSHOT") +val releaseVersion = providers.gradleProperty("releaseVersion").orElse("0.3.0-SNAPSHOT") allprojects { group = "cz.insuit" diff --git a/modules/moneta-api-micronaut/README.md b/modules/moneta-api-micronaut/README.md index 1ab8ae6..f3ba438 100644 --- a/modules/moneta-api-micronaut/README.md +++ b/modules/moneta-api-micronaut/README.md @@ -15,6 +15,7 @@ dependencies {
Maven ```xml + cz.insuit moneta-api-micronaut @@ -35,21 +36,31 @@ moneta: ``` That is the whole setup for an application reading one account: inject `MonetaClient` and `AccessToken`. One -serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in +serving many account holders leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. ```java +import cz.insuit.moneta.AccessToken; +import cz.insuit.moneta.AccountHolder; +import cz.insuit.moneta.MonetaClient; +import cz.insuit.moneta.Transaction; +import jakarta.inject.Singleton; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; + @Singleton -class Payments { +public class Payments { private final AccountHolder me; - Payments(MonetaClient moneta, AccessToken token) { + public Payments(MonetaClient moneta, AccessToken token) { this.me = moneta.with(token); } - List paidToday() { + /** Money received over the last week, booked. */ + public List receivedThisWeek() { var today = LocalDate.now(ZoneId.of("Europe/Prague")); - return me.transactions().between(today, today).incoming().list(); + return me.transactions().between(today.minusDays(7), today).incoming().booked().list(); } } ``` @@ -57,19 +68,31 @@ class Payments {
Kotlin ```kotlin +import cz.insuit.moneta.AccessToken +import cz.insuit.moneta.MonetaClient +import cz.insuit.moneta.Transaction +import jakarta.inject.Singleton +import java.time.LocalDate +import java.time.ZoneId + @Singleton class Payments(moneta: MonetaClient, token: AccessToken) { private val me = moneta.with(token) - fun paidToday(): List { + /** Money received over the last week, booked. */ + fun receivedThisWeek(): List { val today = LocalDate.now(ZoneId.of("Europe/Prague")) - return me.transactions().between(today, today).incoming().list() + return me.transactions().between(today.minusDays(7), today).incoming().booked().list() } } ```
+It reads the only account the token opens and throws `IllegalStateException` when there are more; then, or with +one token per account holder, see [More than one account](../../.docs/usage.md#more-than-one-account). +Without `moneta.token` there is no `AccessToken` bean to inject. + A `MonetaClient` bean of the application's own replaces the one built here. Like every Micronaut bean, the client is built when first injected, not at startup, and closed with the context. A token or partner value it would refuse fails when `moneta.*` is bound; to have that stop the start, without building the client, opt in as Micronaut @@ -156,7 +179,7 @@ None is needed against production. The names are the Spring Boot starter's. |----------|---------|---| | `moneta.base-uri` | production, `MonetaClient.PRODUCTION` | the sandbox's host once the portal gives it | | `moneta.request-timeout` | the client's, 30 s | one call to MONETA: `20s` | -| `moneta.partner.identification-number`, `.business-name`, `.application-name` | — | the registered application's headers, all three or none, ASCII only | +| `moneta.partner.identification-number`, `.business-name`, `.application-name` | none | the registered application's headers, all three or none, ASCII only | MONETA does not ask for the partner headers: every call so far went through without ([how it behaves](../../.docs/moneta-api.md)). Likely for an application approved on the developer portal; set them once the diff --git a/modules/moneta-api-play/README.md b/modules/moneta-api-play/README.md index 8575eba..23a8683 100644 --- a/modules/moneta-api-play/README.md +++ b/modules/moneta-api-play/README.md @@ -6,6 +6,15 @@ A Play Framework module for the client: add the dependency, set `moneta.*`, inje ## Install +```scala +// build.sbt +resolvers += "GitHub Packages" at "https://maven.pkg.github.com/landsman/moneta-api-java" +credentials += Credentials("GitHub Package Registry", "maven.pkg.github.com", "gh", sys.env.getOrElse("GITHUB_TOKEN", "")) +libraryDependencies += "cz.insuit" % "moneta-api-play" % "0.2.0" +``` + +`GITHUB_TOKEN` holds a token with `read:packages`; Gradle and Maven below. + ```kotlin // build.gradle.kts dependencies { @@ -16,6 +25,7 @@ dependencies {
Maven ```xml + cz.insuit moneta-api-play @@ -36,27 +46,42 @@ moneta.token = ${?MONETA_TOKEN} ``` That is the whole setup for an application reading one account: inject `MonetaClient` and `AccessToken`. One -serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in +serving many account holders leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. ```java +import cz.insuit.moneta.AccessToken; +import cz.insuit.moneta.AccountHolder; +import cz.insuit.moneta.MonetaClient; +import cz.insuit.moneta.Transaction; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + @Singleton -class Payments { +public class Payments { private final AccountHolder me; @Inject - Payments(MonetaClient moneta, AccessToken token) { + public Payments(MonetaClient moneta, AccessToken token) { this.me = moneta.with(token); } - List paidToday() { + /** Money received over the last week, booked. */ + public List receivedThisWeek() { var today = LocalDate.now(ZoneId.of("Europe/Prague")); - return me.transactions().between(today, today).incoming().list(); + return me.transactions().between(today.minusDays(7), today).incoming().booked().list(); } } ``` -The call blocks: run it on the pool below, not Play's default one. +It reads the only account the token opens and throws `IllegalStateException` when there are more; then, or with +one token per account holder, see [More than one account](../../.docs/usage.md#more-than-one-account). +Without `moneta.token` there is no `AccessToken` bean to inject. + +The call blocks: run it on [a pool of its own](#calls-block-give-them-a-pool-of-their-own), not Play's default one. The client is a singleton built when the application starts, so a token it would refuse stops the start, and it is closed when the application stops, a dev-mode reload included. @@ -89,18 +114,16 @@ public class BlockingExecutor implements Executor { ```java public class PaymentsController extends Controller { - private final MonetaClient moneta; - private final AccessToken token; + private final Payments payments; private final BlockingExecutor blocking; private final ClassLoaderExecutionContext http; @Inject - public PaymentsController( - MonetaClient moneta, AccessToken token, BlockingExecutor blocking, ClassLoaderExecutionContext http) { … } + public PaymentsController(Payments payments, BlockingExecutor blocking, ClassLoaderExecutionContext http) { … } - public CompletionStage incoming() { - return CompletableFuture.supplyAsync(() -> moneta.incoming(token, accountId, from, to), blocking) - .thenApplyAsync(ts -> ok(views.html.incoming.render(ts)), http.current()); // back off the blocking pool + public CompletionStage received() { + return CompletableFuture.supplyAsync(payments::receivedThisWeek, blocking) + .thenApplyAsync(ts -> ok(views.html.received.render(ts)), http.current()); // back off the blocking pool } } ``` @@ -171,7 +194,7 @@ None is needed against production. The keys are the Spring Boot starter's. |-----|---------|---| | `moneta.base-uri` | production, `MonetaClient.PRODUCTION` | the sandbox's host once the portal gives it | | `moneta.request-timeout` | the client's, 30 s | one call to MONETA, as a HOCON duration: `20s` | -| `moneta.partner.identification-number`, `.business-name`, `.application-name` | — | the registered application's headers, all three or none, ASCII only | +| `moneta.partner.identification-number`, `.business-name`, `.application-name` | none | the registered application's headers, all three or none, ASCII only | MONETA does not ask for the partner headers: every call so far went through without ([how it behaves](../../.docs/moneta-api.md)). Likely for an application approved on the developer portal; set them once the diff --git a/modules/moneta-api-quarkus/README.md b/modules/moneta-api-quarkus/README.md index f9f01fc..b85062e 100644 --- a/modules/moneta-api-quarkus/README.md +++ b/modules/moneta-api-quarkus/README.md @@ -16,6 +16,7 @@ dependencies {
Maven ```xml + cz.insuit moneta-api-quarkus @@ -26,7 +27,8 @@ dependencies {
GitHub Packages asks for a token even for a public package: the repository block and the token are in -[Installing](../../.docs/install.md). Quarkus fetches `moneta-api-quarkus-deployment` from the same repository at build time. +[Installing](../../.docs/install.md). Maven needs both, the `` entry and the ``, because +Quarkus fetches `moneta-api-quarkus-deployment` from there at build time. ## Configure @@ -35,21 +37,31 @@ quarkus.moneta.token=${MONETA_TOKEN} ``` That is the whole setup for an application reading one account: inject `MonetaClient` and `AccessToken`. One -serving many merchants leaves `quarkus.moneta.token` unset and passes each one's token with the call; injecting +serving many account holders leaves `quarkus.moneta.token` unset and passes each one's token with the call; injecting `AccessToken` then fails, naming the property. Keep the token in the environment, never in a committed file. ```java +import cz.insuit.moneta.AccessToken; +import cz.insuit.moneta.AccountHolder; +import cz.insuit.moneta.MonetaClient; +import cz.insuit.moneta.Transaction; +import jakarta.enterprise.context.ApplicationScoped; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; + @ApplicationScoped -class Payments { +public class Payments { private final AccountHolder me; - Payments(MonetaClient moneta, AccessToken token) { + public Payments(MonetaClient moneta, AccessToken token) { this.me = moneta.with(token); } - List paidToday() { + /** Money received over the last week, booked. */ + public List receivedThisWeek() { var today = LocalDate.now(ZoneId.of("Europe/Prague")); - return me.transactions().between(today, today).incoming().list(); + return me.transactions().between(today.minusDays(7), today).incoming().booked().list(); } } ``` @@ -57,19 +69,30 @@ class Payments {
Kotlin ```kotlin +import cz.insuit.moneta.AccessToken +import cz.insuit.moneta.MonetaClient +import cz.insuit.moneta.Transaction +import jakarta.enterprise.context.ApplicationScoped +import java.time.LocalDate +import java.time.ZoneId + @ApplicationScoped class Payments(moneta: MonetaClient, token: AccessToken) { private val me = moneta.with(token) - fun paidToday(): List { + /** Money received over the last week, booked. */ + fun receivedThisWeek(): List { val today = LocalDate.now(ZoneId.of("Europe/Prague")) - return me.transactions().between(today, today).incoming().list() + return me.transactions().between(today.minusDays(7), today).incoming().booked().list() } } ```
+It reads the only account the token opens and throws `IllegalStateException` when there are more; then, or with +one token per account holder, see [More than one account](../../.docs/usage.md#more-than-one-account). + | Property | Default | |----------|---------| | `quarkus.moneta.token` | none: no `AccessToken` bean | diff --git a/modules/moneta-api-spring-boot-starter/README.md b/modules/moneta-api-spring-boot-starter/README.md index 811a94c..4377f78 100644 --- a/modules/moneta-api-spring-boot-starter/README.md +++ b/modules/moneta-api-spring-boot-starter/README.md @@ -15,6 +15,7 @@ dependencies {
Maven ```xml + cz.insuit moneta-api-spring-boot-starter @@ -35,21 +36,31 @@ moneta: ``` That is the whole setup for an application reading one account: inject `MonetaClient` and `AccessToken`. One -serving many merchants leaves `moneta.token` unset and passes each one's token with the call. Keep the token in +serving many account holders leaves `moneta.token` unset and passes each one's token with the call. Keep the token in the environment, never in a committed file. ```java +import cz.insuit.moneta.AccessToken; +import cz.insuit.moneta.AccountHolder; +import cz.insuit.moneta.MonetaClient; +import cz.insuit.moneta.Transaction; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; +import org.springframework.stereotype.Service; + @Service -class Payments { +public class Payments { private final AccountHolder me; - Payments(MonetaClient moneta, AccessToken token) { + public Payments(MonetaClient moneta, AccessToken token) { this.me = moneta.with(token); } - List paidToday() { + /** Money received over the last week, booked. */ + public List receivedThisWeek() { var today = LocalDate.now(ZoneId.of("Europe/Prague")); - return me.transactions().between(today, today).incoming().list(); + return me.transactions().between(today.minusDays(7), today).incoming().booked().list(); } } ``` @@ -57,19 +68,31 @@ class Payments {
Kotlin ```kotlin +import cz.insuit.moneta.AccessToken +import cz.insuit.moneta.MonetaClient +import cz.insuit.moneta.Transaction +import java.time.LocalDate +import java.time.ZoneId +import org.springframework.stereotype.Service + @Service class Payments(moneta: MonetaClient, token: AccessToken) { private val me = moneta.with(token) - fun paidToday(): List { + /** Money received over the last week, booked. */ + fun receivedThisWeek(): List { val today = LocalDate.now(ZoneId.of("Europe/Prague")) - return me.transactions().between(today, today).incoming().list() + return me.transactions().between(today.minusDays(7), today).incoming().booked().list() } } ```
+It reads the only account the token opens and throws `IllegalStateException` when there are more; then, or with +one token per account holder, see [More than one account](../../.docs/usage.md#more-than-one-account). +Without `moneta.token` there is no `AccessToken` bean to inject. + A `MonetaClient` bean of the application's own replaces the auto-configured one. A token the client would refuse stops the application at startup, not at the first call. IntelliJ completes the properties from the metadata the starter ships. @@ -84,7 +107,7 @@ None is needed against production. |----------|---------|---| | `moneta.base-uri` | production, `MonetaClient.PRODUCTION` | the sandbox's host once the portal gives it | | `moneta.request-timeout` | the client's, 30 s | one call to MONETA | -| `moneta.partner.identification-number`, `.business-name`, `.application-name` | — | the registered application's headers, all three or none, ASCII only | +| `moneta.partner.identification-number`, `.business-name`, `.application-name` | none | the registered application's headers, all three or none, ASCII only | MONETA does not ask for the partner headers: every call so far went through without ([how it behaves](../../.docs/moneta-api.md)). Likely for an application approved on the developer portal; set them once the diff --git a/modules/moneta-api/README.md b/modules/moneta-api/README.md index ff35818..30337d3 100644 --- a/modules/moneta-api/README.md +++ b/modules/moneta-api/README.md @@ -15,6 +15,7 @@ dependencies {
Maven ```xml + cz.insuit moneta-api @@ -30,10 +31,10 @@ GitHub Packages asks for a token even for a public package: the repository block ## Use ```java -var moneta = new MonetaClient(); -var me = moneta.with(new AccessToken(System.getenv("MONETA_TOKEN"))); - -me.transactions().between(LocalDate.of(2026, 11, 1), LocalDate.of(2026, 11, 30)).incoming().booked().list(); +try (var moneta = new MonetaClient()) { // one per application; close it when the application stops + var me = moneta.with(new AccessToken(System.getenv("MONETA_TOKEN"))); + me.transactions().between(LocalDate.of(2026, 11, 1), LocalDate.of(2026, 11, 30)).incoming().booked().list(); +} ``` The rest is in [Using the client](../../.docs/usage.md). From bb995b5a65a6f7b204eea094bed6a5c8b499a9b3 Mon Sep 17 00:00:00 2001 From: Michal Landsman Date: Tue, 10 Nov 2026 16:58:27 +0100 Subject: [PATCH 3/4] docs: add llms.txt, so a coding assistant sets the library up without guessing its api --- README.md | 1 + llms.txt | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 llms.txt diff --git a/README.md b/README.md index 7339d7a..eba1372 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Maven Central. | [Roadmap](.docs/plan/roadmap.md) | the working plan: what is still unknown, what comes next, what is not covered | | [Development](.docs/development.md) | build, tests, `make dev` against a real account | | [Example café](examples/cafe/README.md) | bills as QR payments, found paid; try it at http://localhost:8080/scalar after `make dev` | +| [llms.txt](llms.txt) | the setup and the mistakes to avoid, for a coding assistant adding the library to a project | ## What it covers diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..848e508 --- /dev/null +++ b/llms.txt @@ -0,0 +1,47 @@ +# moneta-api-java + +> A Java 21+ and Kotlin client for MONETA API, the free API of the Czech bank MONETA Money Bank for business +> accounts: it reads accounts, balances and transactions with a token the account holder issues in Internet Banka. +> Modules for Spring Boot, Play, Micronaut and Quarkus. Published on GitHub Packages (group `cz.insuit`), not Maven +> Central. Pre-1.0: read records by accessor, never by record pattern. + +Setting it up in a project: + +- Add the module for the project's framework, or `cz.insuit:moneta-api` without one. The framework modules bring + `moneta-api` with them; do not add both. +- Add the GitHub Packages repository `https://maven.pkg.github.com/landsman/moneta-api-java`. It asks for a token + even for a public package (`read:packages`). Never write a token into the build file or the repository. +- The MONETA token comes from the environment: `moneta.token` (Spring Boot, Play, Micronaut) or + `quarkus.moneta.token` (Quarkus), which also provides an `AccessToken` bean. +- One `MonetaClient` per application (framework modules provide it as a bean). Bind a token with + `moneta.with(token)`, then `transactions().between(from, to)`, optionally `.incoming()`, `.outgoing()`, + `.transfers()`, `.cards()`, `.booked()`, `.pending()`, and `.list()`. + +Mistakes to avoid: + +- `entryReference` changes when a pending movement is booked: deduplicating by it counts a payment twice. +- Movements have dates only, no time of day. +- `amount()` is signed: negative is money out. +- `Transaction.Method` gains constants: keep a `default` branch. +- A read is all or nothing across pages; read long ranges month by month. +- The library filters by method, direction and status in memory: only a shorter date range makes a read cheaper. +- Do not return a `Transaction` from a controller as JSON: `raw()` carries the counterparty's name and account. + +## Docs + +- [Installing](https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md): the repository block and the token, for Gradle and Maven +- [Using the client](https://github.com/landsman/moneta-api-java/blob/main/.docs/usage.md): calls, filtering, more than one account, failures +- [How MONETA API behaves](https://github.com/landsman/moneta-api-java/blob/main/.docs/moneta-api.md): pending and booked movements, dates, ids, errors + +## Modules + +- [moneta-api](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api/README.md): the client alone +- [Spring Boot starter](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-spring-boot-starter/README.md): `moneta.*` in application.yml, a `MonetaClient` bean +- [Play module](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-play/README.md): `moneta.*` in application.conf; calls block, run them on their own pool +- [Micronaut module](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-micronaut/README.md): `moneta.*`, a `MonetaClient` bean +- [Quarkus extension](https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-quarkus/README.md): `quarkus.moneta.*`, JVM and native + +## Optional + +- [Kotlin and Spring Boot](https://github.com/landsman/moneta-api-java/blob/main/.docs/compatibility.md): Spring Boot 3 before 3.5.13 needs one dependency pinned; native images +- [Roadmap](https://github.com/landsman/moneta-api-java/blob/main/.docs/plan/roadmap.md): what is still unknown From 58fd7b138ee476750d6b1c110bc61efb033bdf61 Mon Sep 17 00:00:00 2001 From: Michal Landsman Date: Tue, 10 Nov 2026 17:01:25 +0100 Subject: [PATCH 4/4] docs: give each module readme a prompt to paste into a coding assistant --- llms.txt | 3 ++- modules/moneta-api-micronaut/README.md | 17 +++++++++++++++++ modules/moneta-api-play/README.md | 17 +++++++++++++++++ modules/moneta-api-quarkus/README.md | 17 +++++++++++++++++ .../moneta-api-spring-boot-starter/README.md | 17 +++++++++++++++++ modules/moneta-api/README.md | 17 +++++++++++++++++ 6 files changed, 87 insertions(+), 1 deletion(-) diff --git a/llms.txt b/llms.txt index 848e508..7854eec 100644 --- a/llms.txt +++ b/llms.txt @@ -7,7 +7,8 @@ Setting it up in a project: -- Add the module for the project's framework, or `cz.insuit:moneta-api` without one. The framework modules bring +- Add the module for the project's framework, or `cz.insuit:moneta-api` without one; each module's README has + the Gradle and Maven lines, sbt for Play, and a prompt for a coding assistant. The framework modules bring `moneta-api` with them; do not add both. - Add the GitHub Packages repository `https://maven.pkg.github.com/landsman/moneta-api-java`. It asks for a token even for a public package (`read:packages`). Never write a token into the build file or the repository. diff --git a/modules/moneta-api-micronaut/README.md b/modules/moneta-api-micronaut/README.md index f3ba438..75f5ca5 100644 --- a/modules/moneta-api-micronaut/README.md +++ b/modules/moneta-api-micronaut/README.md @@ -28,6 +28,23 @@ dependencies { GitHub Packages asks for a token even for a public package: the repository block and the token are in [Installing](../../.docs/install.md). +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this Micronaut project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-micronaut/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api-micronaut:0.2.0 from GitHub Packages, with Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: moneta.token: ${MONETA_TOKEN} in application.yml. +4. Code: inject MonetaClient and AccessToken. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + ## Configure ```yaml diff --git a/modules/moneta-api-play/README.md b/modules/moneta-api-play/README.md index 23a8683..831c85b 100644 --- a/modules/moneta-api-play/README.md +++ b/modules/moneta-api-play/README.md @@ -38,6 +38,23 @@ dependencies { GitHub Packages asks for a token even for a public package: the repository block and the token are in [Installing](../../.docs/install.md). +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this Play Framework project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-play/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api-play:0.2.0 from GitHub Packages, with sbt, Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: moneta.token = ${?MONETA_TOKEN} in application.conf. +4. Code: inject MonetaClient and AccessToken (javax.inject), and run the calls on a pool of their own, not Play's default one. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + ## Configure ```hocon diff --git a/modules/moneta-api-quarkus/README.md b/modules/moneta-api-quarkus/README.md index b85062e..1e8ef2d 100644 --- a/modules/moneta-api-quarkus/README.md +++ b/modules/moneta-api-quarkus/README.md @@ -30,6 +30,23 @@ GitHub Packages asks for a token even for a public package: the repository block [Installing](../../.docs/install.md). Maven needs both, the `` entry and the ``, because Quarkus fetches `moneta-api-quarkus-deployment` from there at build time. +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this Quarkus project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-quarkus/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api-quarkus:0.2.0 from GitHub Packages, with Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: quarkus.moneta.token=${MONETA_TOKEN} in application.properties. +4. Code: inject MonetaClient and AccessToken. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + ## Configure ```properties diff --git a/modules/moneta-api-spring-boot-starter/README.md b/modules/moneta-api-spring-boot-starter/README.md index 4377f78..0f61698 100644 --- a/modules/moneta-api-spring-boot-starter/README.md +++ b/modules/moneta-api-spring-boot-starter/README.md @@ -28,6 +28,23 @@ dependencies { GitHub Packages asks for a token even for a public package: the repository block and the token are in [Installing](../../.docs/install.md). +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this Spring Boot project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api-spring-boot-starter/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api-spring-boot-starter:0.2.0 from GitHub Packages, with Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: moneta.token: ${MONETA_TOKEN} in application.yml. +4. Code: inject MonetaClient and AccessToken. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + ## Configure ```yaml diff --git a/modules/moneta-api/README.md b/modules/moneta-api/README.md index 30337d3..5e661ad 100644 --- a/modules/moneta-api/README.md +++ b/modules/moneta-api/README.md @@ -28,6 +28,23 @@ dependencies { GitHub Packages asks for a token even for a public package: the repository block and the token are in [Installing](../../.docs/install.md). +## With a coding assistant + +Paste this into it: + +```text +Add the MONETA API client to this plain Java or Kotlin project. + +1. Read https://github.com/landsman/moneta-api-java/blob/main/llms.txt and https://github.com/landsman/moneta-api-java/blob/main/modules/moneta-api/README.md first. Do not guess the API. +2. Dependency: cz.insuit:moneta-api:0.2.0 from GitHub Packages, with Gradle or Maven, as + https://github.com/landsman/moneta-api-java/blob/main/.docs/install.md shows. Never write a token into the project. +3. Configuration: the token from the MONETA_TOKEN environment variable, wrapped in AccessToken. +4. Code: make one MonetaClient for the application and close it when the application stops. Bind the token with moneta.with(token), read with + transactions().between(from, to), narrowed by incoming(), outgoing(), transfers(), + cards(), booked() or pending(), then list(). +5. Act on booked movements only, unless I ask for pending ones. +``` + ## Use ```java