|
1 | | -# Portal Java SDK - Documentation |
| 1 | +# Portal Java SDK |
2 | 2 |
|
3 | | -## Introduction |
4 | | - |
5 | | -A Java client for the Portal WebSocket Server, providing Nostr-based authentication and Lightning Network payment processing capabilities. |
6 | | - |
7 | | ---- |
| 3 | +Java SDK for the [Portal REST API](https://github.com/PortalTechnologiesInc/lib). |
8 | 4 |
|
9 | 5 | ## Installation |
10 | 6 |
|
11 | | -1. Add the Jitpack repository to your `build.gradle`: |
12 | | - ```groovy |
13 | | - repositories { |
14 | | - maven { url 'https://jitpack.io' } |
15 | | - } |
16 | | - ``` |
17 | | - |
18 | | - Or if you are using Maven, add the following to your `pom.xml`: |
19 | | - ```xml |
20 | | - <repository> |
21 | | - <id>jitpack.io</id> |
22 | | - <url>https://jitpack.io</url> |
23 | | - </repository> |
24 | | - ``` |
25 | | - |
26 | | -2. Add the dependency to your `build.gradle`: |
27 | | - ```groovy |
28 | | - dependencies { |
29 | | - implementation 'com.github.PortalTechnologiesInc:java-sdk:0.3.0' |
30 | | - } |
31 | | - ``` |
32 | | - |
33 | | - Or if you are using Maven, add the following to your `pom.xml`: |
34 | | - ```xml |
35 | | - <dependency> |
36 | | - <groupId>com.github.PortalTechnologiesInc</groupId> |
37 | | - <artifactId>java-sdk</artifactId> |
38 | | - <version>0.3.0</version> |
39 | | - </dependency> |
40 | | - ``` |
41 | | - |
42 | | -3. Once you're done, you may now proceed integrating the SDK into your project. |
43 | | - |
44 | | ---- |
45 | | - |
46 | | -## Versioning & Compatibility |
47 | | - |
48 | | -The Java SDK version is kept in sync with the [Portal SDK Daemon](https://hub.docker.com/r/getportal/sdk-daemon) (`getportal/sdk-daemon`). |
49 | | - |
50 | | -**Compatibility rule:** the `major.minor` version of the SDK must match the `major.minor` version of the SDK Daemon. The patch version (`x` in `0.3.x`) is independent and can differ — it only contains bug fixes. |
51 | | - |
52 | | -| SDK version | SDK Daemon version | |
53 | | -|-------------|-------------------| |
54 | | -| `0.3.x` | `0.3.x` | |
| 7 | +Add via JitPack: |
55 | 8 |
|
56 | | -**Example:** SDK `0.3.0` works with `getportal/sdk-daemon:0.3.1`, but not with `getportal/sdk-daemon:0.4.0`. |
57 | | - |
58 | | -When upgrading to a new `major.minor`, update both the SDK dependency and the Docker image tag together. |
59 | | - |
60 | | - |
61 | | ---- |
62 | | - |
63 | | -## Basic Usage |
64 | | - |
65 | | -### Initialization |
66 | | - |
67 | | -Create an instance of `PortalSDK` by passing the websocket endpoint of your portal server: |
| 9 | +```kotlin |
| 10 | +// settings.gradle.kts |
| 11 | +dependencyResolutionManagement { |
| 12 | + repositories { |
| 13 | + maven { url = uri("https://jitpack.io") } |
| 14 | + } |
| 15 | +} |
68 | 16 |
|
69 | | -```java |
70 | | -var portalSDK = new PortalSDK(wsEndpoint); |
| 17 | +// build.gradle.kts |
| 18 | +dependencies { |
| 19 | + implementation("com.github.PortalTechnologiesInc:java-sdk:0.4.0") |
| 20 | +} |
71 | 21 | ``` |
72 | 22 |
|
73 | | -### Connecting to the server |
74 | | - |
75 | | -Establish the WebSocket connection, then authenticate with your token: |
| 23 | +## Quick Start |
76 | 24 |
|
77 | 25 | ```java |
78 | | -portalSDK.connect(); |
79 | | -portalSDK.authenticate(authToken); |
| 26 | +PortalClient client = new PortalClient( |
| 27 | + "http://localhost:3000", // portal-rest base URL |
| 28 | + "your-auth-token", |
| 29 | + "your-webhook-secret" // null if you only use polling |
| 30 | +); |
| 31 | + |
| 32 | +// Check connectivity |
| 33 | +String status = client.health(); // "OK" |
80 | 34 | ``` |
81 | 35 |
|
| 36 | +## Async Operations |
82 | 37 |
|
83 | | -### Sending a command |
84 | | - |
85 | | -You can send a command to the server by calling the `sendCommand` method. |
| 38 | +All async methods return `AsyncOperation<T>` immediately. The `done` future resolves |
| 39 | +when a terminal event arrives — either via webhook or polling. |
86 | 40 |
|
87 | 41 | ```java |
88 | | -portalSDK.sendCommand(request, (response, err) -> { |
89 | | - if(err != null) { |
90 | | - logger.error("error sending command: {}", err); |
91 | | - return; |
92 | | - } |
93 | | - logger.info("command sent successfully: {}", response); |
94 | | -}); |
| 42 | +// Request a single payment |
| 43 | +AsyncOperation<StreamEvent> op = client.requestSinglePayment( |
| 44 | + mainKey, |
| 45 | + List.of(), // subkeys |
| 46 | + new SinglePaymentRequestContent("Coffee", 1000, Currency.MILLISATS, null, null, null) |
| 47 | +); |
| 48 | + |
| 49 | +System.out.println("Stream ID: " + op.streamId()); |
95 | 50 | ``` |
96 | 51 |
|
97 | | -### Basic example |
| 52 | +### Option A — Polling (no inbound HTTP required) |
98 | 53 |
|
99 | 54 | ```java |
100 | | -portalSDK.sendCommand(new CalculateNextOccurrenceRequest("weekly", System.currentTimeMillis() / 1000), (res, err) -> { |
101 | | - if(err != null) { |
102 | | - logger.error("error calculating next occurrence: {}", err); |
103 | | - return; |
104 | | - } |
105 | | - logger.info("next occurrence: {}", res.next_occurrence()); |
106 | | -}); |
| 55 | +StreamEvent result = client.pollUntilComplete( |
| 56 | + op.streamId(), |
| 57 | + PollOptions.defaults() |
| 58 | + .intervalMs(500) |
| 59 | + .timeoutMs(60_000) |
| 60 | + .onEvent(event -> System.out.println("Event: " + event.type)) |
| 61 | +); |
| 62 | +System.out.println("Terminal event: " + result.type); |
107 | 63 | ``` |
108 | | ---- |
109 | | - |
110 | | -## Available Commands |
111 | | - |
112 | | -Commands are implemented as specific request classes in [`src/main/java/cc/getportal/command/request/`](./src/main/java/cc/getportal/command/request/), and used via the `sendCommand()` method of the [`PortalSDK`](./src/main/java/cc/getportal/PortalSDK.java) class. |
113 | 64 |
|
114 | | -Some key available commands include: |
| 65 | +### Option B — Webhooks (recommended for production) |
115 | 66 |
|
116 | | -- [`AuthRequest`](./src/main/java/cc/getportal/command/request/AuthRequest.java): Authenticate using a token. |
117 | | -- [`KeyHandshakeUrlRequest`](./src/main/java/cc/getportal/command/request/KeyHandshakeUrlRequest.java): Get handshake URL for key and relays. |
118 | | -- [`RequestSinglePaymentRequest`](./src/main/java/cc/getportal/command/request/RequestSinglePaymentRequest.java): Request a single payment. |
119 | | -- [`MintCashuRequest`](./src/main/java/cc/getportal/command/request/MintCashuRequest.java): Mint Cashu tokens. |
| 67 | +Mount the webhook endpoint in your HTTP server. The SDK verifies the |
| 68 | +`X-Portal-Signature` HMAC-SHA256 header automatically. |
120 | 69 |
|
121 | | -> See [`src/main/java/cc/getportal/command/request/`](./src/main/java/cc/getportal/command/request/) for all available commands and additional details. |
122 | | -
|
123 | | -To use a command, instantiate its request class and pass it to `PortalSDK.sendCommand(...)`. The full list of commands may evolve; check the request folder for the latest options. |
| 70 | +```java |
| 71 | +// In your HTTP server's POST /portal/webhook handler: |
| 72 | +byte[] rawBody = request.getBody(); |
| 73 | +String signature = request.getHeader("X-Portal-Signature"); |
| 74 | +client.deliverWebhookPayload(rawBody, signature); |
124 | 75 |
|
125 | | ---- |
| 76 | +// The done future resolves when the terminal event is delivered |
| 77 | +StreamEvent result = op.done().get(60, TimeUnit.SECONDS); |
| 78 | +``` |
126 | 79 |
|
127 | | -## Example Integrations |
| 80 | +## Other Operations |
128 | 81 |
|
129 | | -- See [portal-demo](https://github.com/PortalTechnologiesInc/portal-demo) for a Kotlin example. |
| 82 | +```java |
| 83 | +// Key handshake |
| 84 | +AsyncOperation<KeyHandshakeResult> handshake = client.newKeyHandshakeUrl(null, false); |
| 85 | +KeyHandshakeResult r = client.pollUntilComplete(handshake.streamId(), PollOptions.defaults()) |
| 86 | + .data.get... // or use handshake.done() with webhooks |
| 87 | + |
| 88 | +// Recurring payment |
| 89 | +AsyncOperation<RecurringPaymentResponseContent> sub = client.requestRecurringPayment( |
| 90 | + mainKey, List.of(), content |
| 91 | +); |
| 92 | + |
| 93 | +// Invoice request |
| 94 | +AsyncOperation<InvoicePaymentResponse> invoice = client.requestInvoice( |
| 95 | + recipientKey, List.of(), |
| 96 | + new RequestInvoiceParams(1000, Currency.MILLISATS, "Test invoice") |
| 97 | +); |
| 98 | + |
| 99 | +// Pay a BOLT11 invoice |
| 100 | +PayInvoiceResponse paid = client.payInvoice("lnbc..."); |
| 101 | + |
| 102 | +// JWT |
| 103 | +String token = client.issueJwt(targetKey, 24); |
| 104 | +String key = client.verifyJwt(pubkey, token); |
| 105 | + |
| 106 | +// Profile |
| 107 | +Profile profile = client.fetchProfile(mainKey); |
| 108 | + |
| 109 | +// Wallet info |
| 110 | +WalletInfoResponse info = client.getWalletInfo(); |
| 111 | +``` |
130 | 112 |
|
131 | | ---- |
| 113 | +## Versioning & Compatibility |
132 | 114 |
|
133 | | -## Main API |
| 115 | +The Java SDK version `major.minor` must match the portal-rest (sdk-daemon) version. |
| 116 | +Patch versions are independent. |
134 | 117 |
|
135 | | -- `PortalSDK` - Main client class |
136 | | -- `PortalRequest` - Represents a request to the server |
137 | | -- `PortalResponse` - Represents a response from the server |
138 | | -- `PortalNotification` - Represents a notification from the server |
| 118 | +| Java SDK | sdk-daemon | |
| 119 | +|----------|------------| |
| 120 | +| 0.4.x | 0.4.x | |
| 121 | +| 0.3.x | 0.3.x | |
139 | 122 |
|
140 | | ---- |
| 123 | +## Architecture |
141 | 124 |
|
142 | | -## Support |
| 125 | +The new REST + polling/webhooks architecture replaces the previous WebSocket API: |
143 | 126 |
|
144 | | -For questions or issues, see the official documentation or open an issue on the project's GitHub repository. |
| 127 | +- **Sync operations** return their result directly. |
| 128 | +- **Async operations** return `AsyncOperation<T>` with `streamId` (available immediately) |
| 129 | + and `done` (`CompletableFuture<T>` that resolves on terminal event). |
| 130 | +- **Polling** — `pollUntilComplete(streamId, options)` loops on `GET /events/:streamId`. |
| 131 | +- **Webhooks** — `deliverWebhookPayload(rawBody, signature)` verifies HMAC-SHA256 and |
| 132 | + resolves the matching `done` future. |
0 commit comments