FriendlyCaptchaV1Client.java
package org.drjekyll.friendlycaptcha;
import static org.drjekyll.friendlycaptcha.StringUtil.isEmpty;
import java.io.InputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jspecify.annotations.NonNull;
/** Verifies a Friendly Captcha solution using the v1 API. */
@RequiredArgsConstructor
@Slf4j
class FriendlyCaptchaV1Client implements FriendlyCaptchaClient {
private static final URI DEFAULT_ENDPOINT =
URI.create("https://api.friendlycaptcha.com/api/v1/siteverify");
private final FriendlyCaptchaParams friendlyCaptchaParams;
private final VerificationResponseReader verificationResponseReader;
@Override
public URI getDefaultEndpoint() {
return DEFAULT_ENDPOINT;
}
@Override
public String buildRequestBody(@NonNull String solution) {
String entity =
"solution="
+ URLEncoder.encode(solution, StandardCharsets.UTF_8)
+ "&secret="
+ URLEncoder.encode(friendlyCaptchaParams.getApiKey(), StandardCharsets.UTF_8);
if (!isEmpty(friendlyCaptchaParams.getSitekey())) {
entity +=
"&sitekey="
+ URLEncoder.encode(friendlyCaptchaParams.getSitekey(), StandardCharsets.UTF_8);
}
return entity;
}
@Override
public boolean processResponse(int statusCode, InputStream inputStream) {
VerificationResponseV1 response =
verificationResponseReader.readResponse(inputStream, VerificationResponseV1.class);
if (statusCode == 200) {
return response.isSuccess();
}
log.warn("Received error response: {}", response);
Collection<ErrorCode> errors = response.getErrors();
if (errors == null || errors.isEmpty()) {
throw new FriendlyCaptchaException("Verification API returned error status", statusCode);
}
ErrorCode errorCode = errors.iterator().next();
String message =
isEmpty(response.getDetails()) ? errorCode.getDescription() : response.getDetails();
throw new FriendlyCaptchaException(message, statusCode, errorCode);
}
}