Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencyResolutionManagement {

// build.gradle.kts
dependencies {
implementation("com.github.PortalTechnologiesInc:java-sdk:0.4.0")
implementation("com.github.PortalTechnologiesInc:java-sdk:0.4.1")
}
```

Expand Down Expand Up @@ -87,6 +87,20 @@ client.deliverWebhookPayload(rawBody, request.getHeader("X-Portal-Signature"));
| `requestCashu(recipientKey, subkeys, mintUrl, unit, amount)` | `AsyncOperation<CashuResponseStatus>` |
| `authenticateKey(mainKey, subkeys)` | `AsyncOperation<AuthResponseData>` |
| `newKeyHandshakeUrl(staticToken, noRequest)` | `AsyncOperation<KeyHandshakeResult>` |
| `createVerificationSession(relayUrls)` | `VerificationSession` |
| `requestVerificationToken(recipientKey, subkeys)` | `AsyncOperation<CashuResponseStatus>` |

### Age Verification

```java
VerificationSession session = client.createVerificationSession();
System.out.println("Redirect user to: " + session.session.session_url);

CashuResponseStatus result = client.pollUntilComplete(session.operation, new PollOptions(1000, 300_000));
if (result.status.equals("success")) {
System.out.println("Verified! Token: " + result.token);
}
```

## Sync methods

Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = "cc.getportal"
version = "0.4.0"
version = "0.4.1"

java {
toolchain {
Expand All @@ -23,7 +23,7 @@ publishing {

groupId = "cc.getportal"
artifactId = "portal-java-sdk"
version = "0.4.0"
version = "0.4.1"
}
}
}
Expand Down
49 changes: 49 additions & 0 deletions src/main/java/cc/getportal/PortalClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,55 @@ public WalletInfoResponse getWalletInfo() throws IOException, InterruptedExcepti
return get("/wallet/info", WalletInfoResponse.class);
}

// -------------------------------------------------------------------------
// Verification
// -------------------------------------------------------------------------

/**
* Create an age verification session and automatically start listening for the
* verification token. Returns a {@link VerificationSession} containing session
* info and an {@link AsyncOperation} that resolves when the user completes
* verification.
*
* <p>Redirect the user to {@code session.session_url} in their browser.
* Poll or await {@code operation.done()} for the {@link CashuResponseStatus} result.
*
* @param relayUrls optional relay URLs; defaults to server's [nostr] config if null
*/
public VerificationSession createVerificationSession(
@Nullable List<String> relayUrls
) throws IOException, InterruptedException, PortalSDKException {
Map<String, Object> body = new java.util.HashMap<>();
if (relayUrls != null) body.put("relays", relayUrls);
VerificationSessionResponse resp = post("/verification/sessions", body, VerificationSessionResponse.class);
AsyncOperation<CashuResponseStatus> op = registerStream(resp.stream_id,
json -> gson.fromJson(json.getAsJsonObject("status"), CashuResponseStatus.class));
return new VerificationSession(resp, op);
}

/** Convenience overload with default relays. */
public VerificationSession createVerificationSession()
throws IOException, InterruptedException, PortalSDKException {
return createVerificationSession(null);
}

/**
* Request a verification token from a user who already holds one
* (e.g. verified through the mobile app).
*
* @param recipientKey hex-encoded public key of the token holder
* @param subkeys optional subkeys
*/
public AsyncOperation<CashuResponseStatus> requestVerificationToken(
String recipientKey, List<String> subkeys
) throws IOException, InterruptedException, PortalSDKException {
Map<String, Object> body = Map.of("recipient_key", recipientKey, "subkeys", subkeys);
JsonObject resp = post("/verification/token", body, JsonObject.class);
String streamId = resp.get("stream_id").getAsString();
return registerStream(streamId,
json -> gson.fromJson(json.getAsJsonObject("status"), CashuResponseStatus.class));
}

// -------------------------------------------------------------------------
// Events (low-level)
// -------------------------------------------------------------------------
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/cc/getportal/model/VerificationSession.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cc.getportal.model;

import cc.getportal.AsyncOperation;

/**
* Result of {@code createVerificationSession()} — contains the session info
* and an {@link AsyncOperation} for polling the verification token.
*/
public class VerificationSession {
public final VerificationSessionResponse session;
public final AsyncOperation<CashuResponseStatus> operation;

public VerificationSession(
VerificationSessionResponse session,
AsyncOperation<CashuResponseStatus> operation
) {
this.session = session;
this.operation = operation;
}
}
12 changes: 12 additions & 0 deletions src/main/java/cc/getportal/model/VerificationSessionResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cc.getportal.model;

/**
* Response from {@code POST /verification/sessions}.
*/
public class VerificationSessionResponse {
public String session_id;
public String session_url;
public String ephemeral_npub;
public long expires_at;
public String stream_id;
}
Loading