# Accept VietQR bank transfers in your app

Ha Tang watches your own bank account. You create a payment with an amount and a transfer code, the buyer scans the VietQR code in their banking app, and Ha Tang matches the incoming transfer and tells your server. No card processor, no money held by a third party.

```bash
npm install @hatang/sdk
```

**1. Create the payment and send the buyer to checkout**

```ts
import { Hatang } from "@hatang/sdk";

const hatang = new Hatang(); // HATANG_API_KEY; HATANG_ENVIRONMENT=development while you build

const { data: payment, error } = await hatang.payments.createOrder({
  order_id: "SO-1001", // also the idempotency key
  amount: 150_000,
  description: "Order #1001",
  return_url: "https://shop.example.com/orders/SO-1001",
  ipn_url: "https://shop.example.com/api/hatang/ipn",
});
if (error) throw new Error(`${error.name}: ${error.message}`);
redirect(payment.checkout_url); // or draw payment.qr.image_url yourself
```

**2. Receive the IPN** (`app/api/hatang/ipn/route.ts`)

```ts
import { constructIpnEvent, HatangSignatureError } from "@hatang/sdk";

export async function POST(req: Request) {
  const raw = await req.text(); // the raw body, before any JSON parsing
  try {
    const ipn = await constructIpnEvent(raw, req.headers, process.env.HATANG_SECRET_KEY!);
    if (ipn.data.status === "paid" || ipn.data.status === "overpaid") {
      await markOrderPaid(ipn.data.metadata.order_id as string); // idempotently: an IPN can repeat
    }
    return new Response(null, { status: 200 });
  } catch (err) {
    if (err instanceof HatangSignatureError) return new Response(null, { status: 401 });
    throw err; // a 5xx makes Ha Tang retry
  }
}
```

**3. Confirm on the return page**

```ts
const { data: check } = await hatang.payments.verify(paymentId, { order_id: "SO-1001", amount: 150_000 });
if (check?.verified) await fulfil("SO-1001");
```

In the `development` environment no money moves: the checkout page has a button that simulates the transfer, or call `hatang.payments.simulateTransfer({ payment_id })`.

## Go further

- The whole flow with every option: [Payments with the JavaScript SDK](https://hatang.dev/docs/guides/payments/sdk-javascript.md)
- Link your bank account: [Bank connections](https://hatang.dev/docs/guides/payments/bank-connections.md)
- Issue an e-invoice automatically once paid: [E-invoices](https://hatang.dev/docs/guides/payments/einvoices.md)
- Create and settle test payments from your coding agent: [MCP server](https://hatang.dev/docs/guides/sdks/mcp.md)

Source: https://hatang.dev/docs/guides/recipes/vietqr-payments
