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
- Customer clicks "Pay with bKash/Nagad" on your site.
- Your server calls
POST /api/v1/paymentsand redirects the customer to the returnedcheckout_url. - The customer sends the money and submits their TrxID; Fastaar verifies the SMS.
- Fastaar POSTs
payment.completedto 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:
-
Server-to-server calls are never blocked. Requests without an
Origin/Refererheader — like the cURL example above, or any server-side SDK call — always pass. Always create payments from your server, where your secret key stays safe. -
Browser requests must match a registered website. If the origin is not
on your list — or no websites are registered yet — the request gets
403 website_not_allowed.
Checklist before going live
- Switch from your
fk_test_key to anfk_live_key. - Add your webhook endpoint in the merchant panel and store the webhook secret server-side.
- Register your live site under Websites before calling the API from a browser — requests from unregistered origins are rejected.
- Confirm orders only from verified webhooks — never from the redirect alone.
- Keep your paired phone online (it forwards the verification SMS).