From ee38849f1beb900cb1791396d99d387cb558e85d Mon Sep 17 00:00:00 2001 From: ItsNature Date: Mon, 17 Mar 2025 18:52:12 +0100 Subject: [PATCH 1/5] Prepare tebex module for release --- .../tebex/TebexEmbeddedCheckoutSupport.java | 9 +------- .../example/api/examples/TebexApiExample.java | 22 +++++++++++++------ .../ApolloPacketReceiveProtoListener.java | 2 ++ .../player/ApolloPlayerManagerImpl.java | 2 +- docs/developers/modules/_meta.json | 1 + docs/developers/modules/tebex.mdx | 0 6 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 docs/developers/modules/tebex.mdx diff --git a/api/src/main/java/com/lunarclient/apollo/module/tebex/TebexEmbeddedCheckoutSupport.java b/api/src/main/java/com/lunarclient/apollo/module/tebex/TebexEmbeddedCheckoutSupport.java index 7b8ab72e..f89fef7a 100644 --- a/api/src/main/java/com/lunarclient/apollo/module/tebex/TebexEmbeddedCheckoutSupport.java +++ b/api/src/main/java/com/lunarclient/apollo/module/tebex/TebexEmbeddedCheckoutSupport.java @@ -42,13 +42,6 @@ public enum TebexEmbeddedCheckoutSupport { * * @since 1.1.6 */ - WINDOW, - - /** - * The checkout is not supported. - * - * @since 1.1.6 - */ - UNSUPPORTED + WINDOW } diff --git a/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java b/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java index 36829578..605779c6 100644 --- a/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java +++ b/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java @@ -28,8 +28,8 @@ import com.lunarclient.apollo.module.tebex.TebexEmbeddedCheckoutSupport; import com.lunarclient.apollo.module.tebex.TebexModule; import com.lunarclient.apollo.player.ApolloPlayer; -import java.util.Optional; import org.bukkit.entity.Player; +import java.util.Optional; public class TebexApiExample extends TebexExample { @@ -38,13 +38,21 @@ public class TebexApiExample extends TebexExample { @Override public void displayTebexEmbeddedCheckoutExample(Player viewer, String basketIdent, String locale) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - apolloPlayerOpt.ifPresent(apolloPlayer -> { - if (apolloPlayer.getTebexEmbeddedCheckoutSupport() == TebexEmbeddedCheckoutSupport.UNSUPPORTED) { - return; - } - this.tebexModule.displayTebexEmbeddedCheckout(apolloPlayer, basketIdent, locale); - }); + if (apolloPlayerOpt.isPresent()) { + ApolloPlayer apolloPlayer = apolloPlayerOpt.get(); + TebexEmbeddedCheckoutSupport embeddedCheckoutSupport = apolloPlayer.getTebexEmbeddedCheckoutSupport(); + + this.tebexModule.displayTebexEmbeddedCheckout(apolloPlayerOpt.get(), basketIdent, locale); + + if (embeddedCheckoutSupport == TebexEmbeddedCheckoutSupport.OVERLAY) { + viewer.sendMessage("Opening checkout as game overlay!"); + } else { + viewer.sendMessage("Opening checkout in an external window!"); + } + } else { + viewer.sendMessage("Complete your purchase at https://pay.tebex.io/" + basketIdent); + } } } diff --git a/bukkit-example/src/main/java/com/lunarclient/apollo/example/proto/listeners/ApolloPacketReceiveProtoListener.java b/bukkit-example/src/main/java/com/lunarclient/apollo/example/proto/listeners/ApolloPacketReceiveProtoListener.java index 37c22bd9..214cd6fc 100644 --- a/bukkit-example/src/main/java/com/lunarclient/apollo/example/proto/listeners/ApolloPacketReceiveProtoListener.java +++ b/bukkit-example/src/main/java/com/lunarclient/apollo/example/proto/listeners/ApolloPacketReceiveProtoListener.java @@ -34,6 +34,7 @@ import com.lunarclient.apollo.packetenrichment.v1.PlayerChatOpenMessage; import com.lunarclient.apollo.packetenrichment.v1.PlayerInfo; import com.lunarclient.apollo.packetenrichment.v1.PlayerUseItemMessage; +import com.lunarclient.apollo.player.v1.EmbeddedCheckoutSupport; import com.lunarclient.apollo.player.v1.ModMessage; import com.lunarclient.apollo.player.v1.PlayerHandshakeMessage; import java.util.List; @@ -76,6 +77,7 @@ private void onPlayerHandshake(PlayerHandshakeMessage message) { MinecraftVersion minecraftVersion = message.getMinecraftVersion(); LunarClientVersion lunarClientVersion = message.getLunarClientVersion(); + EmbeddedCheckoutSupport checkoutSupport = message.getEmbeddedCheckoutSupport(); String gitBranch = lunarClientVersion.getGitBranch(); String gitCommit = lunarClientVersion.getGitCommit(); String semVer = lunarClientVersion.getSemver(); diff --git a/common/src/main/java/com/lunarclient/apollo/player/ApolloPlayerManagerImpl.java b/common/src/main/java/com/lunarclient/apollo/player/ApolloPlayerManagerImpl.java index ddb1f6ae..8b02317a 100644 --- a/common/src/main/java/com/lunarclient/apollo/player/ApolloPlayerManagerImpl.java +++ b/common/src/main/java/com/lunarclient/apollo/player/ApolloPlayerManagerImpl.java @@ -141,7 +141,7 @@ public void handlePlayerHandshake(@NotNull ApolloPlayer player, @NotNull PlayerH try { checkoutSupportType = TebexEmbeddedCheckoutSupport.values()[message.getEmbeddedCheckoutSupportValue() - 1]; } catch (ArrayIndexOutOfBoundsException e) { - checkoutSupportType = TebexEmbeddedCheckoutSupport.UNSUPPORTED; + checkoutSupportType = null; } EventBus.EventResult result = EventBus.getBus() diff --git a/docs/developers/modules/_meta.json b/docs/developers/modules/_meta.json index d345ddf9..f08c7659 100644 --- a/docs/developers/modules/_meta.json +++ b/docs/developers/modules/_meta.json @@ -21,6 +21,7 @@ "staffmod": "Staff Mod", "stopwatch": "Stopwatch", "team": "Team", + "tebex": "Tebex", "title": "Title", "tntcountdown": "TNT Countdown", "transfer": "Transfer", diff --git a/docs/developers/modules/tebex.mdx b/docs/developers/modules/tebex.mdx new file mode 100644 index 00000000..e69de29b From eead71dc7f9b5102d32b7788bdcb9af7d360da4f Mon Sep 17 00:00:00 2001 From: ItsNature Date: Tue, 25 Mar 2025 21:33:38 +0100 Subject: [PATCH 2/5] Add Tebex Module documentation --- docs/developers/modules/tebex.mdx | 152 ++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/docs/developers/modules/tebex.mdx b/docs/developers/modules/tebex.mdx index e69de29b..a544c5f2 100644 --- a/docs/developers/modules/tebex.mdx +++ b/docs/developers/modules/tebex.mdx @@ -0,0 +1,152 @@ +import { Tab, Tabs } from 'nextra-theme-docs' +import { Callout } from 'nextra-theme-docs' + +# Tebex Module + +## Overview + +The Tebex module allows Lunar Client users to complete purchases directly within the game, providing a faster, more seamless checkout experience. + +**Overlay Mode** + +This mode displays the Tebex checkout flow as an overlay on the Minecraft window, similar to opening an inventory. It provides the most seamless experience and is the preferred method. Overlay mode is only available on **Windows**. + +![Tebex Overlay Example](https://i.imgur.com/1grfRaX.png#center) + +**Window Mode** + +As a fallback, and for **macOS** and **Linux** users, a separate window opens to display the Tebex checkout flow. While not as seamless as overlay mode, this ensures compatibility across all operating systems. + +![Tebex Window Example](https://i.imgur.com/68wBAYZ.png#center) + +## Usage Guidelines + +To ensure a smooth user experience, server must only open checkout windows from user-initiated actions. Examples include, but aren't limited to: + +**Allowed (User-Initiated Actions):** +- Clicking a link in chat +- Running a command +- Clicking a button in a GUI + +**Not Allowed (Automated/Intrusive Actions):** +- Triggering on login +- Automatically opening at set intervals (e.g., every 30 minutes) +- Automatically opening during flash sales, events, or other promotions without user interaction + +This feature is designed to enhance the user experience by providing a seamless checkout process, misuse of this module, such as creating a disruptive or intrusive purchase flow, will result in restricted access in the future. + +## Integration + +The only piece of information sent from the server to the client to trigger a checkout window is the **Tebex basket ident**. This unique identifier represents an in-progress basket and later converts into a **Tebex transaction ID (tbx-...)** upon purchase. + +Optionally, a **locale** value can also be sent to specify the language and regional formatting used when rendering the checkout window (e.g., `en-US`). If no locale is provided, the default locale settings will be used. + +If a player is not using Lunar Client, the same Tebex basket can be used on the web at: `https://pay.tebex.io/` + +**Example flow** +1. Create a basket using the Tebex Headless API. +2. Add package(s) to basket with Tebex Headless API +3. If the player is using Lunar Client: Send an Apollo packet to open the checkout modal. +4. If the player is not using Lunar Client: Send a chat message with a Tebex payment link. + +**Tebex Headless API** + +Basket idents are created via the [Tebex Headless API](https://docs.tebex.io/developers/headless-api/overview). This API allows programmatic basket creation, package additions, coupon applications, and more. The [Lunar Client Store](https://store.lunarclient.com/) is built on this API, and it is enabled by default for all Tebex stores. + +**Getting started with the Tebex Headless API** + +🔗 [Documentation](https://docs.tebex.io/developers/headless-api/overview)
+🔗 [Java SDK](https://github.com/tebexio/TebexHeadless-OpenAPI/tree/main/sdks/java)
+ +Most implementations of Embedded Checkout only require two API endpoints: +1. Creating a basket +2. Adding packages to a basket + +For simplicity, you may opt to handle these requests manually rather than using the SDK. + +**API Authentication** + +You’ll need your **public token** and **private key** from: [Tebex API Keys](https://creator.tebex.io/developers/api-keys) + + + Never share these credentials with Lunar Client or external servers. They should only be used while communicating with Tebex Headless. + + +### Sample Code +Explore each integration by cycling through each tab, to find the best fit for your requirements and needs. + + + + + +**Display Tebex Embedded Checkout** + +```java +@Override +public void displayTebexEmbeddedCheckoutExample(Player viewer, String basketIdent, String locale) { + Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); + + if (apolloPlayerOpt.isPresent()) { + ApolloPlayer apolloPlayer = apolloPlayerOpt.get(); + TebexEmbeddedCheckoutSupport embeddedCheckoutSupport = apolloPlayer.getTebexEmbeddedCheckoutSupport(); + + this.tebexModule.displayTebexEmbeddedCheckout(apolloPlayerOpt.get(), basketIdent, locale); + + if (embeddedCheckoutSupport == TebexEmbeddedCheckoutSupport.OVERLAY) { + viewer.sendMessage("Opening checkout as game overlay!"); + } else { + viewer.sendMessage("Opening checkout in an external window!"); + } + } else { + viewer.sendMessage("Complete your purchase at https://pay.tebex.io/" + basketIdent); + } +} +``` + + + + + +**Display Tebex Embedded Checkout** + + + To detect embedded checkout support type visit [Apollo Serverbound packets](/apollo/developers/lightweight/protobuf/serverbound-packets) + + +```java +public void displayTebexEmbeddedCheckoutExample(Player viewer, String basketIdent, String locale) { + OpenTebexEmbeddedCheckoutMessage.Builder builder = OpenTebexEmbeddedCheckoutMessage.newBuilder() + .setBasketIdent(basketIdent); + + if (locale != null) { + builder.setLocale(locale); + } + + OpenTebexEmbeddedCheckoutMessage message = builder.build(); + ProtobufPacketUtil.sendPacket(viewer, message); +} +``` + + + + + +**Display Tebex Embedded Checkout** + +```java +public void displayTebexEmbeddedCheckoutExample(Player viewer, String basketIdent, String locale) { + JsonObject message = new JsonObject(); + message.addProperty("@type", "type.googleapis.com/lunarclient.apollo.tebex.v1.OpenTebexEmbeddedCheckoutMessage"); + message.addProperty("basket_ident", basketIdent); + + if (locale != null) { + message.addProperty("locale", locale); + } + + JsonPacketUtil.sendPacket(viewer, message); +} +``` + + + + From e5261ac706b624e197e2b5f6179beb9b463ba15d Mon Sep 17 00:00:00 2001 From: ItsNature Date: Tue, 25 Mar 2025 21:41:03 +0100 Subject: [PATCH 3/5] Fix import order --- .../apollo/example/api/examples/TebexApiExample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java b/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java index 605779c6..77dae88e 100644 --- a/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java +++ b/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java @@ -28,8 +28,8 @@ import com.lunarclient.apollo.module.tebex.TebexEmbeddedCheckoutSupport; import com.lunarclient.apollo.module.tebex.TebexModule; import com.lunarclient.apollo.player.ApolloPlayer; -import org.bukkit.entity.Player; import java.util.Optional; +import org.bukkit.entity.Player; public class TebexApiExample extends TebexExample { From 835906f71b8aaaadc0f7087ea09d6685db0c1c7e Mon Sep 17 00:00:00 2001 From: Trentin <25537885+TrentinTheKid@users.noreply.github.com> Date: Tue, 25 Mar 2025 16:54:37 -0400 Subject: [PATCH 4/5] minor changes --- docs/developers/modules/tebex.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/developers/modules/tebex.mdx b/docs/developers/modules/tebex.mdx index a544c5f2..642b6f8e 100644 --- a/docs/developers/modules/tebex.mdx +++ b/docs/developers/modules/tebex.mdx @@ -21,7 +21,7 @@ As a fallback, and for **macOS** and **Linux** users, a separate window opens to ## Usage Guidelines -To ensure a smooth user experience, server must only open checkout windows from user-initiated actions. Examples include, but aren't limited to: +To ensure a smooth user experience, servers must only open checkout windows from user-initiated actions. Examples include, but aren't limited to: **Allowed (User-Initiated Actions):** - Clicking a link in chat @@ -73,7 +73,7 @@ You’ll need your **public token** and **private key** from: [Tebex API Keys](h ### Sample Code -Explore each integration by cycling through each tab, to find the best fit for your requirements and needs. +Explore each integration by cycling through each tab to find the best fit for your requirements and needs. From 79c92e4d6490518f8b1c65085e92f39323507af1 Mon Sep 17 00:00:00 2001 From: ItsNature Date: Tue, 25 Mar 2025 23:36:56 +0100 Subject: [PATCH 5/5] Add TebexEmbeddedCheckoutSupport#UNSUPPORTED --- .../tebex/TebexEmbeddedCheckoutSupport.java | 9 ++++++- .../example/api/examples/TebexApiExample.java | 26 +++++++++++------- .../player/ApolloPlayerManagerImpl.java | 2 +- docs/developers/modules/tebex.mdx | 27 +++++++++++-------- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/api/src/main/java/com/lunarclient/apollo/module/tebex/TebexEmbeddedCheckoutSupport.java b/api/src/main/java/com/lunarclient/apollo/module/tebex/TebexEmbeddedCheckoutSupport.java index f89fef7a..7b8ab72e 100644 --- a/api/src/main/java/com/lunarclient/apollo/module/tebex/TebexEmbeddedCheckoutSupport.java +++ b/api/src/main/java/com/lunarclient/apollo/module/tebex/TebexEmbeddedCheckoutSupport.java @@ -42,6 +42,13 @@ public enum TebexEmbeddedCheckoutSupport { * * @since 1.1.6 */ - WINDOW + WINDOW, + + /** + * The checkout is not supported. + * + * @since 1.1.6 + */ + UNSUPPORTED } diff --git a/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java b/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java index 77dae88e..c318a3e1 100644 --- a/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java +++ b/bukkit-example/src/main/java/com/lunarclient/apollo/example/api/examples/TebexApiExample.java @@ -39,19 +39,25 @@ public class TebexApiExample extends TebexExample { public void displayTebexEmbeddedCheckoutExample(Player viewer, String basketIdent, String locale) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - if (apolloPlayerOpt.isPresent()) { - ApolloPlayer apolloPlayer = apolloPlayerOpt.get(); - TebexEmbeddedCheckoutSupport embeddedCheckoutSupport = apolloPlayer.getTebexEmbeddedCheckoutSupport(); + if (!apolloPlayerOpt.isPresent()) { + viewer.sendMessage("Complete your purchase at https://pay.tebex.io/" + basketIdent); + return; + } - this.tebexModule.displayTebexEmbeddedCheckout(apolloPlayerOpt.get(), basketIdent, locale); + ApolloPlayer apolloPlayer = apolloPlayerOpt.get(); + TebexEmbeddedCheckoutSupport embeddedCheckoutSupport = apolloPlayer.getTebexEmbeddedCheckoutSupport(); - if (embeddedCheckoutSupport == TebexEmbeddedCheckoutSupport.OVERLAY) { - viewer.sendMessage("Opening checkout as game overlay!"); - } else { - viewer.sendMessage("Opening checkout in an external window!"); - } - } else { + if (embeddedCheckoutSupport == TebexEmbeddedCheckoutSupport.UNSUPPORTED) { viewer.sendMessage("Complete your purchase at https://pay.tebex.io/" + basketIdent); + return; + } + + this.tebexModule.displayTebexEmbeddedCheckout(apolloPlayerOpt.get(), basketIdent, locale); + + if (embeddedCheckoutSupport == TebexEmbeddedCheckoutSupport.OVERLAY) { + viewer.sendMessage("Opening checkout as game overlay!"); + } else { + viewer.sendMessage("Opening checkout in an external window!"); } } diff --git a/common/src/main/java/com/lunarclient/apollo/player/ApolloPlayerManagerImpl.java b/common/src/main/java/com/lunarclient/apollo/player/ApolloPlayerManagerImpl.java index 8b02317a..ddb1f6ae 100644 --- a/common/src/main/java/com/lunarclient/apollo/player/ApolloPlayerManagerImpl.java +++ b/common/src/main/java/com/lunarclient/apollo/player/ApolloPlayerManagerImpl.java @@ -141,7 +141,7 @@ public void handlePlayerHandshake(@NotNull ApolloPlayer player, @NotNull PlayerH try { checkoutSupportType = TebexEmbeddedCheckoutSupport.values()[message.getEmbeddedCheckoutSupportValue() - 1]; } catch (ArrayIndexOutOfBoundsException e) { - checkoutSupportType = null; + checkoutSupportType = TebexEmbeddedCheckoutSupport.UNSUPPORTED; } EventBus.EventResult result = EventBus.getBus() diff --git a/docs/developers/modules/tebex.mdx b/docs/developers/modules/tebex.mdx index 642b6f8e..547c7b72 100644 --- a/docs/developers/modules/tebex.mdx +++ b/docs/developers/modules/tebex.mdx @@ -82,23 +82,28 @@ Explore each integration by cycling through each tab to find the best fit for yo **Display Tebex Embedded Checkout** ```java -@Override public void displayTebexEmbeddedCheckoutExample(Player viewer, String basketIdent, String locale) { Optional apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId()); - if (apolloPlayerOpt.isPresent()) { - ApolloPlayer apolloPlayer = apolloPlayerOpt.get(); - TebexEmbeddedCheckoutSupport embeddedCheckoutSupport = apolloPlayer.getTebexEmbeddedCheckoutSupport(); + if (!apolloPlayerOpt.isPresent()) { + viewer.sendMessage("Complete your purchase at https://pay.tebex.io/" + basketIdent); + return; + } - this.tebexModule.displayTebexEmbeddedCheckout(apolloPlayerOpt.get(), basketIdent, locale); + ApolloPlayer apolloPlayer = apolloPlayerOpt.get(); + TebexEmbeddedCheckoutSupport embeddedCheckoutSupport = apolloPlayer.getTebexEmbeddedCheckoutSupport(); - if (embeddedCheckoutSupport == TebexEmbeddedCheckoutSupport.OVERLAY) { - viewer.sendMessage("Opening checkout as game overlay!"); - } else { - viewer.sendMessage("Opening checkout in an external window!"); - } - } else { + if (embeddedCheckoutSupport == TebexEmbeddedCheckoutSupport.UNSUPPORTED) { viewer.sendMessage("Complete your purchase at https://pay.tebex.io/" + basketIdent); + return; + } + + this.tebexModule.displayTebexEmbeddedCheckout(apolloPlayerOpt.get(), basketIdent, locale); + + if (embeddedCheckoutSupport == TebexEmbeddedCheckoutSupport.OVERLAY) { + viewer.sendMessage("Opening checkout as game overlay!"); + } else { + viewer.sendMessage("Opening checkout in an external window!"); } } ```