Usa el SDK de Go en un controlador de Gin

Por solvethree5 min de lectura

Usa un único cliente compartido de solvethree y pasa a Solve un contexto hijo de c.Request.Context() con un plazo límite. Mantén la configuración del destino y la clave de API en el servidor y autentica a quien llama a la aplicación antes de crear una tarea.

En esta página

Instala las dependencias y configura el destino

  1. 1.

    Crea el módulo de Go e instala las dependencias

    Usa Go 1.25 o posterior para la versión actual de Gin. El propio SDK de solvethree requiere Go 1.22+. Inicializa el módulo de tu aplicación con go mod init, usando tu propia ruta de módulo. Instala Gin siguiendo su guía de inicio rápido y, después, usa go get para añadir solvethree.com/[email protected]. El SDK oficial se distribuye a través del proxy público de módulos alojado en el dominio; se sirve desde solvethree.com.

  2. 2.

    Configura las credenciales del servidor

    Establece SOLVETHREE_API_KEY con la clave de API de tu espacio de trabajo e INTEGRATION_KEY con el secreto que tu aplicación interna enviará en X-Integration-Key. Son credenciales independientes: quien llama a la aplicación no necesita la clave de API de solvethree.

  3. 3.

    Configura el destino fijo

    Establece CAPTCHA_SITE con el nombre de host de destino y SOLVETHREE_PROXY con el proxy usado para la resolución. Crea primero un perfil de sitio activo en el espacio de trabajo; consulta la configuración del perfil del sitio. El ejemplo fija Action en login, así que cambia ese valor si tu integración usa otra acción. Establecer el nombre de host y la acción no crea un perfil.

Añade la ruta autenticada de Gin

El cliente se crea una sola vez y admite solicitudes concurrentes de forma segura. La ruta comprueba la credencial de la aplicación interna antes de llamar a solvethree. Acepta un ID de solicitud de quien realiza la llamada, pero no un destino ni un proxy arbitrarios.

Gin · resolución con límites mediante el SDK oficial de Go
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"))
}

La tarea proporciona Site, Action y un único Proxy. Deja Type y URL sin establecer, como requiere reCAPTCHA, y usa el modo Enterprise v3 predeterminado.

El plazo límite explícito del contexto limita la espera del manejador a 180 segundos; la desconexión del cliente puede cancelarla antes. El servidor HTTP y cualquier proxy inverso deben permitir esa duración de solicitud. Si no pueden, usa operaciones separadas de creación y consulta en lugar de mantener abierta esta ruta síncrona.

El listener se vincula a la interfaz de bucle local. Integra la ruta con la configuración existente de autenticación interna y despliegue de tu aplicación; no expongas un endpoint de resolución de pago sin autenticar a quien realiza la llamada.

Gestiona los errores sin exponer secretos

El manejador usa errors.As en lugar de comparar cadenas de error. Un TaskFailedError terminal produce una respuesta 502 que contiene el ID de tarea y el código de error. Otros errores encapsulados del SDK producen una respuesta 502 que contiene los ID de recuperación. Como alternativa final, devuelve un mensaje 400 fijo en lugar de exponer errores sin procesar, credenciales o URL de proxy.

No todas esas respuestas 502 significan que la resolución haya fallado. Usa errors.Is para distinguir entre la cancelación y el vencimiento del plazo. Para gestionar casos específicos de HTTP, errors.As puede extraer un APIError anidado con StatusCode y RetryAfter. Mantén esa distinción en los diagnósticos internos en lugar de tratar cada error como un motivo para crear otra tarea.

No vuelvas a consultar periódicamente una tarea que haya terminado con un fallo. Recupérala si necesitas el texto del error y corrige la causa antes de iniciar otra resolución. Las resoluciones fallidas se reembolsan.

Conserva los ID cuando se agote el tiempo de espera de la solicitud

La cancelación detiene la espera, no el trabajo remoto. El ejemplo devuelve los ID de recuperación cuando puede, pero no los guarda de forma persistente. Si el cliente que realiza la llamada se desconecta, es posible que esa respuesta JSON nunca llegue.

  1. 1.

    Asigna el ID de solicitud antes de llamar a la ruta

    Haz que el cliente interno conserve un X-Request-ID estable para cada resolución lógica. Los ID válidos contienen entre 1–64 letras ASCII, números, guiones bajos o guiones. El SDK genera un ID cuando falta, pero asignarlo de antemano permite la recuperación incluso si se pierde la respuesta.

  2. 2.

    Conserva los datos de entrada exactos de la tarea

    Guarda el nombre de host, la acción, el proxy y el ID de solicitud en un almacenamiento protegido de la aplicación. Un reintento tras un cambio en la configuración del entorno no debe sustituir silenciosamente los datos de entrada de la tarea por otros distintos.

  3. 3.

    Recupera la tarea antes de crear trabajo nuevo

    Si conoces el ID de tarea, llama a GetTask con un contexto nuevo con un límite de tiempo. Si no lo conoces, vuelve a enviar la tarea original mediante CreateTask con el mismo ID de solicitud y los mismos datos de entrada. Esto devuelve la tarea existente; usar datos distintos con el mismo ID provoca un conflicto. Sigue consultándola solo mientras esté pendiente o en proceso de resolución.

Solve crea la tarea una sola vez y reintenta únicamente las consultas con respuestas temporales de estado HTTP 429, 502, 503 o 504, respetando Retry-After dentro del plazo del contexto. CreateTask nunca reintenta POST automáticamente, por lo que un tiempo de espera agotado durante la creación sigue requiriendo recuperación, no un nuevo ID de solicitud.

Envía el token con la identidad devuelta

Una respuesta correcta del manejador contiene datos privados de la solución para el cliente autorizado de la aplicación. Usa Solution.Token, no el ID de tarea, en la solicitud al destino. Usa los valores devueltos de UserAgent y SecChUA, y obtén el proxy que funcionó con SuccessfulProxy(originalTask, readyResult).

El SDK no envía nada al destino. Una tarea lista significa que se ha generado una solución, no que el sitio web la haya aceptado. Comprueba la respuesta del destino por separado.

Ejecuta tu primera tarea.

Empieza con $5 de crédito en tu espacio de trabajo.

Crear un espacio de trabajo
Usa el SDK de Go en un controlador de Gin · solvethree