001/* 002 * Copyright 2024 Vonage 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.vonage.client.voice; 017 018import com.vonage.client.*; 019import com.vonage.client.auth.JWTAuthMethod; 020import com.vonage.client.common.HttpMethod; 021import com.vonage.client.voice.ncco.InputMode; 022import com.vonage.client.voice.ncco.Ncco; 023import com.vonage.jwt.Jwt; 024import java.io.IOException; 025import java.net.URI; 026import java.nio.file.Files; 027import java.nio.file.Path; 028import java.util.Objects; 029import java.util.function.Function; 030 031/** 032 * A client for talking to the Vonage Voice API. The standard way to obtain an instance of this class is to use {@link 033 * VonageClient#getVoiceClient()}. 034 */ 035public class VoiceClient { 036 final RestEndpoint<Call, CallEvent> createCall; 037 final RestEndpoint<String, CallInfo> getCall; 038 final RestEndpoint<CallsFilter, CallInfoPage> listCalls; 039 final RestEndpoint<ModifyCallPayload, Void> modifyCall; 040 final RestEndpoint<StreamPayload, StreamResponse> startStream; 041 final RestEndpoint<String, StreamResponse> stopStream; 042 final RestEndpoint<TalkPayload, TalkResponse> startTalk; 043 final RestEndpoint<String, TalkResponse> stopTalk; 044 final RestEndpoint<DtmfPayload, DtmfResponse> sendDtmf; 045 final RestEndpoint<AddDtmfListenerRequest, Void> addDtmfListener; 046 final RestEndpoint<String, Void> removeDtmfListener; 047 final RestEndpoint<String, byte[]> downloadRecording; 048 049 /** 050 * Constructor. 051 * 052 * @param wrapper (required) shared HTTP wrapper object used for making REST calls. 053 */ 054 public VoiceClient(HttpWrapper wrapper) { 055 056 @SuppressWarnings("unchecked") 057 class Endpoint<T, R> extends DynamicEndpoint<T, R> { 058 Endpoint(Function<T, String> pathGetter, HttpMethod method, R... type) { 059 super(DynamicEndpoint.<T, R> builder(type).authMethod(JWTAuthMethod.class) 060 .responseExceptionType(VoiceResponseException.class) 061 .requestMethod(method).wrapper(wrapper).pathGetter((de, req) -> { 062 String base = de.getHttpWrapper().getHttpConfig().getVersionedApiBaseUri("v1"); 063 String path = pathGetter.apply(req); 064 if (path.startsWith("http") && method == HttpMethod.GET) { 065 return path; 066 } 067 return base + "/calls" + (path.isEmpty() ? "" : "/" + path); 068 }) 069 ); 070 } 071 } 072 073 createCall = new Endpoint<>(req -> "", HttpMethod.POST); 074 getCall = new Endpoint<>(Function.identity(), HttpMethod.GET); 075 listCalls = new Endpoint<>(req -> "", HttpMethod.GET); 076 modifyCall = new Endpoint<>(req -> req.uuid, HttpMethod.PUT); 077 startStream = new Endpoint<>(req -> req.uuid + "/stream", HttpMethod.PUT); 078 stopStream = new Endpoint<>(uuid -> uuid + "/stream", HttpMethod.DELETE); 079 startTalk = new Endpoint<>(req -> req.uuid + "/talk", HttpMethod.PUT); 080 stopTalk = new Endpoint<>(uuid -> uuid + "/talk", HttpMethod.DELETE); 081 sendDtmf = new Endpoint<>(req -> req.uuid + "/dtmf", HttpMethod.PUT); 082 addDtmfListener = new Endpoint<>(req -> req.uuid + "/input/dtmf", HttpMethod.PUT); 083 removeDtmfListener = new Endpoint<>(uuid -> uuid + "/input/dtmf", HttpMethod.DELETE); 084 downloadRecording = new Endpoint<>(Function.identity(), HttpMethod.GET); 085 } 086 087 private String validateUuid(String uuid) { 088 return Objects.requireNonNull(uuid, "UUID is required."); 089 } 090 091 private String validateUrl(String url) { 092 if (url == null || url.trim().isEmpty()) { 093 throw new IllegalArgumentException("URL is required."); 094 } 095 return URI.create(url).toString(); 096 } 097 098 /** 099 * Begin a call to a phone number. 100 * 101 * @param callRequest Describing the call to be made. 102 * 103 * @return A CallEvent describing the initial state of the call, containing the {@code uuid} required to interact 104 * with the ongoing phone call. 105 * 106 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 107 * @throws VonageResponseParseException if the response from the API could not be parsed. 108 */ 109 public CallEvent createCall(Call callRequest) throws VonageResponseParseException, VonageClientException { 110 return createCall.execute(callRequest); 111 } 112 113 /** 114 * Obtain the first page of CallInfo objects, representing the most recent calls initiated by {@link 115 * #createCall(Call)}. 116 * 117 * @return A CallInfoPage representing the response from the Vonage Voice API. 118 * 119 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 120 * @throws VonageResponseParseException if the response from the API could not be parsed. 121 */ 122 public CallInfoPage listCalls() throws VonageResponseParseException, VonageClientException { 123 return listCalls(null); 124 } 125 126 /** 127 * Obtain the first page of CallInfo objects matching the query described by {@code filter}, representing the most 128 * recent calls initiated by {@link #createCall(Call)}. 129 * 130 * @param filter (optional) A filter describing which calls to be listed. 131 * 132 * @return A CallInfoPage representing the response from the Vonage Voice API. 133 * 134 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 135 * @throws VonageResponseParseException if the response from the API could not be parsed. 136 */ 137 public CallInfoPage listCalls(CallsFilter filter) throws VonageResponseParseException, VonageClientException { 138 return listCalls.execute(filter); 139 } 140 141 /** 142 * Look up the status of a single call initiated by {@link #createCall(Call)}. 143 * 144 * @param uuid (required) The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. 145 * This value can be obtained with {@link CallEvent#getUuid()}. 146 * 147 * @return A CallInfo object, representing the response from the Vonage Voice API. 148 * 149 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 150 * @throws VonageResponseParseException if the response from the API could not be parsed. 151 */ 152 public CallInfo getCallDetails(String uuid) throws VonageResponseParseException, VonageClientException { 153 return getCall.execute(validateUuid(uuid)); 154 } 155 156 /** 157 * Send DTMF codes to an ongoing call. 158 * 159 * @param uuid (REQUIRED) The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. 160 * This value can be obtained with {@link CallEvent#getUuid()} 161 * @param digits (REQUIRED) A string specifying the digits to be sent to the call. Valid characters are the digits 162 * {@code 1-9</tt>, <tt>#</tt>, <tt>*</tt>, with the special character <tt>p} indicating a short pause 163 * between tones. 164 * 165 * @return A CallInfo object, representing the response from the Vonage Voice API. 166 * 167 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 168 * @throws VonageResponseParseException if the response from the API could not be parsed. 169 */ 170 public DtmfResponse sendDtmf(String uuid, String digits) throws VonageResponseParseException, VonageClientException { 171 return sendDtmf.execute(new DtmfPayload(digits, validateUuid(uuid))); 172 } 173 174 private void modifyCall(String callId, ModifyCallAction action) throws VoiceResponseException { 175 modifyCall.execute(new ModifyCallPayload(action, validateUuid(callId))); 176 } 177 178 /** 179 * Modify a call with the {@linkplain ModifyCallAction#EARMUFF} action. 180 * This prevents the call from hearing audio. 181 * 182 * @param callId UUID of the call. 183 * 184 * @throws VoiceResponseException If there was an error performing the action. 185 * 186 * @since 7.11.0 187 */ 188 public void earmuffCall(String callId) throws VoiceResponseException { 189 modifyCall(callId, ModifyCallAction.EARMUFF); 190 } 191 192 /** 193 * Modify a call with the {@linkplain ModifyCallAction#UNEARMUFF} action. 194 * This allows the call to hear audio again. 195 * 196 * @param callId UUID of the call. 197 * 198 * @throws VoiceResponseException If there was an error performing the action. 199 * 200 * @since 7.11.0 201 */ 202 public void unearmuffCall(String callId) throws VoiceResponseException { 203 modifyCall(callId, ModifyCallAction.UNEARMUFF); 204 } 205 206 /** 207 * Modify a call with the {@linkplain ModifyCallAction#MUTE} action. 208 * 209 * @param callId UUID of the call. 210 * 211 * @throws VoiceResponseException If there was an error performing the action. 212 * 213 * @since 7.11.0 214 */ 215 public void muteCall(String callId) throws VoiceResponseException { 216 modifyCall(callId, ModifyCallAction.MUTE); 217 } 218 219 /** 220 * Modify a call with the {@linkplain ModifyCallAction#UNMUTE} action. 221 * 222 * @param callId UUID of the call. 223 * 224 * @throws VoiceResponseException If there was an error performing the action. 225 * 226 * @since 7.11.0 227 */ 228 public void unmuteCall(String callId) throws VoiceResponseException { 229 modifyCall(callId, ModifyCallAction.UNMUTE); 230 } 231 232 /** 233 * Modify a call with the {@linkplain ModifyCallAction#HANGUP} action. 234 * 235 * @param callId UUID of the call. 236 * 237 * @throws VoiceResponseException If there was an error performing the action. 238 * 239 * @since 7.11.0 240 */ 241 public void terminateCall(String callId) throws VoiceResponseException { 242 modifyCall(callId, ModifyCallAction.HANGUP); 243 } 244 245 /** 246 * Transfer a call to a different NCCO endpoint. 247 * 248 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value 249 * can be obtained with {@link CallEvent#getUuid()}. 250 * @param nccoUrl The URL of the NCCO endpoint the call should be transferred to. 251 * 252 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 253 * @throws VonageResponseParseException if the response from the API could not be parsed. 254 */ 255 public void transferCall(String uuid, String nccoUrl) throws VonageResponseParseException, VonageClientException { 256 modifyCall.execute(new TransferCallPayload(validateUrl(nccoUrl), validateUuid(uuid))); 257 } 258 259 /** 260 * Transfer a call to a different NCCO. 261 * 262 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value can 263 * be obtained with {@link CallEvent#getUuid()}. 264 * @param ncco The new NCCO that will be used in the call. 265 * 266 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 267 * @throws VonageResponseParseException if the response from the API could not be parsed. 268 */ 269 public void transferCall(String uuid, Ncco ncco) throws VonageResponseParseException, VonageClientException { 270 modifyCall.execute(new TransferCallPayload(ncco, validateUuid(uuid))); 271 } 272 273 /** 274 * Stream audio to an ongoing call. 275 * 276 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value 277 * can be obtained with {@link CallEvent#getUuid()}. 278 * @param streamUrl A URL of an audio file in MP3 or 16-bit WAV format, to be streamed to the call. 279 * @param loop The number of times to repeat the audio. The default value is {@code 1}, or you can use {@code 280 * 0} to indicate that the audio should be repeated indefinitely. 281 * 282 * @return The data returned from the Voice API. 283 * 284 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 285 * @throws VonageResponseParseException if the response from the API could not be parsed. 286 */ 287 public StreamResponse startStream(String uuid, String streamUrl, int loop) throws VonageResponseParseException, VonageClientException { 288 return startStream(uuid, streamUrl, loop, 0d); 289 } 290 291 /** 292 * Stream audio to an ongoing call. 293 * 294 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value 295 * can be obtained with {@link CallEvent#getUuid()}. 296 * @param streamUrl A URL of an audio file in MP3 or 16-bit WAV format, to be streamed to the call. 297 * @param loop The number of times to repeat the audio. The default value is {@code 1}, or you can use {@code 298 * 0} to indicate that the audio should be repeated indefinitely. 299 * @param level The audio level of the stream, between -1 and 1 with a precision of 0.1. The default value is 0. 300 * 301 * @return The data returned from the Voice API. 302 * 303 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 304 * @throws VonageResponseParseException if the response from the API could not be parsed. 305 * 306 * @since 7.3.0 307 */ 308 public StreamResponse startStream(String uuid, String streamUrl, int loop, double level) throws VonageResponseParseException, VonageClientException { 309 return startStream.execute(new StreamPayload(validateUrl(streamUrl), loop, level, validateUuid(uuid))); 310 } 311 312 /** 313 * Stream audio to an ongoing call. 314 * 315 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value 316 * can be obtained with {@link CallEvent#getUuid()}. 317 * @param streamUrl A URL of an audio file in MP3 or 16-bit WAV format, to be streamed to the call. 318 * 319 * @return The data returned from the Voice API. 320 * 321 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 322 * @throws VonageResponseParseException if the response from the API could not be parsed. 323 */ 324 public StreamResponse startStream(String uuid, String streamUrl) throws VonageResponseParseException, VonageClientException { 325 return startStream(uuid, streamUrl, 1); 326 } 327 328 /** 329 * Stop the audio being streamed into a call. 330 * 331 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value can 332 * be obtained with {@link CallEvent#getUuid()}. 333 * 334 * @return The data returned from the Voice API. 335 * 336 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 337 * @throws VonageResponseParseException if the response from the API could not be parsed. 338 */ 339 public StreamResponse stopStream(String uuid) throws VonageResponseParseException, VonageClientException { 340 return stopStream.execute(validateUuid(uuid)); 341 } 342 343 /** 344 * Send a synthesized speech message to an ongoing call. 345 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. 346 * This value can be obtained with {@link CallEvent#getUuid()}. 347 * 348 * @param properties Properties of the text-to-speech request. 349 * 350 * @return Metadata from the Voice API if successful. 351 * 352 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 353 * @throws VonageResponseParseException if the response from the API could not be parsed. 354 * 355 * @since 7.3.0 356 */ 357 public TalkResponse startTalk(String uuid, TalkPayload properties) { 358 Objects.requireNonNull(properties, "TalkPayload is required").uuid = validateUuid(uuid); 359 return startTalk.execute(properties); 360 } 361 362 /** 363 * Send a synthesized speech message to an ongoing call. 364 * <p> 365 * The message will only play once, spoken with the default en-US voice. 366 * 367 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value can 368 * be obtained with {@link CallEvent#getUuid()} 369 * @param text The message to be spoken to the call participants. 370 * 371 * @return The data returned from the Voice API. 372 * 373 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 374 * @throws VonageResponseParseException if the response from the API could not be parsed. 375 * 376 * @deprecated Use {@link #startTalk(String, TalkPayload)}. 377 */ 378 @Deprecated 379 public TalkResponse startTalk(String uuid, String text) throws VonageResponseParseException, VonageClientException { 380 return startTalk(uuid, TalkPayload.builder(text).build()); 381 } 382 383 /** 384 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value 385 * can be obtained with {@link CallEvent#getUuid()} 386 * @param text The message to be spoken to the call participants. 387 * 388 * @param language The Language to use when converting text-to-speech. 389 * 390 * @return The data returned from the Voice API. 391 * 392 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 393 * @throws VonageResponseParseException if the response from the API could not be parsed. 394 * 395 * @deprecated Use {@link #startTalk(String, TalkPayload)}. 396 */ 397 @Deprecated 398 public TalkResponse startTalk(String uuid, String text, TextToSpeechLanguage language) throws VonageResponseParseException, VonageClientException { 399 return startTalk(uuid, TalkPayload.builder(text).language(language).build()); 400 } 401 402 /** 403 * Send a synthesized speech message to an ongoing call. 404 * <p> 405 * The message will be spoken with the default en-US voice. 406 * 407 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value can 408 * be obtained with {@link CallEvent#getUuid()}. 409 * @param text The message to be spoken to the call participants. 410 * @param loop The number of times to repeat the message. The default value is {@code 1}, or you can use {@code 0} 411 * to indicate that the message should be repeated indefinitely. 412 * 413 * @return The data returned from the Voice API. 414 * 415 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 416 * @throws VonageResponseParseException if the response from the API could not be parsed. 417 * 418 * @deprecated Use {@link #startTalk(String, TalkPayload)}. 419 */ 420 @Deprecated 421 public TalkResponse startTalk(String uuid, String text, int loop) throws VonageResponseParseException, VonageClientException { 422 return startTalk(uuid, TalkPayload.builder(text).loop(loop).build()); 423 } 424 425 /** 426 * Send a synthesized speech message to an ongoing call. 427 * 428 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. 429 * This value can be obtained with {@link CallEvent#getUuid()}. 430 * @param text The message to be spoken to the call participants. 431 * @param language The language to use for the text-to-speech. 432 * @param style The language style to use for the text-to-speech. 433 * @param loop The number of times to repeat the message. The default value is {@code 1}, or you can use {@code 434 * 0} to indicate that the message should be repeated indefinitely. 435 * 436 * @return The data returned from the Voice API. 437 * 438 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 439 * @throws VonageResponseParseException if the response from the API could not be parsed. 440 * 441 * @deprecated Use {@link #startTalk(String, TalkPayload)}. 442 */ 443 @Deprecated 444 public TalkResponse startTalk(String uuid, String text, TextToSpeechLanguage language, int style, int loop) throws VonageResponseParseException, VonageClientException { 445 return startTalk(uuid, TalkPayload.builder(text).loop(loop).language(language).style(style).build()); 446 } 447 448 /** 449 * Send a synthesized speech message to an ongoing call. 450 * 451 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value 452 * can be obtained with {@link CallEvent#getUuid()}. 453 * @param text The message to be spoken to the call participants. 454 * @param language The language to use for the text-to-speech. 455 * @param style The language style to use for the text-to-speech. 456 * 457 * @return The data returned from the Voice API. 458 * 459 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 460 * @throws VonageResponseParseException if the response from the API could not be parsed. 461 * 462 * @deprecated Use {@link #startTalk(String, TalkPayload)}. 463 */ 464 @Deprecated 465 public TalkResponse startTalk(String uuid, String text, TextToSpeechLanguage language, int style) throws VonageResponseParseException, VonageClientException { 466 return startTalk(uuid, TalkPayload.builder(text).language(language).style(style).build()); 467 } 468 469 /** 470 * Stop the message being spoken into a call. 471 * 472 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. This value can 473 * be obtained with {@link CallEvent#getUuid()}. 474 * 475 * @return The data returned from the Voice API. 476 * 477 * @throws VonageClientException if there was a problem with the Vonage request or response objects. 478 * @throws VonageResponseParseException if the response from the API could not be parsed. 479 */ 480 public TalkResponse stopTalk(String uuid) throws VonageResponseParseException, VonageClientException { 481 return stopTalk.execute(validateUuid(uuid)); 482 } 483 484 /** 485 * Register a listener for asynchronous DTMF events sent by a caller to an 486 * {@linkplain com.vonage.client.voice.ncco.InputAction} NCCO action, when the 487 * {@linkplain com.vonage.client.voice.ncco.InputAction.Builder#mode(InputMode)} is 488 * {@link com.vonage.client.voice.ncco.InputMode#ASYNCHRONOUS}. 489 * 490 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. 491 * This value can be obtained with {@link CallEvent#getUuid()}. 492 * 493 * @param eventUrl The URL to send asynchronous DTMF user input events to. 494 * 495 * @throws VoiceResponseException If the call does not exist or the listener could not be added, 496 * for example if the call's state or input mode are incompatible. 497 * 498 * @since 8.12.0 499 */ 500 public void addDtmfListener(String uuid, String eventUrl) throws VoiceResponseException { 501 addDtmfListener.execute(new AddDtmfListenerRequest(validateUuid(uuid), URI.create(validateUrl(eventUrl)))); 502 } 503 504 /** 505 * Stop receiving updates for asynchronous DTMF events sent by a caller to an 506 * {@linkplain com.vonage.client.voice.ncco.InputAction} NCCO, when the 507 * {@linkplain com.vonage.client.voice.ncco.InputAction.Builder#mode(InputMode)} is 508 * {@link com.vonage.client.voice.ncco.InputMode#ASYNCHRONOUS}. Calling this method 509 * stops sending DTMF events to the event URL set in {@link #addDtmfListener(String, String)}. 510 * 511 * @param uuid The UUID of the call, obtained from the object returned by {@link #createCall(Call)}. 512 * This value can be obtained with {@link CallEvent#getUuid()}. 513 * 514 * @throws VoiceResponseException If the call does not exist or have a listener attached. 515 * 516 * @since 8.12.0 517 */ 518 public void removeDtmfListener(String uuid) throws VoiceResponseException { 519 removeDtmfListener.execute(validateUuid(uuid)); 520 } 521 522 /** 523 * Download a recording. 524 * 525 * @param recordingUrl The recording URL, as obtained from the webhook callback. 526 * 527 * @return The raw contents of the downloaded recording as a byte array. 528 * 529 * @throws IllegalArgumentException If the recordingUrl is invalid. 530 * @throws VoiceResponseException If there was an error downloading the recording from the URL. 531 * 532 * @since 7.11.0 533 */ 534 public byte[] downloadRecordingRaw(String recordingUrl) { 535 return downloadRecording.execute(validateUrl(recordingUrl)); 536 } 537 538 /** 539 * Download a recording and save it to a file. 540 * 541 * @param recordingUrl The recording URL, as obtained from the webhook callback. 542 * @param destination Path to save the recording to. 543 * 544 * @throws IOException If there was an error writing to the file. 545 * @throws VoiceResponseException If there was an error downloading the recording from the URL. 546 * @throws IllegalArgumentException If the recordingUrl is invalid. 547 * 548 * @since 7.11.0 549 */ 550 public void saveRecording(String recordingUrl, Path destination) throws IOException { 551 Path path = Objects.requireNonNull(destination, "Save path is required."); 552 byte[] binary = downloadRecordingRaw(recordingUrl); 553 if (Files.isDirectory(destination)) { 554 String fileName = recordingUrl.substring(recordingUrl.lastIndexOf('/') + 1); 555 path = path.resolve(fileName); 556 } 557 Files.write(path, binary); 558 } 559 560 /** 561 * Utility method for verifying whether a token was signed by a secret. 562 * This is mostly useful when using signed callbacks to ensure that the inbound 563 * data came from Vonage servers. The signature is performed using the SHA-256 HMAC algorithm. 564 * 565 * @param jwt The JSON Web Token to verify. 566 * @param secret The symmetric secret key (HS256) to use for decrypting the token's signature. 567 * 568 * @return {@code true} if the token was signed by the secret, {@code false} otherwise. 569 * 570 * @since 7.11.0 571 */ 572 public static boolean verifySignature(String jwt, String secret) { 573 return Jwt.verifySignature(jwt, secret); 574 } 575}