Skip to content

feat: replace WebSocket with REST + polling + webhooks (0.4.0)#7

Merged
denmeh merged 7 commits into
mainfrom
feat/rest-polling-webhooks
Mar 20, 2026
Merged

feat: replace WebSocket with REST + polling + webhooks (0.4.0)#7
denmeh merged 7 commits into
mainfrom
feat/rest-polling-webhooks

Conversation

@portal-wheatley

@portal-wheatley portal-wheatley commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the WebSocket-based PortalSDK with a REST HTTP client that mirrors the TypeScript SDK's async operation pattern.

Architecture

All async methods return AsyncOperation<T> immediately:

  • streamId — available right away (use it to track the operation)
  • doneCompletableFuture<T> that resolves with the typed result when a terminal event arrives

Events are delivered via:

  • Webhooksclient.deliverWebhookPayload(rawBody, signature) (HMAC-SHA256 verified)
  • Pollingclient.pollUntilComplete(op, PollOptions) as a fallback

StreamEvent is used only internally and for per-event callbacks (PollOptions.onEvent). Terminal payloads are deserialized directly to T — no raw JSON is ever exposed in the public API.

Async methods

Method Resolves to
requestSinglePayment(...) AsyncOperation<InvoiceStatus>
requestPaymentRaw(...) AsyncOperation<InvoiceStatus>
requestRecurringPayment(...) AsyncOperation<RecurringPaymentResponseContent>
requestInvoice(...) AsyncOperation<InvoicePaymentResponse>
requestCashu(...) AsyncOperation<CashuResponseStatus>
authenticateKey(...) AsyncOperation<AuthResponseData>
newKeyHandshakeUrl(...) AsyncOperation<KeyHandshakeResult>

Sync methods

health(), version(), info(), fetchProfile(), payInvoice(), closeRecurringPayment(), issueJwt(), verifyJwt(), addRelay(), removeRelay(), mintCashu(), burnCashu(), sendCashuDirect(), calculateNextOccurrence(), fetchNip05Profile(), getWalletInfo()

New classes

Class Description
PortalClient HTTP client using java.net.http (Java 17, no new deps)
AsyncOperation<T> Record with streamId + CompletableFuture<T> done
PollOptions Builder for polling interval/timeout/per-event callback
StreamEvent Thin event (type, index, timestamp, isTerminal()) — internal use only
WebhookPayload Webhook POST body with streamId
InvoiceStatus Terminal payload for single/raw payments
InvoicePaymentResponse Terminal payload for invoice requests
KeyHandshakeResult Terminal payload for key handshakes
AuthResponseData Terminal payload for authenticate-key
RecurringPaymentResponseContent Terminal payload for recurring payments

Removed

  • PortalSDK.java (WebSocket)
  • PortalWsClient.java
  • Response.java / ResponseDeserializer.java
  • command/ package (all WS request/response/notification classes)
  • org.java-websocket dependency

Notes

  • Aligned with lib PR #171 (wheatley/rest-api-webhooks)
  • Java not available in the sandbox — compilation verified by CI (PR ci: add build check workflow #8 merged first)

Replaces the WebSocket-based PortalSDK with a REST HTTP client that
mirrors the TypeScript SDK's async operation pattern.

## New architecture

- PortalClient: HTTP client using java.net.http.HttpClient (Java 17)
- AsyncOperation<T>: record { streamId, CompletableFuture<T> done }
- PollOptions: builder for polling interval/timeout/per-event callback
- StreamEvent: base event with type, index, timestamp, isTerminal()
- WebhookPayload: extends StreamEvent with streamId field

## Async operations

Methods that trigger a portal async operation return AsyncOperation<T>
immediately. Events resolve the done future via:
- Polling: client.pollUntilComplete(streamId, PollOptions.defaults())
- Webhooks: client.deliverWebhookPayload(rawBody, xPortalSignature)
  (HMAC-SHA256 verified, routed to matching CompletableFuture)

## New model classes

AuthResponseData, AuthResponseStatus, KeyHandshakeResult,
RecurringPaymentResponseContent, RecurringPaymentStatus,
InvoicePaymentResponse, PayInvoiceResponse, EventsResponse,
VersionResponse, InfoResponse, WalletInfoResponse, RequestInvoiceParams

## Removed

- PortalSDK.java (WebSocket-based)
- PortalWsClient.java
- Response.java / ResponseDeserializer.java
- command/ package (all WS request/response/notification classes)

Aligns with lib PR#171 (refactor/portal-rest: REST + polling + webhooks)
@portal-wheatley portal-wheatley force-pushed the feat/rest-polling-webhooks branch from 4c2b8de to af545f3 Compare March 17, 2026 20:01
- requestSinglePayment: use payment_request: {...} not flat body
- requestPaymentRaw: use payment_request: {...} not flat body
- requestRecurringPayment: use payment_request: {...} not flat body
- requestInvoice: use content: {...} not flat body

All four endpoints expect a nested wrapper field — flattening the
content into the top-level body caused 400/422 errors at runtime.
@denmeh denmeh merged commit 93684c2 into main Mar 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants