Используйте Go SDK в обработчике Gin
Используйте один общий клиент solvethree и передавайте в Solve дочерний контекст c.Request.Context() с заданным предельным сроком. Храните конфигурацию целевого ресурса и API-ключ на сервере, а перед созданием задачи проверяйте аутентификацию вызывающей приложение стороны.
На этой странице
Установите зависимости и настройте целевой ресурс
- 1.
Создайте модуль Go и установите зависимости
Для текущей версии Gin используйте Go 1.25 или новее. Сам SDK solvethree требует Go 1.22+. Инициализируйте модуль приложения командой
go mod init, указав собственный путь модуля. Установите Gin по краткому руководству, затем добавьтеsolvethree.com/[email protected]с помощьюgo get. Официальный SDK распространяется через публичный прокси модулей, размещённый на домене; он предоставляется с solvethree.com. - 2.
Задайте серверные учётные данные
Установите
SOLVETHREE_API_KEYв значение API-ключа рабочего пространства, аINTEGRATION_KEY— в значение секрета, который внутреннее приложение будет отправлять вX-Integration-Key. Это разные учётные данные: вызывающей приложение стороне не нужен API-ключ solvethree. - 3.
Настройте фиксированный целевой ресурс
Установите
CAPTCHA_SITEв значение имени целевого хоста, аSOLVETHREE_PROXY— в значение прокси, используемого для решения. Сначала создайте активный профиль сайта в рабочем пространстве; см. настройку профиля сайта. В примереActionзафиксировано какlogin, поэтому измените это значение, если ваша интеграция использует другое действие. Задание имени хоста и действия не создаёт профиль.
Добавьте маршрут Gin с аутентификацией
Клиент создаётся один раз и безопасен для параллельных запросов. Маршрут проверяет учётные данные внутреннего приложения перед вызовом solvethree. Он принимает ID запроса от вызывающей стороны, но не произвольный целевой ресурс или прокси.
package main
import (
"context"
"errors"
"log"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
solvethree "solvethree.com/sdk"
)
func main() {
client,err:=solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"),nil)
if err!=nil { log.Fatal(err) }
secret:=os.Getenv("INTEGRATION_KEY")
if secret=="" { log.Fatal("Set INTEGRATION_KEY") }
router:=gin.New()
router.Use(gin.Recovery())
router.POST("/captcha/solve",func(c *gin.Context){
if c.GetHeader("X-Integration-Key")!=secret { c.Status(http.StatusUnauthorized);return }
input:=solvethree.TaskRequest{Site:os.Getenv("CAPTCHA_SITE"),Action:"login",
Proxy:os.Getenv("SOLVETHREE_PROXY"),RequestID:c.GetHeader("X-Request-ID")}
ctx,cancel:=context.WithTimeout(c.Request.Context(),180*time.Second);defer cancel()
result,err:=client.Solve(ctx,input,solvethree.SolveOptions{})
if err!=nil {
var failed *solvethree.TaskFailedError
var recovery *solvethree.Error
if errors.As(err,&failed) { c.JSON(502,gin.H{"taskId":failed.TaskID,"errorCode":failed.ErrorCode});return }
if errors.As(err,&recovery) { c.JSON(502,gin.H{"requestId":recovery.RequestID,"taskId":recovery.TaskID});return }
c.JSON(400,gin.H{"error":"Invalid task input"});return
}
// Return private solution data only to the authorized application caller.
c.JSON(200,result)
})
log.Fatal(router.Run("127.0.0.1:3000"))
}В задаче задаются Site, Action и один Proxy. Поля Type и URL остаются незаданными, как и требуется для reCAPTCHA, и используется режим Enterprise v3 по умолчанию.
Явно заданный предельный срок контекста ограничивает ожидание обработчика 180 секундами; отключение клиента может отменить его раньше. HTTP-сервер и любой обратный прокси должны допускать такую длительность запроса. Если это невозможно, используйте отдельные операции создания и получения вместо удержания этого синхронного маршрута открытым.
Слушатель привязывается к loopback-интерфейсу. Интегрируйте маршрут с существующими механизмами внутренней аутентификации и развёртывания приложения; не открывайте доступ к платной конечной точке решения без аутентификации вызывающей стороны.
Обрабатывайте ошибки, не раскрывая секреты
Обработчик использует errors.As, а не сравнение строк ошибок. Конечная ошибка TaskFailedError приводит к ответу 502 с ID задачи и кодом ошибки. Другие обёрнутые ошибки SDK приводят к ответу 502 с идентификаторами для восстановления. В остальных случаях возвращается фиксированное сообщение 400, чтобы не раскрывать исходные ошибки, учётные данные или URL прокси.
Не все ответы 502 означают, что решение не удалось. Используйте errors.Is, чтобы различать отмену и истечение срока ожидания. Для обработки на уровне HTTP функция errors.As позволяет извлечь вложенный APIError с полями StatusCode и RetryAfter. Учитывайте это различие во внутренней диагностике, а не считайте любую ошибку поводом создать новую задачу.
Задачу, завершившуюся ошибкой, больше не следует опрашивать. Получите её, если нужен текст ошибки, затем устраните причину, прежде чем запускать новое решение. Средства за неудачные решения возвращаются.
Сохраняйте идентификаторы при тайм-ауте запроса
Отмена прекращает ожидание, но не удалённую работу. Пример возвращает идентификаторы для восстановления, когда это возможно, но не сохраняет их. Если вызывающая сторона отключится, этот JSON-ответ может так и не дойти.
- 1.
Назначайте идентификатор запроса до вызова маршрута
Внутренний вызывающий компонент должен сохранять постоянный
X-Request-IDдля каждой логической операции решения. Допустимые идентификаторы содержат 1–64 ASCII-буквы, цифры, символы подчёркивания или дефисы. SDK генерирует идентификатор, если он отсутствует, но предварительное назначение позволяет восстановить задачу даже при потере ответа. - 2.
Сохраняйте исходные параметры задачи без изменений
Сохраняйте имя хоста, действие, прокси и идентификатор запроса в защищённом хранилище приложения. При повторной попытке после изменения конфигурации окружения исходные параметры задачи не должны незаметно подменяться другими.
- 3.
Восстанавливайте задачу, прежде чем создавать новую
Если идентификатор задачи известен, вызовите
GetTaskс новым контекстом с ограниченным сроком действия. Если нет, повторно отправьте исходную задачу черезCreateTaskс тем же идентификатором запроса и параметрами. Это вернёт существующую задачу; другие параметры с тем же идентификатором вызовут конфликт. Продолжайте получать задачу только пока она ожидает обработки или решается.
Solve создаёт задачу один раз и повторяет только запросы опроса, получившие временные HTTP-ответы со статусами 429, 502, 503 или 504, соблюдая Retry-After в пределах срока действия контекста. CreateTask никогда не повторяет POST автоматически, поэтому при тайм-ауте создания всё равно нужно восстановление, а не новый идентификатор запроса.
Отправляйте токен с возвращёнными идентификационными данными
Успешный ответ обработчика содержит конфиденциальные данные решения для авторизованного вызывающего компонента приложения. Используйте в запросе к целевому сайту Solution.Token, а не идентификатор задачи. Используйте возвращённые UserAgent и SecChUA, а прокси, с которым решение удалось, получите через SuccessfulProxy(originalTask, readyResult).
SDK ничего не отправляет на целевой сайт. Готовая задача означает, что решение сгенерировано, а не что сайт его принял. Проверяйте ответ целевого сайта отдельно.
Источники и справочник API
Запустите первую задачу.
Начните с $5 на балансе рабочего пространства.