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.verify2; 017 018import com.fasterxml.jackson.annotation.*; 019import com.vonage.client.Jsonable; 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Locale; 023import java.util.Objects; 024import java.util.regex.Pattern; 025 026/** 027 * Defines properties for a verify request to be sent to the user. Such properties include the brand 028 * (i.e. the sender that the user sees asking them for verification), language to send the message in, 029 * length of the verification code and timeout between delivery channel workflows. 030 * <p> 031 * A verification request can have multiple "workflows". Each workflow defines a contact 032 * method for verification. The order of workflows defines the order in which each contact 033 * method will be attempted. Once a contact method has succeeded, the remaining workflows will 034 * be aborted. This flexibility exists as a fallback / backup verification. A different communication 035 * channel and/or number can be contacted if desired. 036 */ 037public class VerificationRequest implements Jsonable { 038 static final Pattern CODE_REGEX = Pattern.compile("[a-zA-Z0-9]{4,10}"); 039 040 @JsonProperty("locale") protected final Locale locale; 041 protected final Integer channelTimeout, codeLength; 042 protected final Boolean fraudCheck; 043 protected final String brand, code, clientRef; 044 protected final List<Workflow> workflows; 045 046 VerificationRequest(Builder builder) { 047 locale = builder.locale; 048 clientRef = builder.clientRef; 049 fraudCheck = builder.fraudCheck != null && !builder.fraudCheck ? false : null; 050 if ((brand = builder.brand) == null || brand.trim().isEmpty()) { 051 throw new IllegalArgumentException("Brand name is required."); 052 } 053 if (brand.length() > 16) { 054 throw new IllegalArgumentException("Brand cannot exceed 16 characters in length."); 055 } 056 if ((channelTimeout = builder.timeout) != null && (channelTimeout < 15 || channelTimeout > 900)) { 057 throw new IllegalArgumentException("Delivery wait timeout must be between 15 and 900 seconds."); 058 } 059 if ((workflows = builder.workflows).isEmpty()) { 060 throw new IllegalStateException("At least one workflow must be defined."); 061 } 062 if ((codeLength = builder.codeLength) != null && (codeLength < 4 || codeLength > 10)) { 063 throw new IllegalArgumentException("Code length must be between 4 and 10 (inclusive)."); 064 } 065 if ((code = builder.code) != null && !CODE_REGEX.matcher(code).matches()) { 066 throw new IllegalArgumentException("Custom verification code must be 4-10 alphanumeric characters."); 067 } 068 if (isCodeless()) { 069 if (codeLength != null) { 070 throw new IllegalStateException("Code length has no effect for codeless workflows."); 071 } 072 if (code != null) { 073 throw new IllegalStateException("Code has no effect for codeless workflows."); 074 } 075 } 076 if (code != null && codeLength != null && code.length() != codeLength) { 077 throw new IllegalStateException("Code '"+code+"' is not "+codeLength+" characters."); 078 } 079 if (workflows.stream().anyMatch(SilentAuthWorkflow.class::isInstance)) { 080 if (!(workflows.get(0) instanceof SilentAuthWorkflow)) { 081 throw new IllegalStateException("Silent Auth must be the first workflow."); 082 } 083 } 084 } 085 086 /** 087 * The brand that is sending the verification request. This is what 088 * the user will see when they receive the notification. 089 * 090 * @return The verification sender name. 091 */ 092 @JsonProperty("brand") 093 public String getBrand() { 094 return brand; 095 } 096 097 /** 098 * Language for the request in ISO_639-1 format. 099 * 100 * @return The locale, or {@code null} if not set (the default). 101 */ 102 @JsonIgnore 103 public Locale getLocale() { 104 return locale; 105 } 106 107 @JsonGetter("locale") 108 protected String getLocaleAsString() { 109 return locale == null ? null : locale.toString().replace("_", "-").toLowerCase(); 110 } 111 112 /** 113 * Specifies the wait time in seconds between attempts to delivery the verification code. 114 * 115 * @return The delivery timeout, or {@code null} if not set (the default). 116 */ 117 @JsonProperty("channel_timeout") 118 public Integer getChannelTimeout() { 119 return channelTimeout; 120 } 121 122 /** 123 * Length of the code to send to the user. Does not apply to codeless verification channels. 124 * 125 * @return The verification code length, or {@code null} if unset (the default) or not applicable. 126 */ 127 @JsonProperty("code_length") 128 public Integer getCodeLength() { 129 return codeLength; 130 } 131 132 /** 133 * Custom alphanumeric verification code to send to the user instead of the Vonage-generated one. 134 * 135 * @return The custom code, or {@code null} if unset. 136 */ 137 @JsonProperty("code") 138 public String getCode() { 139 return code; 140 } 141 142 /** 143 * If the client_ref is set when the request is sent, it will be included in the callbacks. 144 * 145 * @return The client reference, or {@code null} if not set. 146 */ 147 @JsonProperty("client_ref") 148 public String getClientRef() { 149 return clientRef; 150 } 151 152 /** 153 * Determines if the network level fraud check is enforced. See 154 * <a href=https://developer.vonage.com/en/verify/verify-v2/guides/v2-anti-fraud>the documentation</a>. 155 * This feature only takes effect if it has been enabled on your account. 156 * 157 * @return Whether network block is respected, or {@code null} if not set or {@code true} (the default). 158 */ 159 @JsonProperty("fraud_check") 160 public Boolean getFraudCheck() { 161 return fraudCheck; 162 } 163 164 /** 165 * Workflows are a sequence of actions that Vonage use to reach the user you wish to verify with a PIN code. 166 * 167 * @return The list of workflows (contact methods) to be used in verification, in order of preference. 168 */ 169 @JsonProperty("workflow") 170 protected List<Workflow> getWorkflows() { 171 return workflows; 172 } 173 174 /** 175 * Determines if the workflows defined in this request do not prompt the user for code entry. 176 * 177 * @return {@code true} if all the defined workflows do not require a code or {@code false} 178 * if at least one of the contact methods involves a code being sent to the user. 179 */ 180 @JsonIgnore 181 public boolean isCodeless() { 182 return workflows.stream().allMatch(type -> 183 type instanceof WhatsappCodelessWorkflow || 184 type instanceof SilentAuthWorkflow 185 ); 186 } 187 188 /** 189 * Entry point for constructing an instance of this class. 190 * 191 * @return A new Builder. 192 */ 193 public static Builder builder() { 194 return new Builder(); 195 } 196 197 public static final class Builder { 198 Boolean fraudCheck; 199 String brand, code, clientRef; 200 Integer timeout, codeLength; 201 Locale locale; 202 List<Workflow> workflows = new ArrayList<>(1); 203 204 private Builder() {} 205 206 /** 207 * (REQUIRED) 208 * Workflows are a sequence of actions that Vonage use to reach the user you wish to verify with a PIN code. 209 * Each workflow represents a contact method - typically, the channel and number. A verification request must 210 * define at least one workflow. The order in which they are defined is the order in which they will be 211 * attempted, until one is successful. The first one you add will be the preferred contact method. Any 212 * subsequent ones act as a fallback / backup. 213 * 214 * @param workflow The workflow to add to the list. 215 * 216 * @return This builder. 217 */ 218 public Builder addWorkflow(Workflow workflow) { 219 workflows.add(Objects.requireNonNull(workflow, "Workflow cannot be null")); 220 return this; 221 } 222 223 /** 224 * (REQUIRED if {@linkplain #addWorkflow(Workflow)}) has not been called. 225 * Workflows are a sequence of actions that Vonage use to reach the user you wish to verify with a PIN code. 226 * Each workflow represents a contact method - typically, the channel and number. A verification request must 227 * define at least one workflow. The order in which they are defined is the order in which they will be 228 * attempted, until one is successful. The first one you add will be the preferred contact method. Any 229 * subsequent ones act as a fallback / backup. 230 * 231 * @param workflows The workflows to use.This will replace the existing list. 232 * 233 * @return This builder. 234 */ 235 public Builder workflows(List<Workflow> workflows) { 236 this.workflows.clear(); 237 this.workflows.addAll(Objects.requireNonNull(workflows, "Workflows must not be null.")); 238 return this; 239 } 240 241 /** 242 * (REQUIRED) 243 * The brand that is sending the verification request. 244 * This is what the user will see when they receive the notification. 245 * 246 * @param brand The brand name. 247 * 248 * @return This builder. 249 */ 250 public Builder brand(String brand) { 251 this.brand = brand; 252 return this; 253 } 254 255 /** 256 * (OPTIONAL) 257 * An optional alphanumeric custom code to use, if you don't want Vonage to generate the code. Must be 258 * between 4 and 10 alphanumeric characters. This is not used for Silent Auth or Whatsapp Interactive. 259 * 260 * @param code The code to use as a string. 261 * 262 * @return This builder. 263 */ 264 public Builder code(String code) { 265 this.code = code; 266 return this; 267 } 268 269 /** 270 * (OPTIONAL) 271 * Length of the code to send to the user, must be between 4 and 10 (inclusive). 272 * This is not used for Silent Auth or Whatsapp Interactive. 273 * 274 * @param codeLength The verification code length. 275 * 276 * @return This builder. 277 */ 278 public Builder codeLength(int codeLength) { 279 this.codeLength = codeLength; 280 return this; 281 } 282 283 /** 284 * (OPTIONAL) 285 * Specifies the wait time in seconds between attempts to delivery the verification code 286 * between workflows. Must be between 60 and 900. Default is 300. 287 * 288 * @param timeout The delivery timeout in seconds. 289 * 290 * @return This builder. 291 */ 292 public Builder channelTimeout(int timeout) { 293 this.timeout = timeout; 294 return this; 295 } 296 297 /** 298 * (OPTIONAL) 299 * Set the language that this request will be delivered in. Refer to 300 * <a href=https://developer.vonage.com/en/verify/guides/verify-v2-languages>the documentation</a> 301 * for a list of supported locales. 302 * 303 * @param locale The language locale. 304 * 305 * @return This builder. 306 * 307 * @since 8.0.0 308 */ 309 public Builder locale(Locale locale) { 310 if (locale == null || locale.toString().isEmpty()) { 311 throw new IllegalArgumentException("Invalid locale"); 312 } 313 this.locale = locale; 314 return this; 315 } 316 317 /** 318 * (OPTIONAL) 319 * Set the language that this request will be delivered in. 320 * 321 * @param locale The language locale as a string. This should be a 322 * <a href=https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes>ISO 639-1 code</a>. 323 * 324 * @return This builder. 325 * 326 * @since 8.0.0 327 * @see #locale(Locale) 328 */ 329 public Builder locale(String locale) { 330 return locale(Locale.forLanguageTag(locale)); 331 } 332 333 /** 334 * (OPTIONAL) 335 * If this reference is set when the request is sent, it will be included in the callbacks. 336 * 337 * @param clientRef The callback reference for this request. 338 * 339 * @return This builder. 340 */ 341 public Builder clientRef(String clientRef) { 342 this.clientRef = clientRef; 343 return this; 344 } 345 346 /** 347 * (OPTIONAL) 348 * Set this parameter to {@code false} to force through the request even if it's 349 * blocked by the network's fraud protection. Refer to 350 * <a href=https://developer.vonage.com/en/verify/verify-v2/guides/v2-anti-fraud> 351 * the documentation</a> for details. This feature must be enabled on your account to take effect. 352 * 353 * @param fraudCheck Whether to enforce network block. Default is {@code true}. 354 * Set to {@code false} to bypass a network block for this request. 355 * 356 * @return This builder. 357 */ 358 public Builder fraudCheck(boolean fraudCheck) { 359 this.fraudCheck = fraudCheck; 360 return this; 361 } 362 363 /** 364 * Bypasses the network block used for fraud check. See {@link #fraudCheck(boolean)}. 365 * 366 * @return This builder. 367 * @see #fraudCheck(boolean) 368 */ 369 public Builder fraudCheck() { 370 return fraudCheck(false); 371 } 372 373 /** 374 * Constructs a VerificationRequest with this builder's properties. 375 * 376 * @return A new VerificationRequest instance. 377 */ 378 public VerificationRequest build() { 379 return new VerificationRequest(this); 380 } 381 } 382}