Add Fastaar to your website

The full flow has three parts: your server creates a payment, the customer pays on the hosted checkout page, and your server confirms the order from a webhook. Start with a test-mode key so you can integrate without real money.

The flow

  1. Customer clicks "Pay with bKash/Nagad" on your site.
  2. Your server calls POST /api/v1/payments and redirects the customer to the returned checkout_url.
  3. The customer sends the money and submits their TrxID; Fastaar verifies the SMS.
  4. Fastaar POSTs payment.completed to your webhook — you mark the order paid.

Using PHP, Laravel, Node.js, Next.js, Python, or Django? The official SDK for your stack wraps payment creation and webhook signature verification for you. Running WooCommerce? Install the WordPress/WooCommerce plugin instead — no code required. Prefer to call the API directly? Here's the same flow by hand:

Custom integration (without an SDK)

Plain PHP:

// pay.php — create the payment and redirect
$ch = curl_init('https://pay.codeappear.com/api/v1/payments');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer '.getenv('FASTAAR_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 1250,
        'invoice_number' => $orderId,
        'success_url' => 'https://shop.example.com/orders/'.$orderId,
    ]),
]);
$payment = json_decode(curl_exec($ch), true)['data'];

header('Location: '.$payment['checkout_url']);
exit;
// webhook.php — confirm the order
$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_FASTAAR_SIGNATURE'] ?? '';

if (! preg_match('/^t=(?P<t>\d+),v1=(?P<v1>[a-f0-9]{64})$/', $header, $m)) {
    http_response_code(400); exit;
}

$valid = abs(time() - (int) $m['t']) <= 300
    && hash_equals(hash_hmac('sha256', "{$m['t']}.{$rawBody}", getenv('FASTAAR_WEBHOOK_SECRET')), $m['v1']);

if (! $valid) {
    http_response_code(400);
    exit;
}

$event = json_decode($rawBody, true);

if ($event['event'] === 'payment.completed') {
    markOrderPaid($event['data']['invoice_number'], $event['data']['id']);
}

http_response_code(200);

Laravel:

// routes/web.php
Route::post('/pay', function () {
    $response = Http::withToken(config('services.fastaar.key'))
        ->post(config('services.fastaar.url').'/api/v1/payments', [
            'amount' => 1250,
            'invoice_number' => $orderId,
            'success_url' => route('orders.show', $orderId),
        ])->throw()->json('data');

    return redirect($response['checkout_url']);
});

// routes/api.php — exclude from CSRF, verify, then confirm
Route::post('/webhooks/fastaar', function (Request $request) {
    $header = $request->header('X-Fastaar-Signature', '');
    $rawBody = $request->getContent();

    if (! preg_match('/^t=(?P<t>\d+),v1=(?P<v1>[a-f0-9]{64})$/', $header, $m)) {
        abort(400);
    }

    $valid = abs(time() - (int) $m['t']) <= 300
        && hash_equals(hash_hmac('sha256', "{$m['t']}.{$rawBody}", config('services.fastaar.webhook_secret')), $m['v1']);

    abort_unless($valid, 400);

    $event = $request->json()->all();

    if ($event['event'] === 'payment.completed') {
        // mark order paid (idempotent on $event['data']['id'])
    }

    return response()->noContent();
});

Node.js (Express):

import crypto from 'crypto';

app.post('/pay', async (req, res) => {
    const response = await fetch('https://pay.codeappear.com/api/v1/payments', {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${process.env.FASTAAR_API_KEY}`,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            amount: 1250,
            invoice_number: req.body.orderId,
            success_url: `https://shop.example.com/orders/${req.body.orderId}`,
        }),
    });
    const { data: payment } = await response.json();

    res.redirect(payment.checkout_url);
});

function verifySignature(header, rawBody, secret) {
    const m = Object.fromEntries(header.split(',').map(p => p.split('=')));
    if (Math.abs(Date.now() / 1000 - Number(m.t)) > 300) return false;

    const expected = crypto.createHmac('sha256', secret)
        .update(`${m.t}.${rawBody}`)
        .digest('hex');

    return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m.v1));
}

app.post('/webhooks/fastaar', express.raw({ type: 'application/json' }), (req, res) => {
    if (! verifySignature(req.header('X-Fastaar-Signature'), req.body, process.env.FASTAAR_WEBHOOK_SECRET)) {
        return res.sendStatus(400);
    }

    const event = JSON.parse(req.body);

    if (event.event === 'payment.completed') {
        // mark order paid (idempotent on event.data.id)
    }

    res.sendStatus(200);
});

Raw cURL (any language)

curl -X POST https://pay.codeappear.com/api/v1/payments \
  -H "Authorization: Bearer $FASTAAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount": 1250, "invoice_number": "ORDER-42", "success_url": "https://shop.example.com/thanks"}'

Websites

Under Developer → Websites in the merchant panel you can restrict which websites may call the API from the browser. Add the websites your checkout runs on — e.g. shop.example.com. The scheme and a leading www. are ignored, so https://www.shop.example.com and shop.example.com are treated the same. Subdomains are matched exactly — example.com and shop.example.com are different entries, so register each brand's subdomain your checkout runs on. How many websites you can register depends on your plan's Website Limit.

The check looks at the request's Origin (then Referer) header. Two things follow from that:

Checklist before going live