### Create and Manage GPG Keys Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/MAVEN.md Use Homebrew to install GnuPG, then generate a new GPG key. After generation, upload your public key to a keyserver and export both your public and secret keys in base64 format for use in CI/CD environments. ```shell brew install gnupg gpg --gen-key gpg --keyserver keyserver.ubuntu.com --send-keys KEYID gpg --export KEYID | base64 gpg --export-secret-keys KEYID | base64 ``` -------------------------------- ### Make API Request with HttpClient Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Example of using ApiRequest.get to fetch data from an API endpoint and parse the response using ObjectMapper. ```java HttpResponse response = ApiRequest.get(ApiEndpoint.STORES, API_BEARER_TOKEN); StoreListResponse list = objectMapper.readValue(response.body(), StoreListResponse.class); System.out.println("Number of shops: " + list.getStores().size()); for (Store store : list.getStores()) { System.out.println("ID " + store.getId() + ": " + store.getAttributes().getName()); } ``` -------------------------------- ### Fetch Stores using ApiRequest GET Helper Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Make a Bearer-authenticated GET request to fetch all stores using ApiRequest.get(). Deserialization of the raw HttpResponse is handled manually. ```java import be.codewriter.lemonsqueezy.api.ApiRequest; import be.codewriter.lemonsqueezy.generic.ApiEndpoint; import be.codewriter.lemonsqueezy.store.Store; import be.codewriter.lemonsqueezy.store.StoreListResponse; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.net.http.HttpResponse; String API_KEY = "eyJ0eXAiOiJKV1Qi..."; // your Lemon Squeezy API key ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); // Fetch all stores HttpResponse response = ApiRequest.get(ApiEndpoint.STORES, API_KEY); StoreListResponse list = objectMapper.readValue(response.body(), StoreListResponse.class); System.out.println("Number of stores: " + list.getStores().size()); for (Store store : list.getStores()) { System.out.println("Store #" + store.getId() + " | " + store.getAttributes().getName() + " | Currency: " + store.getAttributes().getCurrency() + " | Total revenue: " + store.getAttributes().getTotalRevenue()); } // Example output: // Number of stores: 3 // Store #1 | My Store | Currency: USD | Total revenue: 999.0 ``` -------------------------------- ### Implement Spring Boot Webhook Controller Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Example implementation of a Spring Boot RestController to handle webhooks from Lemon Squeezy. It includes parsing the incoming JSON message and registering a JavaTimeModule for timestamp handling. ```java @RestController @RequestMapping("/api/callback") @PermitAll public class CallbackController { private final ObjectMapper objectMapper = new ObjectMapper(); public CallbackController() { // This is needed to parse the timestamps provided in JSON format by Lemon Squeezy objectMapper.registerModule(new JavaTimeModule()); } @PostMapping() public void webhook(@RequestBody String message) { // 1. Authenticate the webhook request from Lemon Squeezy using the `X-Signature` header // 2. Parse the payload if the request is authenticated WebhookMessage webhookMessage = objectMapper.readValue(message), WebhookMessage.class) // 3. Do something with the webhookMessage, depending on our use-case } } ``` -------------------------------- ### Store Object Structure Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Explains the structure of the Store object, including Store, StoreAttributes, StoreResponse, and StoreListResponse, with an example of parsing JSON. ```APIDOC ## Store object (`Store` / `StoreAttributes` / `StoreResponse` / `StoreListResponse`) `Store` extends `Data` (carrying `id`, `type`, `relationships`, `links`). `StoreAttributes` extends `BaseAttributes` and adds store-specific fields including sales and revenue metrics. ```java import be.codewriter.lemonsqueezy.store.*; // Parse a single store response from JSON String json = """ { "jsonapi": { "version": "1.0" }, "links": { "self": "https://api.lemonsqueezy.com/v1/stores/1" }, "data": { "type": "stores", "id": "1", "attributes": { "name": "My Store", "slug": "my-store", "domain": "my-store.lemonsqueezy.com", "url": "https://my-store.lemonsqueezy.com", "plan": "fresh", "country": "US", "country_nicename": "United States", "currency": "USD", "total_sales": 1, "total_revenue": 999, "thirty_day_sales": 0, "thirty_day_revenue": 0, "created_at": "2021-05-24T14:15:06.000000Z", "updated_at": "2021-06-15T10:03:14.000000Z" } } } """; StoreResponse response = objectMapper.readValue(json, StoreResponse.class); Store store = response.getStore(); StoreAttributes attr = store.getAttributes(); System.out.println("ID: " + store.getId()); // "1" System.out.println("Name: " + attr.getName()); // "My Store" System.out.println("Domain: " + attr.getDomain()); // "my-store.lemonsqueezy.com" System.out.println("Currency: " + attr.getCurrency()); // "USD" System.out.println("Country: " + attr.getCountryNicename()); // "United States" System.out.println("Total revenue: " + attr.getTotalRevenue()); // 999.0 System.out.println("30-day sales: " + attr.getThirtyDaySales()); // 0.0 System.out.println("Created: " + attr.getCreatedAt()); // 2021-05-24T14:15:06 // Relationship links System.out.println(store.getRelationships().getProducts().getLinks().getRelated()); // → https://api.lemonsqueezy.com/v1/stores/1/products ``` ``` -------------------------------- ### Spring Boot Controller for LemonSqueezyClient Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Example of injecting LemonSqueezyClient into a Spring REST controller to handle paginated lists and single resource lookups. ```java package com.yourproject.controller; import be.codewriter.lemonsqueezy.configuration.LemonSqueezyClient; import be.codewriter.lemonsqueezy.generic.ApiEndpoint; import be.codewriter.lemonsqueezy.response.LemonSqueezyResponse; import be.codewriter.lemonsqueezy.variant.Variant; import be.codewriter.lemonsqueezy.variant.VariantResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.io.IOException; @RestController @RequestMapping("/api/lemonsqueezy") @RequiredArgsConstructor public class LemonSqueezyController { private final LemonSqueezyClient client; /** * Fetch a paginated list of variants (plans) */ @GetMapping("/list") public LemonSqueezyResponse getList() throws IOException, InterruptedException { // Uses the built-in generic list handler return client.getList(ApiEndpoint.VARIANTS); } /** * Fetch specific details for a single variant by ID */ @GetMapping("/item/{id}") public VariantResponse getItem(@PathVariable Long id) throws IOException, InterruptedException { // Maps the specific resource data to the VariantItem DTO return client.getItem(ApiEndpoint.VARIANTS, id, VariantResponse.class); } } ``` -------------------------------- ### ApiRequest GET Request Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt The ApiRequest.get() method allows you to perform Bearer-authenticated GET requests to the Lemon Squeezy API using java.net.http.HttpClient. It returns the raw HttpResponse, leaving deserialization to the caller. ```APIDOC ## ApiRequest — static HTTP helper `ApiRequest.get()` issues a Bearer-authenticated GET request using `java.net.http.HttpClient` and returns the raw `HttpResponse`. Deserialization is left to the caller. ```java import be.codewriter.lemonsqueezy.api.ApiRequest; import be.codewriter.lemonsqueezy.generic.ApiEndpoint; import be.codewriter.lemonsqueezy.store.Store; import be.codewriter.lemonsqueezy.store.StoreListResponse; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.net.http.HttpResponse; String API_KEY = "eyJ0eXAiOiJKV1Qi..."; // your Lemon Squeezy API key ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); // Fetch all stores HttpResponse response = ApiRequest.get(ApiEndpoint.STORES, API_KEY); StoreListResponse list = objectMapper.readValue(response.body(), StoreListResponse.class); System.out.println("Number of stores: " + list.getStores().size()); for (Store store : list.getStores()) { System.out.println("Store #" + store.getId() + " | " + store.getAttributes().getName() + " | Currency: " + store.getAttributes().getCurrency() + " | Total revenue: " + store.getAttributes().getTotalRevenue()); } // Example output: // Number of stores: 3 // Store #1 | My Store | Currency: USD | Total revenue: 999.0 ``` ``` -------------------------------- ### Get API Endpoint URL using ApiEndpoint Enum Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Use the ApiEndpoint enum to get the full URL for supported REST resources. This is used with ApiRequest and LemonSqueezyClient. ```java import be.codewriter.lemonsqueezy.generic.ApiEndpoint; // Prints: https://api.lemonsqueezy.com/v1/stores System.out.println(ApiEndpoint.STORES.getApiEndpoint()); // All available values: // ApiEndpoint.STORES → https://api.lemonsqueezy.com/v1/stores // ApiEndpoint.PRODUCTS → https://api.lemonsqueezy.com/v1/products // ApiEndpoint.VARIANTS → https://api.lemonsqueezy.com/v1/variants // ApiEndpoint.ORDERS → https://api.lemonsqueezy.com/v1/orders // ApiEndpoint.SUBSCRIPTIONS→ https://api.lemonsqueezy.com/v1/subscriptions ``` -------------------------------- ### Initialize and Use LemonSqueezyClient Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Instantiate the LemonSqueezyClient with an API key to perform API operations. Demonstrates fetching a store, a variant by ID, and a list of variants. ```java import be.codewriter.lemonsqueezy.configuration.LemonSqueezyClient; import be.codewriter.lemonsqueezy.generic.ApiEndpoint; import be.codewriter.lemonsqueezy.response.LemonSqueezyResponse; import be.codewriter.lemonsqueezy.store.StoreResponse; import be.codewriter.lemonsqueezy.variant.Variant; import be.codewriter.lemonsqueezy.variant.VariantResponse; LemonSqueezyClient client = new LemonSqueezyClient("your-api-key"); // 1. Fetch a single resource — GET /v1/stores (mapped to StoreResponse) StoreResponse storeResp = client.get(ApiEndpoint.STORES, StoreResponse.class); System.out.println("Store: " + storeResp.getStore().getAttributes().getName()); // 2. Fetch a resource by ID — GET /v1/variants/42 VariantResponse variantResp = client.getItem(ApiEndpoint.VARIANTS, 42L, VariantResponse.class); System.out.println("Variant name: " + variantResp.getVariant().getAttributes().getName()); // 3. Fetch a paginated list — GET /v1/variants LemonSqueezyResponse variantList = client.getList(ApiEndpoint.VARIANTS); System.out.println("Total variants: " + variantList.getData().size()); System.out.println("JSON:API version: " + variantList.getJsonapi().getVersion()); // "1.0" System.out.println("Self link: " + variantList.getLinks().getSelf()); ``` -------------------------------- ### Construct and Parse Checkout Objects Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Demonstrates building a checkout creation request by setting product, checkout, and customer data, then serializing it to JSON. It also shows how to parse a checkout API response. ```java import be.codewriter.lemonsqueezy.checkout.*; import be.codewriter.lemonsqueezy.product.ProductOptions; import be.codewriter.lemonsqueezy.relationship.*; // --- Build a checkout creation request --- ProductOptions productOptions = new ProductOptions(); productOptions.setRedirectUrl("https://yourapp.com/thank-you"); CheckoutOptions checkoutOptions = new CheckoutOptions(); checkoutOptions.setButtonColor("#2DD272"); checkoutOptions.setEmbed(false); checkoutOptions.setLogo(true); checkoutOptions.setDark(false); CheckoutData checkoutData = new CheckoutData(); checkoutData.setEmail("buyer@example.com"); checkoutData.setName("Jane Doe"); checkoutData.setDiscountCode("SAVE10"); checkoutData.setCustom(new CheckoutDataCustom(/* userId */ 42)); CheckoutAttributes attr = new CheckoutAttributes(); attr.setCustomerPrice(2999.0); // in cents: $29.99 attr.setProductOptions(productOptions); attr.setCheckoutOptions(checkoutOptions); attr.setCheckoutData(checkoutData); attr.setExpiresAt(LocalDateTime.of(2025, 12, 31, 23, 59, 59)); Relationships relationships = new Relationships(); relationships.setStore(new RelationshipItem(new RelationshipData("stores", "1"))); relationships.setVariant(new RelationshipItem(new RelationshipData("variants", "394420"))); Checkout checkout = new Checkout(attr); checkout.setRelationships(relationships); // Serialize to JSON for POST body String requestBody = objectMapper.writeValueAsString(checkout); // --- Parse a checkout API response --- CheckoutResponse response = objectMapper.readValue(responseJson, CheckoutResponse.class); Checkout parsed = response.getCheckout(); CheckoutAttributes parsedAttr = parsed.getAttributes(); System.out.println("Checkout ID: " + parsed.getId()); System.out.println("Variant ID: " + parsedAttr.getVariantId()); System.out.println("Checkout URL: " + parsedAttr.getUrl()); System.out.println("Expires at: " + parsedAttr.getExpiresAt()); System.out.println("Customer email: " + parsedAttr.getCheckoutData().getEmail()); System.out.println("Embed mode: " + parsedAttr.getCheckoutOptions().getEmbed()); System.out.println("Button color: " + parsedAttr.getCheckoutOptions().getButtonColor()); ``` -------------------------------- ### Spring Boot Configuration for API Key Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Add your Lemon Squeezy API key to the application.properties or application.yml file for automatic SDK initialization. ```properties lemonsqueezy.api-key=YOUR_API_KEY ``` -------------------------------- ### LemonSqueezyClient Usage Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Demonstrates how to use the LemonSqueezyClient to fetch single resources, resources by ID, and paginated lists. ```APIDOC ## LemonSqueezyClient Usage `LemonSqueezyClient` wraps the HTTP call with Jackson deserialization. It provides three methods: `get()` for single-resource responses mapped to a specific class, `getItem()` for a single resource by ID, and `getList()` for paginated list responses. ```java import be.codewriter.lemonsqueezy.configuration.LemonSqueezyClient; import be.codewriter.lemonsqueezy.generic.ApiEndpoint; import be.codewriter.lemonsqueezy.response.LemonSqueezyResponse; import be.codewriter.lemonsqueezy.store.StoreResponse; import be.codewriter.lemonsqueezy.variant.Variant; import be.codewriter.lemonsqueezy.variant.VariantResponse; LemonSqueezyClient client = new LemonSqueezyClient("your-api-key"); // 1. Fetch a single resource — GET /v1/stores (mapped to StoreResponse) StoreResponse storeResp = client.get(ApiEndpoint.STORES, StoreResponse.class); System.out.println("Store: " + storeResp.getStore().getAttributes().getName()); // 2. Fetch a resource by ID — GET /v1/variants/42 VariantResponse variantResp = client.getItem(ApiEndpoint.VARIANTS, 42L, VariantResponse.class); System.out.println("Variant name: " + variantResp.getVariant().getAttributes().getName()); // 3. Fetch a paginated list — GET /v1/variants LemonSqueezyResponse variantList = client.getList(ApiEndpoint.VARIANTS); System.out.println("Total variants: " + variantList.getData().size()); System.out.println("JSON:API version: " + variantList.getJsonapi().getVersion()); // "1.0" System.out.println("Self link: " + variantList.getLinks().getSelf()); ``` ``` -------------------------------- ### Spring Boot Controller Using LemonSqueezyClient Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt A Spring Boot controller demonstrating how to inject and use the auto-configured LemonSqueezyClient to expose API endpoints for variants. ```java // Spring Boot controller using the auto-configured bean import be.codewriter.lemonsqueezy.configuration.LemonSqueezyClient; import be.codewriter.lemonsqueezy.generic.ApiEndpoint; import be.codewriter.lemonsqueezy.response.LemonSqueezyResponse; import be.codewriter.lemonsqueezy.variant.Variant; import be.codewriter.lemonsqueezy.variant.VariantResponse; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/lemonsqueezy") public class LemonSqueezyController { private final LemonSqueezyClient client; public LemonSqueezyController(LemonSqueezyClient client) { this.client = client; } @GetMapping("/variants") public LemonSqueezyResponse listVariants() throws Exception { return client.getList(ApiEndpoint.VARIANTS); } @GetMapping("/variants/{id}") public VariantResponse getVariant(@PathVariable Long id) throws Exception { return client.getItem(ApiEndpoint.VARIANTS, id, VariantResponse.class); } } ``` -------------------------------- ### Access License Key Details in Java Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Extract and display license key information, including its status, activation limits, and associated order details. This is useful when processing license key creation or update webhooks. ```java import be.codewriter.lemonsqueezy.license.*; // Cast from webhook data when EventType is SUBSCRIPTION_LICENSE_KEY_CREATED LicenseKey licenseKey = (LicenseKey) webhookMessage.getData(); LicenseKeyAttributes attr = licenseKey.getAttributes(); System.out.println("Key: " + attr.getKey()); // "7A5472DB-C5B9-459E-..." System.out.println("Short key: " + attr.getKeyShort()); // "XXXX-FAE467F0B28E" System.out.println("Status: " + attr.getStatusFormatted()); // "Inactive" System.out.println("Disabled: " + attr.getDisabled()); // false System.out.println("Activation limit: " + attr.getActivationLimit()); // 1 System.out.println("Instances: " + attr.getInstancesCount()); // null System.out.println("Expires at: " + attr.getExpiresAt()); // null (never expires) System.out.println("Customer: " + attr.getUserName() + " <" + attr.getUserEmail() + ">"); System.out.println("Order ID: " + attr.getOrderId()); // 2925880 System.out.println("Product ID: " + attr.getProductId()); // 278897 ``` -------------------------------- ### Spring Boot Auto-Configuration Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Details on how LemonSqueezyAutoConfigurer enables automatic creation of a LemonSqueezyClient bean in Spring Boot applications. ```APIDOC ## Spring Boot Auto-Configuration `LemonSqueezyAutoConfigurer` creates a `LemonSqueezyClient` bean automatically when `lemonsqueezy.api.api-key` is set in application properties. The bean is conditional on no existing `LemonSqueezyClient` bean, allowing manual override. ```properties # application.properties lemonsqueezy.api.api-key=YOUR_API_KEY ``` ```java // Spring Boot controller using the auto-configured bean import be.codewriter.lemonsqueezy.configuration.LemonSqueezyClient; import be.codewriter.lemonsqueezy.generic.ApiEndpoint; import be.codewriter.lemonsqueezy.response.LemonSqueezyResponse; import be.codewriter.lemonsqueezy.variant.Variant; import be.codewriter.lemonsqueezy.variant.VariantResponse; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/lemonsqueezy") public class LemonSqueezyController { private final LemonSqueezyClient client; public LemonSqueezyController(LemonSqueezyClient client) { this.client = client; } @GetMapping("/variants") public LemonSqueezyResponse listVariants() throws Exception { return client.getList(ApiEndpoint.VARIANTS); } @GetMapping("/variants/{id}") public VariantResponse getVariant(@PathVariable Long id) throws Exception { return client.getItem(ApiEndpoint.VARIANTS, id, VariantResponse.class); } } ``` ``` -------------------------------- ### Validate JReleaser Configuration Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/MAVEN.md Run the JReleaser Maven plugin to validate your project's release configuration. This command checks for necessary configurations like signing keys, passphrases, and API tokens, providing specific error messages if any are missing. ```shell $ mvn jreleaser:config [INFO] Scanning for projects... Downloading from central: https://repo.maven.apache.org/maven2/org/jreleaser/jreleaser-maven-plugin/1.13.1/jreleaser-maven-plugin-1.13.1.pom Downloaded from central: https://repo.maven.apache.org/maven2/org/jreleaser/jreleaser-maven-plugin/1.13.1/jreleaser-maven-plugin-1.13.1.pom (3.2 kB at 8.4 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/org/jreleaser/jreleaser-maven-plugin/1.13.1/jreleaser-maven-plugin-1.13.1.jar Downloaded from central: https://repo.maven.apache.org/maven2/org/jreleaser/jreleaser-maven-plugin/1.13.1/jreleaser-maven-plugin-1.13.1.jar (76 kB at 614 kB/s) [INFO] [INFO] ------------------< be.codewriter:lemonsqueezy-java >------------------- [INFO] Building lemonsqueezy-java 0.0.1-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- # The following errors are expected, as the keys are configured as GitLab Action Secrets [ERROR] == JReleaser == [ERROR] signing.passphrase must not be blank. Configure a value using the Maven DSL, or define a System property jreleaser.gpg.passphrase, or define a JRELEASER_GPG_PASSPHRASE environment variable, or define a key/value pair in /Users/frank/.jreleaser/config.properties with a key named JRELEASER_GPG_PASSPHRASE [ERROR] signing.publicKey must not be blank. Configure a value using the Maven DSL, or define a System property jreleaser.gpg.public.key, or define a JRELEASER_GPG_PUBLIC_KEY environment variable, or define a key/value pair in /Users/frank/.jreleaser/config.properties with a key named JRELEASER_GPG_PUBLIC_KEY [ERROR] signing.secretKey must not be blank. Configure a value using the Maven DSL, or define a System property jreleaser.gpg.secret.key, or define a JRELEASER_GPG_SECRET_KEY environment variable, or define a key/value pair in /Users/frank/.jreleaser/config.properties with a key named JRELEASER_GPG_SECRET_KEY [ERROR] release.github.token must not be blank. Configure a value using the Maven DSL, or define a System property jreleaser.github.token, or define a JRELEASER_GITHUB_TOKEN environment variable, or define a key/value pair in /Users/frank/.jreleaser/config.properties with a key named JRELEASER_GITHUB_TOKEN ``` -------------------------------- ### Access Subscription Details in Java Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Extract and display subscription lifecycle information, including status, billing dates, and customer details. This code is typically used when handling subscription webhooks. ```java import be.codewriter.lemonsqueezy.subscription.*; // Received via webhook (subscription_created / subscription_updated) Subscription sub = (Subscription) webhookMessage.getData(); SubscriptionAttributes attr = sub.getAttributes(); System.out.println("Product: " + attr.getProductName()); // "MelodyMatrix (1 year subscription)" System.out.println("Variant: " + attr.getVariantName()); // "Default" System.out.println("Status: " + attr.getStatusFormatted()); // "Active" System.out.println("Customer: " + attr.getUserEmail()); // "test@company.com" System.out.println("Card: " + attr.getCardBrand() + " ****" + attr.getCardLastFour()); // "visa ****4242" System.out.println("Cancelled: " + attr.getCancelled()); // false System.out.println("Renews at: " + attr.getRenewsAt()); // 2025-06-19T14:08:09 System.out.println("Trial ends: " + attr.getTrialEndsAt()); // null (no trial) System.out.println("Billing anchor (day): " + attr.getBillingAnchor()); // 19 // First subscription item SubscriptionItem item = attr.getFirstSubscriptionItem(); System.out.println("Item ID: " + item.getId()); // 355942 System.out.println("Price ID: " + item.getPriceId()); // 571912 System.out.println("Quantity: " + item.getQuantity()); // 1 System.out.println("Usage based: " + item.getUsageBased()); // false // Management URLs System.out.println("Update payment: " + attr.getUrls().getUpdatePaymentMethod()); System.out.println("Customer portal: " + attr.getUrls().getCustomerPortal()); System.out.println("Update sub: " + attr.getUrls().getCustomerPortalUpdateSubscription()); ``` -------------------------------- ### Spring Boot Auto-Configuration for LemonSqueezyClient Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Configure the API key in application properties to automatically create a LemonSqueezyClient bean. This bean can then be injected into Spring components. ```properties # application.properties lemonsqueezy.api.api-key=YOUR_API_KEY ``` -------------------------------- ### Handle Lemon Squeezy Webhooks in Spring Boot Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Use this controller to receive and process incoming webhook POST payloads from Lemon Squeezy. Ensure webhook signature verification is implemented before processing payload data. ```java import be.codewriter.lemonsqueezy.webhook.*; import be.codewriter.lemonsqueezy.order.*; import be.codewriter.lemonsqueezy.subscription.*; import be.codewriter.lemonsqueezy.license.*; // Spring Boot REST controller for receiving Lemon Squeezy webhooks @RestController @RequestMapping("/api/webhook") public class WebhookController { private final ObjectMapper objectMapper; public WebhookController() { objectMapper = new ObjectMapper(); JavaTimeModule module = new JavaTimeModule(); module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer( DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"))); objectMapper.registerModule(module); } @PostMapping public ResponseEntity handleWebhook( @RequestBody String payload, @RequestHeader("X-Signature") String signature) throws Exception { // 1. Verify X-Signature header using HMAC-SHA256 of payload with your webhook secret // (verification logic omitted — always verify before processing) // 2. Parse the webhook payload WebhookMessage message = objectMapper.readValue(payload, WebhookMessage.class); EventType eventType = message.getMeta().getType(); System.out.println("Event: " + eventType); // e.g. ORDER_CREATED System.out.println("Event ID: " + message.getMeta().getId()); // UUID // 3. Dispatch on event type — data is auto-cast to the correct subtype switch (eventType) { case ORDER_CREATED -> { OrderAttributes order = ((Order) message.getData()).getAttributes(); System.out.println("New order: " + order.getTotalFormatted() + " from " + order.getUserEmail()); } case SUBSCRIPTION_CREATED, SUBSCRIPTION_UPDATED -> { SubscriptionAttributes sub = ((Subscription) message.getData()).getAttributes(); System.out.println("Subscription " + eventType + " | status: " + sub.getStatusFormatted() + " | renews: " + sub.getRenewsAt()); } case SUBSCRIPTION_PAYMENT_SUCCESS -> { SubscriptionInvoiceAttributes inv = ((SubscriptionInvoice) message.getData()).getAttributes(); System.out.println("Payment received: " + inv.getTotalformatted() + " | invoice: " + inv.getUrls().getInvoiceUrl()); } case SUBSCRIPTION_LICENSE_KEY_CREATED -> { LicenseKeyAttributes lk = ((LicenseKey) message.getData()).getAttributes(); System.out.println("License issued: " + lk.getKeyShort() + " to " + lk.getUserEmail()); } case UNDEFINED -> System.out.println("Unknown event type received"); } return ResponseEntity.ok().build(); } } // All EventType values: // ORDER_CREATED, ORDER_REFUNDED // SUBSCRIPTION_CREATED, SUBSCRIPTION_UPDATED, SUBSCRIPTION_CANCELLED // SUBSCRIPTION_RESUMED, SUBSCRIPTION_EXPIRED, SUBSCRIPTION_PAUSED, SUBSCRIPTION_UNPAUSED // SUBSCRIPTION_PAYMENT_SUCCESS, SUBSCRIPTION_PAYMENT_FAILED // SUBSCRIPTION_PAYMENT_RECOVERED, SUBSCRIPTION_PAYMENT_REFUNDED // SUBSCRIPTION_LICENSE_KEY_CREATED, SUBSCRIPTION_LICENSE_KEY_UPDATED // UNDEFINED (fallback for unknown event strings) ``` -------------------------------- ### Parse License Key Data Object Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Parses a JSON string into a LicenseKey object and retrieves its attributes. ```java LicenseKey licenseKey = objectMapper.readValue(licenseKeyJson, LicenseKey.class); LicenseKeyAttributes attr = licenseKey.getAttributes(); ``` -------------------------------- ### Access Order Details in Java Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Retrieve and print various details from an Order object, typically received from a webhook. Ensure the Order object is correctly cast from the webhook message data. ```java import be.codewriter.lemonsqueezy.order.*; // Typically received as part of a webhook payload Order order = (Order) webhookMessage.getData(); OrderAttributes attr = order.getAttributes(); System.out.println("Order #" + attr.getOrderNumber()); // Order #912571 System.out.println("Identifier: " + attr.getIdentifier()); // UUID System.out.println("Customer: " + attr.getUserName() + " <" + attr.getUserEmail() + ">"); System.out.println("Currency: " + attr.getCurrency()); // "EUR" System.out.println("Subtotal: " + attr.getSubTotalFormatted()); // "€19.99" System.out.println("Tax: " + attr.getTaxFormatted()); // "€0.00" System.out.println("Total: " + attr.getTotalFormatted()); // "€19.99" System.out.println("Refunded: " + attr.getRefunded()); // false System.out.println("Status: " + attr.getStatusFormatted()); // "Paid" // First order item OrderItem item = attr.getFirstOrderItem(); System.out.println("Product: " + item.getProductName()); // "MelodyMatrix (1 year subscription)" System.out.println("Product ID: " + item.getProductId()); // 278897 // Receipt URL System.out.println("Receipt: " + attr.getUrls().getReceipt()); ``` -------------------------------- ### Parse Store Object from JSON Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Demonstrates parsing a JSON string representing a store response into Java objects using Jackson. Accesses store attributes and relationship links. ```java import be.codewriter.lemonsqueezy.store.*; // Parse a single store response from JSON String json = """ { "jsonapi": { "version": "1.0" }, "links": { "self": "https://api.lemonsqueezy.com/v1/stores/1" }, "data": { "type": "stores", "id": "1", "attributes": { "name": "My Store", "slug": "my-store", "domain": "my-store.lemonsqueezy.com", "url": "https://my-store.lemonsqueezy.com", "plan": "fresh", "country": "US", "country_nicename": "United States", "currency": "USD", "total_sales": 1, "total_revenue": 999, "thirty_day_sales": 0, "thirty_day_revenue": 0, "created_at": "2021-05-24T14:15:06.000000Z", "updated_at": "2021-06-15T10:03:14.000000Z" } } } """; StoreResponse response = objectMapper.readValue(json, StoreResponse.class); Store store = response.getStore(); StoreAttributes attr = store.getAttributes(); System.out.println("ID: " + store.getId()); // "1" System.out.println("Name: " + attr.getName()); // "My Store" System.out.println("Domain: " + attr.getDomain()); // "my-store.lemonsqueezy.com" System.out.println("Currency: " + attr.getCurrency()); // "USD" System.out.println("Country: " + attr.getCountryNicename()); // "United States" System.out.println("Total revenue: " + attr.getTotalRevenue()); // 999.0 System.out.println("30-day sales: " + attr.getThirtyDaySales()); // 0.0 System.out.println("Created: " + attr.getCreatedAt()); // 2021-05-24T14:15:06 // Relationship links System.out.println(store.getRelationships().getProducts().getLinks().getRelated()); // → https://api.lemonsqueezy.com/v1/stores/1/products ``` -------------------------------- ### Configure Jackson ObjectMapper for JavaTimeModule Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Register JavaTimeModule to correctly deserialize ISO-8601 timestamps with microsecond precision used by the Lemon Squeezy API. ```java import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; // Standard setup — used in all examples below ObjectMapper objectMapper = new ObjectMapper(); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addDeserializer( LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'")) ); objectMapper.registerModule(javaTimeModule); ``` -------------------------------- ### Parse User Data Object and Assert Attributes Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Parses a JSON string into a User object, retrieves its attributes, and asserts specific values for name and email. ```java User user = objectMapper.readValue(userJson, User.class); UserAttributes attr = user.getAttributes(); assertAll( () -> assertEquals("Darlene Daugherty", attr.getName(), "User attributes name should be equal"), () -> assertEquals("gernser@yahoo.com", attr.getEmail(), "User attributes email should be equal"), ... ); ``` -------------------------------- ### Parse Customer Object from JSON Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Parses a JSON string representing a customer into a Customer object and prints key attributes. Ensure the `objectMapper` is properly configured. ```java import be.codewriter.lemonsqueezy.customer.*; String json = /* JSON from Lemon Squeezy API or webhook */ "..."; Customer customer = objectMapper.readValue(json, Customer.class); CustomerAttributes attr = customer.getAttributes(); System.out.println("Name: " + attr.getName()); // "Luke Skywalker" System.out.println("Email: " + attr.getEmail()); // "luke@example.com" System.out.println("Status: " + attr.getStatus()); // "subscribed" System.out.println("City: " + attr.getCity()); // "New York" System.out.println("Country: " + attr.getCountryFormatted()); // "United States" System.out.println("MRR: " + attr.getMonthlyRecurringRevenueFormatted()); // "$29.00" System.out.println("Revenue: " + attr.getTotalRevenueCurrencyFormatted()); // "$348.00" // Portal URL (may be null if not subscribed) System.out.println("Portal: " + attr.getUrls().getCheckoutPortal()); ``` -------------------------------- ### Parse Paginated List Responses with LemonSqueezyResponse Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Use `LemonSqueezyResponse` to handle generic paginated list responses from API endpoints. This wrapper includes pagination metadata, API version, links, and the list of data items. ```java import be.codewriter.lemonsqueezy.response.LemonSqueezyResponse; import be.codewriter.lemonsqueezy.variant.Variant; LemonSqueezyClient client = new LemonSqueezyClient("your-api-key"); LemonSqueezyResponse page = client.getList(ApiEndpoint.VARIANTS); System.out.println("JSON:API version: " + page.getJsonapi().getVersion()); // "1.0" System.out.println("Self link: " + page.getLinks().getSelf()); System.out.println("Total items: " + page.getData().size()); // Iterate items for (Variant v : page.getData()) { System.out.println("Variant #" + v.getId() + " | type=" + v.getType() + " | storeId=" + v.getAttributes().getStoreId()); } ``` -------------------------------- ### Maven Dependency for lemonsqueezy-java Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Add this dependency to your Maven pom.xml to include the lemonsqueezy-java SDK. ```xml be.codewriter lemonsqueezy-java 1.0.1 ``` -------------------------------- ### Accessing Base Attributes from Order Resource Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Demonstrates how to access common fields like store ID, test mode, creation, and update timestamps from an Order resource's attributes after parsing a webhook message. Ensure the `ObjectMapper` is configured with `JavaTimeModule` for proper `LocalDateTime` deserialization. ```java import be.codewriter.lemonsqueezy.BaseAttributes; import be.codewriter.lemonsqueezy.order.Order; import be.codewriter.lemonsqueezy.order.OrderAttributes; // After parsing any resource, BaseAttributes fields are accessible OrderAttributes attr = ((Order) webhookMessage.getData()).getAttributes(); System.out.println("Store ID: " + attr.getStoreId()); // 91257 System.out.println("Test mode: " + attr.getTestMode()); // true (sandbox) or false (live) System.out.println("Created at: " + attr.getCreatedAt()); // LocalDateTime System.out.println("Updated at: " + attr.getUpdatedAt()); // LocalDateTime ``` -------------------------------- ### Add Lemonsqueezy-java Dependency to Maven Project Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Specifies the Maven dependency required to include the lemonsqueezy-java library in your project. ```xml be.codewriter lemonsqueezy-java ${lemonsqueezy.version} ``` -------------------------------- ### Parse Subscription Data Object Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Parses a JSON string into a Subscription object and retrieves its attributes. ```java Subscription subscription = objectMapper.readValue(subscriptionJson, Subscription.class); SubscriptionAttributes attr = subscription.getAttributes(); ``` -------------------------------- ### Parse Customer Data Object Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Parses a JSON string into a Customer object and retrieves its attributes. ```java Customer user = objectMapper.readValue(customerJson, Customer.class); CustomerAttributes attr = user.getAttributes(); ``` -------------------------------- ### Configure Jackson ObjectMapper for JavaTimeModule Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md When parsing JSON with datetime formats, ensure the JavaTimeModule is registered with the ObjectMapper. ```java ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); ``` -------------------------------- ### Parse User Object from JSON Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt Parses a JSON string representing the authenticated user into a User object and prints user attributes. This is useful for retrieving store owner or team member details. ```java import be.codewriter.lemonsqueezy.user.*; // Using LemonSqueezyClient — no dedicated ApiEndpoint for users yet; // parse from raw JSON or webhook data String json = /* user JSON */ "..."; User user = objectMapper.readValue(json, User.class); UserAttributes attr = user.getAttributes(); System.out.println("Name: " + attr.getName()); // "Darlene Daugherty" System.out.println("Email: " + attr.getEmail()); // "gernser@yahoo.com" System.out.println("Color: " + attr.getColor()); // "#898FA9" System.out.println("Custom avatar: " + attr.getHasCustomAvatar()); // false System.out.println("Avatar URL: " + attr.getAvatarUrl()); ``` -------------------------------- ### Parse Checkout Data Object Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Parses a JSON string into a Checkout object and retrieves its attributes. ```java Checkout checkout = objectMapper.readValue(checkoutJson, Checkout.class); CheckoutAttributes attr = checkout.getAttributes(); ``` -------------------------------- ### ApiEndpoint Enum Source: https://context7.com/codewriterbv/lemonsqueezy-java/llms.txt The ApiEndpoint enum provides a convenient way to access the full URL for each supported REST resource in the Lemon Squeezy API. It can be used with both ApiRequest and LemonSqueezyClient. ```APIDOC ## ApiEndpoint Enum `ApiEndpoint` enumerates every supported REST resource and produces its full URL via `getApiEndpoint()`. ```java import be.codewriter.lemonsqueezy.generic.ApiEndpoint; // Prints: https://api.lemonsqueezy.com/v1/stores System.out.println(ApiEndpoint.STORES.getApiEndpoint()); // All available values: // ApiEndpoint.STORES → https://api.lemonsqueezy.com/v1/stores // ApiEndpoint.PRODUCTS → https://api.lemonsqueezy.com/v1/products // ApiEndpoint.VARIANTS → https://api.lemonsqueezy.com/v1/variants // ApiEndpoint.ORDERS → https://api.lemonsqueezy.com/v1/orders // ApiEndpoint.SUBSCRIPTIONS→ https://api.lemonsqueezy.com/v1/subscriptions ``` ``` -------------------------------- ### Parse Store Data Object Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Parses a JSON string into a Store object and retrieves its attributes. ```java Store store = objectMapper.readValue(storeJson, Store.class); StoreAttributes attr = store.getAttributes(); ``` -------------------------------- ### Parse Order Data Object Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Parses a JSON string into an Order object and retrieves its attributes. ```java Order order = objectMapper.readValue(orderJson, Order.class); OrderAttributes attr = order.getAttributes(); ``` -------------------------------- ### Parse Variant Data Object Source: https://github.com/codewriterbv/lemonsqueezy-java/blob/main/README.md Parses a JSON string into a Variant object and retrieves its attributes. ```java Variant variant = objectMapper.readValue(variantJson, Variant.class); VariantAttributes attr = variant.getAttributes(); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.