CoinGuard Docs

Anti-bot verification API · v1

CoinGuard manush ar bot alada kore. Ekta chhobi te koyekta number thake — user chhoto theke boro tap kore. Shothik uttor kokhono browser e jay na, tai bot chhobi na poRe pass korte pare na.

Ei page e

Kibhabe kaj kore

Duita call. Duitai tomar server theke, browser theke na.

1
Browser page kholе
Tomar Laravel app page render kore.
2
Tomar server → CoinGuard
POST /cg/challenge — site key, user er UA ar IP pathao. Ferot pai ekta id ar base64 chhobi.
3
User tap kore
Chhobi te chhoto theke boro tap. Browser shudhu coordinate jomay — "60,75|39,159|..." — kono uttor jane na.
4
Form submit
Coordinate gulo tomar server e ashe.
5
Tomar server → CoinGuard
POST /cg/verify — secret key soho. Ferot pai {"success":true} ba error.
6
Tumi siddhanto nao
success true na hole reward dio na.

Shobcheye boro niyom: verify call tomar server theke hote hobe, secret key diye. Browser theke korle bot nijei call kore result upekkha korbe — pura system ortho-hin hoye jabe.

Ki ki lagbe

PHP 8.1+
Laravel 10 / 11 / 12 / 13 — shob choley
Site key + Secret key
Admin panel theke toiri korte hobe
Server theke outgoing HTTPS
Tomar server jate https://samzune.com e call korte pare
jQuery lagbe na
Sada JS diyei kaj hoy

Key gulo

Protita site er duita key thake. Ei page e asol key dekhano hoy na — Panel → Sites theke nao.

demo Niche jегulo dekhacchi shudhu dekhanor jonno — kaj korbe na
site key browser e jete pare
cg_site_1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d
secret key shudhu server er .env e — kokhono browser e na
cg_secret_9f8e7d6c5b4a39281706f5e4d3c2b1a09f8e7d6c5b4a3928

Notun key banate: Panel → Sites → + New site. Secret ek baroi dekhabe — sathe sathe kopi koro.

Secret harale ba faas hole: Panel → Sites → Rotate secret. Purono ta sathe sathe ojoggo hoye jabe.

1

.env ar config

Client project er .env file er shesh e jog koro:

CG_URL=https://samzune.com
CG_SITE=cg_site_...
CG_SECRET=cg_secret_...

Tarpor config/services.php er array te jog koro:

'coinguard' => [
    'url'    => env('CG_URL'),
    'site'   => env('CG_SITE'),
    'secret' => env('CG_SECRET'),
],

Sheshe: php artisan config:clear

2

Service class

Ei file ta banao — app/Services/CoinGuard.php. Eta poroborti shob step e byabohar hobe.

<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;

class CoinGuard
{
    private string $url;
    private string $site;
    private string $secret;

    public function __construct()
    {
        $this->url    = rtrim((string) config('services.coinguard.url'), '/');
        $this->site   = (string) config('services.coinguard.site');
        $this->secret = (string) config('services.coinguard.secret');
    }

    /**
     * Notun challenge ano - page load er shomoy dako.
     * Ferot dey ['id' => ..., 'image' => ..., 'taps' => ...] ba null.
     */
    public function challenge(): ?array
    {
        $r = request();

        try {
            $res = Http::asForm()->timeout(10)->post($this->url . '/cg/challenge', [
                'site' => $this->site,
                'ua'   => (string) $r->userAgent(),
                'ip'   => (string) $r->ip(),
                'host' => (string) $r->getHost(),
            ]);
        } catch (\Throwable $e) {
            report($e);
            return null;
        }

        $j = $res->json();
        if (!is_array($j) || empty($j['success'])) {
            return null;
        }

        return [
            'id'    => (string) $j['id'],
            'image' => (string) $j['image'],
            'taps'  => (int) $j['taps'],
        ];
    }

    /**
     * Form submit er por dako. true hole manush.
     * $id   = hidden field cg_id
     * $taps = hidden field cg_taps
     */
    public function verify(?string $id, ?string $taps): bool
    {
        if (empty($id) || empty($taps)) {
            return false;
        }

        $r = request();

        try {
            $res = Http::asForm()->timeout(10)->post($this->url . '/cg/verify', [
                'site'   => $this->site,
                'secret' => $this->secret,
                'id'     => $id,
                'taps'   => $taps,
                'ua'     => (string) $r->userAgent(),
                'ip'     => (string) $r->ip(),
            ]);
        } catch (\Throwable $e) {
            report($e);
            return false;
        }

        $j = $res->json();
        return is_array($j) && !empty($j['success']);
    }
}

Khyal koro: network fail hole eta false dey — mane reward dey na. Eta icchakrito: sondeho hole nirapod dike bhanga bhalo.

3

Controller e use

Duijaygay: page dekhanor shomoy challenge ano, form ashar por verify koro.

<?php

namespace App\Http\Controllers;

use App\Services\CoinGuard;
use Illuminate\Http\Request;

class ClaimController extends Controller
{
    // 1) page dekhai - challenge ene view e pathai
    public function show(CoinGuard $cg)
    {
        return view('claim', [
            'cg' => $cg->challenge(),   // null hole niche dekhano ache ki korte hobe
        ]);
    }

    // 2) form ashe - agey verify, tarpor reward
    public function store(Request $r, CoinGuard $cg)
    {
        if (!$cg->verify($r->input('cg_id'), $r->input('cg_taps'))) {
            return back()->withErrors([
                'cg' => 'Verification hoy nai. Abar chesta koro.',
            ]);
        }

        // ---- ekhan theke tomar nijer logic ----
        // timer check, daily limit, tarpor reward
        // $user->increment('balance', 10);

        return back()->with('ok', 'Reward peye gecho!');
    }
}

Route duita:

Route::get('/claim',  [ClaimController::class, 'show']);
Route::post('/claim', [ClaimController::class, 'store']);

Bhul kora jabe na: verify er por reward — ulto na. Ar reward er logic verify() er bhitore na, oi if block er pore.

4

Blade + JS

resources/views/claim.blade.php — chhobi dekhay, tap dhore, hidden field e bhore.

<form method="POST" action="/claim">
  @csrf

  @if ($cg)
    <p>Chhoto theke boro tap koro</p>

    <div id="cgWrap" style="position:relative;width:320px;max-width:100%;
         border-radius:14px;overflow:hidden;cursor:pointer;touch-action:manipulation">
      <img src="{{ $cg['image'] }}" alt="" draggable="false"
           style="display:block;width:100%;height:auto;pointer-events:none">
    </div>

    <div style="margin-top:10px">
      <span id="cgCount">0 / {{ $cg['taps'] }}</span>
      <span id="cgUndo" style="cursor:pointer;margin-left:12px">undo</span>
    </div>

    <input type="hidden" name="cg_id"   value="{{ $cg['id'] }}">
    <input type="hidden" name="cg_taps" id="cgTaps" value="">

    <button type="submit">Claim</button>
  @else
    <p>Verification service ekhon paoa jacche na. Page refresh koro.</p>
  @endif
</form>

@if ($cg)
<script>
(function () {
  var need = {{ $cg['taps'] }};
  var wrap = document.getElementById('cgWrap');
  var f = document.getElementById('cgTaps');
  var c = document.getElementById('cgCount');
  var taps = [];

  function paint() {
    f.value = taps.join('|');
    c.textContent = taps.length + ' / ' + need;
    wrap.querySelectorAll('.cgdot').forEach(function (d) { d.remove() });
    var r = wrap.getBoundingClientRect();
    taps.forEach(function (t, i) {
      var p = t.split(','), d = document.createElement('span');
      d.className = 'cgdot';
      d.textContent = i + 1;
      d.style.cssText = 'position:absolute;width:26px;height:26px;border-radius:99px;' +
        'background:#10b981;color:#04231a;font:800 13px sans-serif;display:grid;' +
        'place-items:center;transform:translate(-50%,-50%);pointer-events:none;' +
        'left:' + (p[0] * r.width / 320) + 'px;top:' + (p[1] * r.height / 200) + 'px';
      wrap.appendChild(d);
    });
  }

  wrap.addEventListener('click', function (e) {
    if (taps.length >= need) return;
    var r = wrap.getBoundingClientRect();
    var x = Math.round((e.clientX - r.left) * 320 / r.width);
    var y = Math.round((e.clientY - r.top) * 200 / r.height);
    if (x < 0 || y < 0 || x > 320 || y > 200) return;
    taps.push(x + ',' + y);
    paint();
  });

  document.getElementById('cgUndo').addEventListener('click', function () {
    taps.pop(); paint();
  });

  wrap.closest('form').addEventListener('submit', function (e) {
    if (taps.length !== need) { e.preventDefault(); c.textContent = 'shob tap koro' }
  });
})();
</script>
@endif

Keno eibhabe

Coordinate scale kora hoy
Chhobi CSS e chhoto-boro hoy, kintu server 320x200 hisheb kore. Tai bhag kore pathai — na korle shob tap bhul hobe.
pointer-events:none chhobi te
Click event wrap e bosano. Chhobi event gile fellei hisheb golmal.
touch-action:manipulation
Mobile e double-tap zoom er 300ms deri shoray.
Shob tap na hole submit atkay
Na hole "count" error khaye challenge nosto hoy.

AJAX / Alpine / Livewire / Vue

Form jodi sadharon HTML form na hoy — ei ongsho ta MUST poRo.

Shobcheye beshi je bhul ta hoy

Sadharon <form> submit korle hidden input er man APNAAPNI server e chole jay. Kintu fetch(), axios, Alpine, Livewire, Vue, React — ei shob e jay NA.

JS ke hate hate cg_id ar cg_taps request body te dite hobe. Na dile server e khali pouchabe, verify shathe shathe false debe, ar CoinGuard e kono call e jabe na.

// ===== 1) Tap widget - tap gulo ekta global jaygay rakho =====
function cgTap(need, id) {
  return {
    need: need, cgid: id, taps: [],
    get done() { return this.taps.length === this.need },

    hit(e) {
      if (this.taps.length >= this.need) return;
      var r = this.$refs.wrap.getBoundingClientRect();
      // JORURI: 320x200 scale e rupantor - na korle shob tap bhul hobe
      var x = Math.round((e.clientX - r.left) * 320 / r.width);
      var y = Math.round((e.clientY - r.top)  * 200 / r.height);
      if (x < 0 || y < 0 || x > 320 || y > 200) return;
      this.taps.push(x + ',' + y);
      this.sync();
    },

    undo()  { this.taps.pop(); this.sync() },
    clear() { this.taps = [];  this.sync() },

    // baire er submit function ei ta poRbe
    sync() {
      window.__cg = {
        id:   this.cgid,
        taps: this.taps.join('|'),
        done: this.taps.length === this.need,
      };
    },
    init() { this.sync() },
  }
}

// ===== 2) Submit korar shomoy - EI DUITA LINE BHULO NA =====
async function submitClaim() {
  var cg = window.__cg || {};          // <-- ekhane poRchi

  var res = await fetch('/claim', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content,
    },
    body: JSON.stringify({
      // ... tomar nijer field gulo ...
      cg_id:   cg.id   || '',          // <-- EITA
      cg_taps: cg.taps || '',          // <-- AR EITA
    }),
  });

  var d = await res.json();
}

Kibhabe bujhbe ei bhul ta hoyeche

Panel → API logs e jao. Jodi dekho challenge → ok ache kintu verify er ekta o line nei — tahole nishchit, JS cg_id/cg_taps pathacche na. Onno kono karon e eta hoy na, karon bhul uttor pathaleo ontoto ekta fail log thakto.

UI — user ke bujhte dao

Captcha kaj korleo, user jodi na bojhe ki korte hobe, tahole se claim korte parbe na. Niche ja ja thakle user atkabe na.

Ja ja thakte HOBE

Nirdesh — chhobir upore
"Tap the numbers, low to high" — eta chhobir UPORE thakte hobe, niche na. User age nirdesh poRbe, tarpor chhobi dekhbe.
Gona — koyta hoyeche
"2 / 5" ba "3 more" — user jate jane ar koyta baki. Eta na thakle se bujhbe na keno submit hocche na.
Tap er chinho — number soho
Protita tap e ekta sobuj dot, tar bhitore 1, 2, 3 lekha. Tahole krom ta chokhe dekha jay, ar bhul hole nijei dhorte parbe.
Undo — ASOL BUTTON er moto
Shudhu dhusor lekha dile user bujhbei na je chapa jay. Background, border, rounded corner ar ekta arrow icon lagbe.
Clear all — 2 tap er beshi hole
4 ta tap bhul hole 4 bar undo chapa birokti kor. Ekta "Clear all" rakho, kintu shudhu 2 tap er beshi hole dekhao.
Done — sobuj tick
Shob tap hoye gele sobuj tick + "done". User jate nishchit hoy je ekhon claim chapa jabe.

Ja korbe na

× Undo ke shudhu text banabe na — button er moto dekhate hobe

× Chhobi te fixed width (320px) dibe na — chhoto phone e kete jabe. width:100%; max-width:340px dao

× touch-action:manipulation bad dibe na — na hole mobile e 300ms deri hobe

× Chhobi te pointer-events:none dite bhulbe na — na hole click er hisheb golmal hobe

<!-- nirdesh + gona : chhobir UPORE -->
<div class="flex items-center mb-2.5">
  <span class="text-[13.5px] font-bold">Tap the numbers, low to high</span>
  <span x-show="!done" class="ml-auto text-[11.5px] font-bold tabular-nums opacity-40"
        x-text="taps.length + ' / ' + need"></span>
  <span x-show="done" x-cloak
        class="ml-auto text-[11.5px] font-extrabold text-emerald-500">ready</span>
</div>

<!-- chhobi -->
<div x-ref="wrap" @click="hit($event)"
     class="relative w-full rounded-2xl overflow-hidden cursor-pointer select-none border"
     style="touch-action:manipulation;max-width:340px">
  <img src="{{ $cg['image'] }}" alt="" draggable="false"
       class="block w-full h-auto pointer-events-none">
  <template x-for="(t, i) in taps" :key="i">
    <span class="absolute grid place-items-center rounded-full font-extrabold pointer-events-none"
          :style="dot(t)" x-text="i + 1"></span>
  </template>
</div>

<!-- button gulo : chhobir NICHE -->
<div class="flex items-center gap-2 mt-3" style="max-width:340px">
  <button type="button" @click="undo()" x-show="taps.length" x-cloak
          class="inline-flex items-center gap-1.5 rounded-xl px-3 py-2 text-[12.5px]
                 font-bold bg-slate-100 dark:bg-white/[.07] border
                 border-slate-200 dark:border-white/10">
    <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor"
         stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
      <path d="M9 14L4 9l5-5"/><path d="M4 9h11a5 5 0 010 10h-4"/>
    </svg>
    Undo
  </button>

  <button type="button" @click="clear()" x-show="taps.length > 1" x-cloak
          class="rounded-xl px-3 py-2 text-[12.5px] font-bold opacity-50">Clear all</button>

  <span x-show="!done" x-cloak class="ml-auto text-[11.5px] font-semibold opacity-40">
    <span x-show="!taps.length">tap the smallest number first</span>
    <span x-show="taps.length" x-text="(need - taps.length) + ' more'"></span>
  </span>

  <span x-show="done" x-cloak
        class="ml-auto inline-flex items-center gap-1 text-[11.5px] font-extrabold text-emerald-500">
    <svg viewBox="0 0 24 24" class="w-3.5 h-3.5" fill="none" stroke="currentColor"
         stroke-width="3" stroke-linecap="round"><path d="M4 12l5 5L20 6"/></svg>
    done
  </span>
</div>

<!-- dot er style : percent diye, tai chhobi chhoto-boro hole-o thik jaygay boshe -->
<script>
function dot(t) {
  var p = t.split(',');
  return 'width:28px;height:28px;font-size:13.5px;'
       + 'background:linear-gradient(150deg,#34d399,#059669);color:#04231a;'
       + 'border:2px solid rgba(255,255,255,.85);'
       + 'box-shadow:0 4px 12px rgba(0,0,0,.45);'
       + 'transform:translate(-50%,-50%);'
       + 'left:' + (p[0] / 320 * 100) + '%;'
       + 'top:'  + (p[1] / 200 * 100) + '%';
}
</script>

API reference

Duita endpoint. Duitai POST, form-encoded body. Uttor shob shomoy JSON.

POST https://samzune.com/cg/challenge

Notun challenge ano

Body
site ha Tomar site key
ua ha User er browser er User-Agent
ip ha User er IP
host na Tomar site er domain — domain lock kora thakle lagbe
Uttor
{
  "success": true,
  "id": "5297f09ca6eb2113...",
  "image": "data:image/png;base64,...",
  "taps": 5,
  "expires": 900
}
POST https://samzune.com/cg/verify

Uttor jachai koro

Body
site ha Tomar site key
secret ha Secret key — shudhu server theke
id ha challenge theke paoa id
taps ha Coordinate — "60,75|39,159|..."
ua ha Ekoi user er UA (challenge er shomoy jeta chhilo)
ip ha Ekoi user er IP
Uttor
{ "success": true }

// ba
{ "success": false, "error": "wrong-order" }

ua ar ip duijaygay ek hote hobe. challenge er shomoy je UA/IP pathabe, verify tei ekoi ta pathate hobe — na hole fp-mismatch ashbe. Ei chek tai id churi kore onno jayga theke jachai kora bondho kore.

Error code gulo

success:false holе error field e karon thake.

bad-site-key Site key bhul, ba site bondho kora ache
bad-secret Secret key mile nai — .env dekho
bad-domain Site e domain lock ache kintu host mile nai — panel e domain thik koro
rate-limit Ei site er prot i minute er limit shesh — panel e Rate/min baRao
missing-field Kono ekta field pathao nai
no-challenge id nei — meyad shesh, ba agei byabohar hoyeche
expired 15 minute er beshi purono challenge
wrong-order Tap er krom ba jayga bhul — manush bhul korle eta ashe
count Joto tap dorkar tar cheye kom ba beshi
too-fast Onek druto uttor — prai shomoy bot
fp-mismatch UA ba IP mile nai — id churi ba bhul kore pathano
too-many Ekoi challenge e 5 baro cheye beshi bhul
honeypot Lukano field bhora hoyeche — manush kore na
setup bhul — tomar config thik koro shabhabik — manush bhul korte pare bot er chinho

Somossa hole

Chhobi ashe na, ba blank box
challenge() null ferot dicche. Server theke outgoing HTTPS bondho thakte pare. Client server e cholao: curl -s -X POST -d "site=YOUR_SITE_KEY&ua=x&ip=1.1.1.1" https://samzune.com/cg/challenge
Shob shomoy fp-mismatch
challenge ar verify te alada UA ba IP jacche. Duijaygay ekoi $r->userAgent() ar $r->ip() pathao. Proxy/CDN thakle ip() bhul dite pare — Laravel er TrustProxies thik ache kina dekho.
bad-domain error
Site e domain lock ache kintu challenge call e host pathao nai, ba bhul domain. Service class e host => request()->getHost() ache kina dekho. Ba panel theke domain er ghor khali kore dao — tokhon lock thakbe na.
Shob shomoy bad-secret
.env e secret bhul, ba config cache purono. php artisan config:clear cholao. Secret harale panel theke Rotate secret koro.
Tap kore kintu wrong-order
Coordinate scale kora hoy nai. JS er * 320 / r.width ongsho ta thakte hobe.
419 Page Expired
Ei ta tomar nijer form er CSRF — @csrf jog koro. CoinGuard er API te CSRF lage na.
too-fast pacchi nijer test e
Eta thik achhe — 3 second er kome uttor dile bot dhora hoy. Test korar shomoy opekkha koro.

AI ke diye bosao

Code na bujhleo shomossa nei. Niche Copy chapo, tarpor ChatGPT / Claude / Cursor — jekhane khushi paste koro. Sathe tomar site key ar secret key ta likhe dio.

Ami amar Laravel project e CoinGuard nam er ekta anti-bot captcha bosate chai.
Tumi amake step by step bolo ar puro code likhe dao.

=== CoinGuard kibhabe kaj kore ===
Ekta chhobi te koyekta number thake elomelo jaygay. User chhoto theke boro
krome tap kore. Shothik uttor kokhono browser e jay na - shudhu CoinGuard er
server e thake. Tai bot chhobi na poRe pass korte pare na.

=== Duita API endpoint ===
1) POST https://samzune.com/cg/challenge
   Body (form-encoded): site, ua, ip, host
   host = tomar site er domain (request()->getHost())
   Uttor: {"success":true,"id":"...","image":"data:image/png;base64,...","taps":5}

2) POST https://samzune.com/cg/verify
   Body (form-encoded): site, secret, id, taps, ua, ip
   taps format: "60,75|39,159|214,166|151,100|241,83"
   Uttor: {"success":true} othoba {"success":false,"error":"wrong-order"}

=== JORURI NIYOM ===
- Duita call e Laravel er SERVER theke korte hobe (Http::asForm()->post),
  browser theke NA. Secret key kokhono browser e ba JS e pathabe na.
- challenge ar verify - duijaygay EKOI ua ar ip pathate hobe
  (request()->userAgent() ar request()->ip()). Na hole fp-mismatch error ashbe.
- Browser shudhu tap er coordinate jomay: "x,y|x,y|..."
  Coordinate 320x200 scale e pathate hobe. Chhobi CSS e chhoto-boro hole
  bhag kore nite hobe: Math.round((e.clientX - rect.left) * 320 / rect.width)
- challenge call e host o pathate hobe (request()->getHost()) - panel e
  domain lock kora thakle eta na pathale bad-domain error ashbe.
- verify() true hole tar POREI reward dite hobe - agey na.
- Ekta challenge ek baroi kaj kore. Fail hole notun challenge lagbe (page reload).
- Network fail hole verify false dey - mane reward dey na. Eta thik ache.

=== Amar key ===
CG_URL=https://samzune.com
CG_SITE=<ekhane amar site key bosao>
CG_SECRET=<ekhane amar secret key bosao>

=== Ja ja banate hobe ===
1. .env e uporer 3 ta line, ar config/services.php e 'coinguard' array
2. app/Services/CoinGuard.php - challenge() ar verify($id, $taps) method soho
3. Controller: show() e challenge ano, store() e verify koro tarpor reward
4. Blade: chhobi dekhao, tap dhoro, hidden field cg_id ar cg_taps e bhoro,
   undo button, ar shob tap na hole submit atkao
5. Vanilla JS - jQuery chhara

Amar project e ekhon je faucet/claim page ta ache, sheta amake dekhao kothay
ki bosate hobe. Kono line mucho na jodi na bujho seta ki kore.

Sabdhan: prompt e secret key bosanor por sheta public jaygay (github, forum, screenshot) share korbe na. AI chat privateই rakho. Bhul kore faas hole panel theke Rotate secret koro.

CoinGuard · https://samzune.com