Troubleshooting

reCAPTCHA Token Sona Erme Tarihi: Zamanlama Pencereleri ve Yarış Koşulları

Token'ın ömrü 120 saniyedir ve sayaç, token'ı aldığınız an değil, çözücünün onu ürettiği an başlar. timeout-or-duplicate görüyorsanız token ya çok geç gönderilmiştir ya da ikinci kez kullanılmıştır.

Türkiye'deki ekiplerin çoğu bu hatayla e-ticaret ödeme akışının QA otomasyonunda tanışıyor: CAPTCHA ilk ekranda çözülüyor, gönderim üçüncü ekrana kalıyor, gece koşusu rastgele kırmızıya dönüyor. Sebep "flaky test" değil, zamanlama bütçesidir.

Kısa cevap:

  • Token 120 saniye sonra sunucu tarafında geçersizleşir; bu sınır uzatılamaz.
  • Aynı token ikinci kez gönderilirse, süresi dolmamış olsa bile aynı hatayı alırsınız.
  • Ölçmeniz gereken tek metrik vardır: gönderim anındaki token yaşı.

Önce hızlı teşhis tablosu

Akışınızı önce bu tabloya göre gözden geçirin; çoğu vaka ilk iki satırda çözülür.

Konu Öneri
Çözüm–gönderim arası 90 saniyenin altında tutun (30 saniyelik güvenlik payı)
Sorgulama aralığı res.php kontrolleri arasında 5 saniye
Erken çözüm Yalnızca gönderimin 60 saniye içinde olacağından eminseniz
Süre dolduğunda timeout-or-duplicate görünce her zaman yeni token isteyin
Token kuyruğu Token'ları kuyruğa almayın; her birinin süresi bağımsız işler
Paralel işler Görev başına çözün, hepsini toplayıp sonra kullanmayın
İzleme Gönderim anındaki token yaşını metrik olarak kaydedin

Aynı mantık diğer CAPTCHA türlerinde de geçerlidir; yalnızca token alanının adı değişir.


Token zamanlama bütçeniz: 120 saniye nasıl harcanır

Her token üretiminden tam 120 saniye sonra geçersiz olur; sınır tüm sürümlerde aynıdır ve Google tarafında uygulanır.

Token generated (solve complete)
    ├─── 0s:   Token is valid ✓
    ├─── 60s:  Token is valid ✓
    ├─── 110s: Token is valid ✓  (but cutting it close)
    ├─── 119s: Token is valid ✓  (dangerous territory)
    └─── 120s: Token EXPIRED ✗  (timeout-or-duplicate)

Sayaç hangi anda başlar

Senaryo Sayaç şu anda başlar...
reCAPTCHA v2 onay kutusu Kullanıcı onay kutusunu işaretler (doğrulama tamamlanır)
reCAPTCHA v2 görsel ızgarası Kullanıcı son görsel seçim turunu bitirir
görünmez reCAPTCHA (v2) execute() callback'i token ile tetiklenir
reCAPTCHA v3 execute() Promise'i token ile çözümlenir
API çözücü (CaptchaAI) Çözücü token'ı üretir (siz aldığınızda DEĞİL)

Son satır en çok gözden kaçanıdır: token size ulaşana kadar sayaç işler.

Çözücü ile kodunuz arasındaki gecikme

Solver generates token (timer starts)
    ↓ ~1-5 seconds (network + polling interval)
Your code receives token via res.php poll
    ↓ You now have ~115-119 seconds remaining
Your code processes and submits token
    ↓ ~1-10 seconds (depends on your workflow)
Target website validates token with Google
    ↓ ~1-2 seconds (Google API response time)
Total remaining after validation: ~105-117 seconds (comfortable)

Pencere, token'ı alır almaz gönderen akışlar için fazlasıyla geniştir. Daralma dört durumda başlar:

  • Token'ı aldıktan sonra gönderime kadar ek işlem adımlarınız var
  • Gönderimden önce birden fazla formun doldurulması gerekiyor
  • Hedef sitenin yanıt süresi yavaş
  • Token'ları kuyruğa alıp daha sonra gönderiyorsunuz

Sorgulama aralığı da bu bütçeden yer kapar: res.php kontrolleri 5 saniyede birse gerçekte ~115 saniyeniz vardır.


Süreyi tüketen üç yarış koşulu

1. Paralel çözüm, sıralı gönderim

Toplu koşuların en sık deseni: hepsi aynı anda çözülür, tek tek gönderilir.

# WRONG: Solving multiple CAPTCHAs in parallel, then submitting sequentially
tokens = []
for url in urls:
    task_id = solve_captcha(url)  # All submitted at t=0
    tokens.append(task_id)

# All tokens arrive around t=30
solved_tokens = [poll_result(tid) for tid in tokens]

# Sequential submission: first token at t=32, last at t=120+
for i, (url, token) in enumerate(zip(urls, solved_tokens)):
    submit_form(url, token)  # Later tokens may be expired!
    time.sleep(10)  # Each wait adds pressure

Düzeltme: Bir sonraki URL'ye başlamadan önce her CAPTCHA'yı çözün ve gönderin.

# CORRECT: Solve and submit one at a time
for url in urls:
    token = solve_and_wait(url)  # Token received at t=30
    submit_form(url, token)      # Submitted at t=31 (89 seconds remaining)

Bu düzeltme koşuyu yavaşlatmaz; yalnızca aradaki mesafeyi kısaltır.

2. Token'ı erkenden çözmek

# WRONG: Pre-fetching tokens before knowing when they'll be used
token = solve_captcha()  # Token received at t=0

# ... user fills out form (30-120+ seconds) ...
# ... validation checks ...
# ... other processing ...

submit_form(token)  # Token may be expired!

Düzeltme: CAPTCHA çözümünü gönderimden önceki son adıma taşıyın.

# CORRECT: Late-bind the CAPTCHA solve
prepare_form_data()   # Do everything that doesn't need the token
validate_inputs()     # Run validation before spending a token

# Now solve and submit immediately
token = solve_captcha()  # Token received at t=0
submit_form(token)       # Submitted at t=1 (119 seconds remaining)
  • Token gerektirmeyen her işi önce bitirin
  • Doğrulamaları token harcamadan çalıştırın

3. Çok adımlı form akışı

Yukarıdaki QA senaryosunun kod karşılığı: token adım 1'de alınır, gönderim adım 3'tedir.

# PROBLEMATIC: Multi-step form where CAPTCHA is on step 1 but submit is step 3
token = solve_captcha()       # t=0: Token received

fill_step_1(token)            # t=5: Step 1 submitted
response = fill_step_2()      # t=15: Step 2 completed
# ... step 2 has additional verification ...
wait_for_verification()       # t=60: Verification complete
fill_step_3_and_submit()      # t=65: Final submission (55 seconds remaining - OK)
# BUT if step 2 takes longer than expected...

Düzeltme: Token yaşını ölçün ve eşiği aşarsa yeniden çözün.

token_received_at = time.time()
token = solve_captcha()
token_received_at = time.time()

# ... multi-step process ...

# Before final submission, check token age
token_age = time.time() - token_received_at
if token_age > 100:  # 20-second safety margin
    print(f"Token is {token_age:.0f}s old — requesting fresh token")
    token = solve_captcha()
    token_received_at = time.time()

submit_final(token)

Eşiği 100 saniyede bırakmak zorunda değilsiniz; payı p95 yanıt süresine göre seçin.


Token zamanlama yöneticisi

Aşağıdaki sınıf token yaşını tek yerde toplar; gönderim öncesi use_token() çağırın.

import time
import requests

class TokenTimingManager:
    """Manage reCAPTCHA token timing to prevent expiration errors."""

    API_KEY = "YOUR_API_KEY"
    TOKEN_LIFETIME = 120
    SAFETY_MARGIN = 15  # seconds before expiry to consider "stale"

    def __init__(self, site_key, page_url, version="v2"):
        self.site_key = site_key
        self.page_url = page_url
        self.version = version
        self.current_token = None
        self.token_timestamp = None

    def _solve(self):
        """Request and poll for a new token."""
        params = {
            "key": self.API_KEY,
            "method": "userrecaptcha",
            "googlekey": self.site_key,
            "pageurl": self.page_url,
            "json": 1,
        }
        if self.version == "v3":
            params.update({"version": "v3", "action": "submit"})

        submit = requests.post("https://ocr.captchaai.com/in.php", data=params).json()
        task_id = submit["request"]

        for _ in range(60):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.API_KEY,
                "action": "get",
                "id": task_id,
                "json": 1,
            }).json()

            if result.get("status") == 1:
                self.current_token = result["request"]
                self.token_timestamp = time.time()
                return self.current_token

        raise TimeoutError("Token solve timeout")

    @property
    def token_age(self):
        """Seconds since current token was received."""
        if self.token_timestamp is None:
            return float("inf")
        return time.time() - self.token_timestamp

    @property
    def token_remaining(self):
        """Seconds remaining before token expires."""
        return max(0, self.TOKEN_LIFETIME - self.token_age)

    @property
    def is_fresh(self):
        """Whether the token is fresh enough to use."""
        return self.token_remaining > self.SAFETY_MARGIN

    def get_token(self):
        """Get a valid token, solving if current is stale or missing."""
        if self.current_token and self.is_fresh:
            return self.current_token

        return self._solve()

    def use_token(self):
        """Get and consume a token (cannot be reused)."""
        token = self.get_token()
        # Mark as consumed
        self.current_token = None
        self.token_timestamp = None
        return token

# Usage
manager = TokenTimingManager(
    site_key="6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp",
    page_url="https://staging.example.com/qa-login",
)

# Get a fresh token right before submission
token = manager.use_token()
print(f"Token remaining: {manager.TOKEN_LIFETIME}s (fresh solve)")

# Submit form with token...

SAFETY_MARGIN değerini ortamınıza göre ayarlayın: hedef sitenin p95 yanıt süresi yüksekse 15 yerine 25–30 saniye daha güvenlidir.

  • token_age gönderim anında loglanacak metriktir
  • use_token() token'ı tüketir; ikinci gönderim aynı token'ı kullanamaz

timeout-or-duplicate geldiğinde ne yapılır

Yeniden denemeden önce token yaşına bakın:

def handle_recaptcha_error(error_codes, token_age_seconds):
    """Diagnose and handle reCAPTCHA validation errors."""

    if "timeout-or-duplicate" in error_codes:
        if token_age_seconds > 120:
            return {
                "cause": "Token expired (age: {:.0f}s > 120s)".format(token_age_seconds),
                "fix": "Reduce time between receiving and submitting token",
                "action": "re-solve",
            }
        elif token_age_seconds < 5:
            return {
                "cause": "Token likely reused (duplicate submission)",
                "fix": "Ensure each form submission gets a unique token",
                "action": "re-solve",
            }
        else:
            return {
                "cause": "Token may have been reused or server-side timing issue",
                "fix": "Check for double-submit in form handler",
                "action": "re-solve",
            }

    if "invalid-input-response" in error_codes:
        return {
            "cause": "Token is malformed or corrupted",
            "fix": "Check token transmission (URL encoding, field name)",
            "action": "re-solve",
        }

    return {"cause": "Unknown", "action": "investigate"}

Üç durumda da eylem aynıdır — yeni token isteyin — ama nedenler farklıdır: 120 saniyeyi aşan yaş süre dolumunu, 5 saniyenin altı çift gönderimi işaret eder. Bunları ayrı metrik olarak toplarsanız darboğazı tek koşuda görürsünüz.


Sık sorulan sorular

Token yaşını üretimde nasıl loglamalıyım?

Token'ı aldığınız anın zaman damgasını isteğin bağlamına yazın, gönderim yanıtıyla farkı kaydedin. timeout-or-duplicate oranını token yaşı dağılımıyla görürseniz payı veriyle seçersiniz.

Aynı token'ı iki farklı isteğe gönderebilir miyim?

Hayır. Token tek kullanımlıktır; ikinci gönderim, süresi dolmamış olsa bile timeout-or-duplicate döner. Yeniden deneme mantığınız eski token'ı değil, yeni bir çözüm istemelidir.

Yavaş bir hedef site 120 saniyeyi tüketebilir mi?

Evet. Gönderim isteğinizin kendisi de bu bütçenin içindedir. Yanıt süresi saniyelerle ölçülüyorsa payı artırın ve token'ı form verisi hazır olduktan sonra çözün.

CaptchaAI planı çözüm süresini etkiler mi?

Plan, eşzamanlı thread sayısını belirler; tek bir çözümün süresini değil. BASIC ($15/ay, 5 thread) ile beş CAPTCHA aynı anda çözülür, altıncı istek sırada bekler. Bu bekleme token yaşına değil, toplam süreye eklenir.

Zaman aşımı hatalarını KVKK kapsamındaki bir akışta nasıl ele almalıyım?

Loglara token ve istek meta verisini yazın, form içeriğindeki kişisel veriyi değil. Yetkili QA akışlarında token yaşı ile hata kodu yeterlidir.


Özet

Token 120 saniye yaşar ve bir kez kullanılır; timeout-or-duplicate bu iki kuraldan birinin çiğnenmesinden doğar. CaptchaAI ile çalışırken:

  • Token'ı gönderimden hemen önce çözün
  • Gönderim anındaki yaşını ölçün ve loglayın
  • Token'ları kuyruğa almayın, ikinci kez göndermeyin

İlgili Makaleler

Bu makale için yorumlar devre dışı bırakılmıştır.