@@ -486,6 +486,58 @@ public WalletInfoResponse getWalletInfo() throws IOException, InterruptedExcepti
486486 return get ("/wallet/info" , WalletInfoResponse .class );
487487 }
488488
489+ // -------------------------------------------------------------------------
490+ // Verification
491+ // -------------------------------------------------------------------------
492+
493+ /**
494+ * Create an age verification session and automatically start listening for the
495+ * verification token. Returns session info plus an {@link AsyncOperation} that
496+ * resolves when the user completes verification.
497+ *
498+ * <p>Redirect the user to {@code response.session_url} in their browser.
499+ * Poll or await {@code done()} for the {@link CashuResponseStatus} result.
500+ *
501+ * @param relayUrls optional relay URLs; defaults to server's [nostr] config if null
502+ */
503+ public AsyncOperation <CashuResponseStatus > createVerificationSession (
504+ @ Nullable List <String > relayUrls
505+ ) throws IOException , InterruptedException , PortalSDKException {
506+ Map <String , Object > body = new java .util .HashMap <>();
507+ if (relayUrls != null ) body .put ("relays" , relayUrls );
508+ VerificationSessionResponse resp = post ("/verification/sessions" , body , VerificationSessionResponse .class );
509+ AsyncOperation <CashuResponseStatus > op = registerStream (resp .stream_id ,
510+ json -> gson .fromJson (json .getAsJsonObject ("status" ), CashuResponseStatus .class ));
511+ op .setMetadata ("session_id" , resp .session_id );
512+ op .setMetadata ("session_url" , resp .session_url );
513+ op .setMetadata ("ephemeral_npub" , resp .ephemeral_npub );
514+ op .setMetadata ("expires_at" , String .valueOf (resp .expires_at ));
515+ return op ;
516+ }
517+
518+ /** Convenience overload with default relays. */
519+ public AsyncOperation <CashuResponseStatus > createVerificationSession ()
520+ throws IOException , InterruptedException , PortalSDKException {
521+ return createVerificationSession (null );
522+ }
523+
524+ /**
525+ * Request a verification token from a user who already holds one
526+ * (e.g. verified through the mobile app).
527+ *
528+ * @param recipientKey hex-encoded public key of the token holder
529+ * @param subkeys optional subkeys
530+ */
531+ public AsyncOperation <CashuResponseStatus > requestVerificationToken (
532+ String recipientKey , List <String > subkeys
533+ ) throws IOException , InterruptedException , PortalSDKException {
534+ Map <String , Object > body = Map .of ("recipient_key" , recipientKey , "subkeys" , subkeys );
535+ JsonObject resp = post ("/verification/token" , body , JsonObject .class );
536+ String streamId = resp .get ("stream_id" ).getAsString ();
537+ return registerStream (streamId ,
538+ json -> gson .fromJson (json .getAsJsonObject ("status" ), CashuResponseStatus .class ));
539+ }
540+
489541 // -------------------------------------------------------------------------
490542 // Events (low-level)
491543 // -------------------------------------------------------------------------
0 commit comments