Skip to content
SDK preview. Packages are not publicly published yet — use REST / cURL, or the local development artifacts.

Quickstart

Accept your first test payment in a few minutes — no real money, no payment account.

The whole idea#

  1. Use test mode. It needs no bKash or Nagad account.
  2. Create a test secret key in the dashboard.
  3. The secret key lives on your server, never in browser or app code.
  4. Create a Payment from your server.
  5. Open its checkoutUrl for the customer.
  6. Simulate success with one call.
  7. Verify the webhook PayTaka sends your server.

Building a mobile app? Your app talks to your small backend, and that backend talks to PayTaka — start with your platform below.

Choose your platform#

The steps below are for Node.js, Next.js and any server that speaks HTTP. Other platforms follow the same six steps in their own guide.

1. Create a test secret key#

In the dashboard open Developers → API keys → Create secret key, choose Test, and copy the key (it starts with pt_test_sk_). It is shown once. Keep it on your server:

Shell
export PAYTAKA_SECRET_KEY=pt_test_sk_...
export PAYTAKA_API_URL=https://api.paytaka.live

2. Get the SDK (or use REST)#

Node.js

Next.js

You can always skip the SDK and call the REST API directly — the steps are identical.

3. Create a payment#

create-payment.mjs · JavaScript
// @paytaka/sdk quickstart — run on your SERVER (Node.js 18+).
import { PayTaka } from "@paytaka/sdk";

const paytaka = new PayTaka({
  secretKey: process.env.PAYTAKA_SECRET_KEY,
  baseUrl: process.env.PAYTAKA_API_URL, // @preview-only — not needed once the SDK ships with the production URL built in
});

const payment = await paytaka.payments.create({
  amount: 500,
});

console.log(payment.checkoutUrl);

amount: 500 is fine for a simple value. For an exact amount such as ৳500.50, pass a string: amount: "500.50". The SDK adds a secure idempotency key for you, so a retried request never creates a second payment.

Prefer plain HTTP?

Shell
#!/usr/bin/env bash
# Create a TEST payment with plain curl. Needs a Test secret key from
# Developers → API keys (it starts with pt_test_sk_). No real money is involved.
set -euo pipefail
: "${PAYTAKA_SECRET_KEY:?Set PAYTAKA_SECRET_KEY to your pt_test_sk_... key}"
: "${PAYTAKA_API_URL:?Set PAYTAKA_API_URL to the PayTaka API base URL you were given}"

curl "$PAYTAKA_API_URL/v1/payments" \
  -X POST \
  -H "Authorization: Bearer $PAYTAKA_SECRET_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"amount":"500.00"}'

4. Open the checkoutUrl#

Send your customer to payment.checkoutUrl: redirect them from your server, or return it to your mobile app to open (Android, iOS). The test checkout page shows a TEST MODE banner and a fake receiving number.

5. Simulate the payment#

No real money moves in test mode. Complete the payment with one call — or press Simulate successful payment on the test checkout page:

JavaScript
await paytaka.testHelpers.payments.succeed(payment.id)

6. Receive the webhook#

In Developers → Webhooks → Add endpoint register your server's URL for the Test environment and copy its signing secret (whsec_…) into PAYTAKA_WEBHOOK_SECRET.

JavaScript
// Receive PayTaka webhooks with @paytaka/sdk (Node.js 18+, no framework).
import { createServer } from "node:http";
import { PayTakaSignatureVerificationError, verifyWebhook } from "@paytaka/sdk";

// Demo only: PayTaka delivers each event AT LEAST once, so remember the ids you have handled.
// In production use your database (a table with a unique event_id) instead of a Set.
const processed = new Set();

createServer(async (req, res) => {
  if (req.method !== "POST" || req.url !== "/paytaka/webhook") {
    res.writeHead(404).end();
    return;
  }

  // 1. Read the RAW body — do not JSON.parse before verifying.
  const chunks = [];
  for await (const chunk of req) chunks.push(chunk);
  const rawBody = Buffer.concat(chunks);

  // 2. Verify the signature. Never trust an unverified webhook.
  let event;
  try {
    event = verifyWebhook(rawBody, req.headers["paytaka-signature"], process.env.PAYTAKA_WEBHOOK_SECRET);
  } catch (error) {
    if (error instanceof PayTakaSignatureVerificationError) {
      res.writeHead(400).end();
      return;
    }
    throw error;
  }

  // 3. Already handled this event? Acknowledge and stop — never fulfil twice.
  if (processed.has(event.id)) {
    res.writeHead(200).end();
    return;
  }

  // 4. Fulfil ONLY from a verified payment.paid event. Ignore anything you don't recognise.
  if (event.type === "payment.paid") {
    console.log("Fulfil the order for payment", event.data.object.id);
  }
  processed.add(event.id);

  // 5. Any 2xx acknowledges the event. Don't rely on delivery order.
  res.writeHead(200, { "Content-Type": "application/json" }).end(JSON.stringify({ received: true }));
}).listen(Number(process.env.PORT ?? 3000));

The example verifies the signature, ignores an event it has already handled, and fulfils only from payment.paid. Details: Webhooks.

You're done#

If everything worked you now have:

WhereWhat you see
Your console.loga checkoutUrl
The paymentstatus: PAID after step 5
Your webhook endpointa verified payment.paid event with livemode: false
The dashboard → Developers → Webhooksthe delivery marked Delivered

That is a complete integration. Next: Going live when you're ready for real payments, or Payments and Checkout for the details.