> For the complete documentation index, see [llms.txt](https://docs.loomgate.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.loomgate.io/getting-started/quickstart.md).

# Khởi động nhanh

Từ API key tới thanh toán đầu tiên trên website của bạn.

Hướng dẫn này dùng `loomgate.js` (script do Loomgate phục vụ) cho trình duyệt và Node.js cho máy chủ. Mọi ngôn ngữ máy chủ khác đều gọi được API bằng HTTP như ví dụ `curl`.

{% hint style="info" %}
Muốn xem mã chạy được ngay? [Kho ví dụ loomgate-examples](https://github.com/loomgroup/loomgate-examples) có trang checkout hoàn chỉnh cho từng cách tích hợp (JS SDK, thẻ script `loomgate.js`, bản UMD, React): chọn ví dụ hợp với stack của bạn. [Playground](https://examples.loomgate.io) cho bạn chạy thử từng bước bằng key của chính mình (thanh toán bằng tiền thật).
{% endhint %}

{% stepper %}
{% step %}

### Lấy API key

Đăng nhập [dashboard merchant](https://app.loomgate.io), mở [**API key**](https://app.loomgate.io/api-keys) và tạo một cặp key mới.

* **Secret key** (`sk_live_…`) chỉ hiện **một lần**. Lưu ngay vào biến môi trường của máy chủ, ví dụ `LOOMGATE_SECRET_KEY`. Không bao giờ đưa nó xuống trình duyệt hay commit vào mã nguồn.
* **Publishable key** (`pk_live_…`) dùng trong trình duyệt, xem lại được bất cứ lúc nào.
  {% endstep %}

{% step %}

### Tạo payment từ máy chủ

Khi người mua vào trang thanh toán, máy chủ của bạn tạo một payment cho đơn hàng. `items` là bắt buộc; người mua, mã đơn và địa chỉ giao hàng có thể gửi ngay hoặc cập nhật sau, nhưng phải có trước khi người mua trả tiền (xem [Chi tiết giao dịch](/concepts/payment-details.md)).

{% tabs %}
{% tab title="curl" %}

```bash
curl https://api.loomgate.io/partner/v1/payment_intents \
  -H "Authorization: Bearer $LOOMGATE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042" \
  -d '{
    "amount": 10000,
    "currency": "usd",
    "items": [{ "name": "T-shirt", "quantity": 2, "unit_amount": 4500 }],
    "shipping_amount": 1000,
    "order_reference": "1042",
    "buyer": { "name": "Jane Doe" },
    "shipping_details": {
      "name": "Jane Doe",
      "address": { "line1": "1 Market St", "city": "San Francisco", "state": "CA", "postal_code": "94105", "country": "US" }
    },
    "metadata": { "order_id": "1042" }
  }'
```

{% endtab %}

{% tab title="Node.js" %}

```js
import { createLoomgateServerClient } from '@loompay/loomgate-js-sdk/server';

const loomgate = createLoomgateServerClient(process.env.LOOMGATE_SECRET_KEY, {
  apiBaseUrl: 'https://api.loomgate.io',
});

const intent = await loomgate.paymentIntents.create(
  {
    amount: 10000,
    currency: 'usd',
    items: [{ name: 'T-shirt', quantity: 2, unit_amount: 4500 }],
    shipping_amount: 1000,
    order_reference: '1042',
    buyer: { name: 'Jane Doe' },
    shipping_details: {
      name: 'Jane Doe',
      address: { line1: '1 Market St', city: 'San Francisco', state: 'CA', postal_code: '94105', country: 'US' },
    },
    metadata: { order_id: '1042' },
  },
  { idempotencyKey: 'order-1042' },
);

// Gửi intent.client_secret xuống trình duyệt, lưu intent.id vào đơn hàng.
```

{% endtab %}
{% endtabs %}

Response (rút gọn; phí thực tế theo biểu phí của bạn):

```json
{
  "id": "lg_pi_4Qx9ZcM2LwT8rKv1bN3a",
  "object": "payment_intent",
  "status": "pending",
  "currency": "usd",
  "amount": 10000,
  "fee_buyer": 320,
  "amount_total": 10320,
  "fee_bearer": "buyer",
  "client_secret": "lg_cs_4Qx9ZcM2LwT8rKv1bN3a_…"
}
```

`client_secret` chỉ có trong response tạo payment. Hiển thị cho người mua đúng `amount_total`: đó là số tiền thẻ sẽ bị trừ.
{% endstep %}

{% step %}

### Hiện form thẻ trong trình duyệt

Nạp `loomgate.js`, gắn form thẻ và phần thương hiệu (bắt buộc), rồi gọi `confirm()` khi người mua bấm Pay.

```html
<div id="loomgate-payment"></div>
<div id="loomgate-branding"></div> <!-- bắt buộc -->
<p id="total"></p>
<button id="pay" disabled>Pay</button>

<script src="https://api.loomgate.io/partner/v1/loomgate.js"></script>
<script>
  (async () => {
    const loomgate = await window.Loomgate.loadLoomgate('pk_live_…');
    const session = loomgate.payment({ clientSecret: 'lg_cs_…' }); // từ máy chủ của bạn

    session.on('change', ({ complete, amountTotal, currency }) => {
      document.getElementById('pay').disabled = !complete;
      document.getElementById('total').textContent =
        new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amountTotal / 100);
    });
    await session.mount({ payment: '#loomgate-payment', branding: '#loomgate-branding' });

    document.getElementById('pay').addEventListener('click', () => {
      // Gọi confirm() ngay trong handler click, không await gì trước nó.
      session
        .confirm({
          billingDetails: {
            email: 'buyer@example.com',
            name: 'Jane Doe',
            address: { line1: '1 Market St', city: 'San Francisco', state: 'CA', postal_code: '94105', country: 'US' },
          },
        })
        .then((result) => {
          if (result.error) alert(result.error.message);
          else location.assign('/orders/1042/thanks');
        });
    });
  })();
</script>
```

Xem chi tiết ở [Form thanh toán (loomgate.js)](/integrations/browser.md).
{% endstep %}

{% step %}

### Nhận webhook và giao hàng

Tạo một webhook endpoint trong dashboard (mục **Webhook**) trỏ về máy chủ của bạn và ghi lại **ID endpoint** (`we_…`). Khi thanh toán thành công, Loomgate gửi sự kiện `payment_intent.succeeded`:

```js
import express from 'express';
import { createLoomgateServerClient, verifyWebhook } from '@loompay/loomgate-js-sdk/server';

const loomgate = createLoomgateServerClient(process.env.LOOMGATE_SECRET_KEY, {
  apiBaseUrl: 'https://api.loomgate.io',
});
const endpointId = process.env.LOOMGATE_WEBHOOK_ENDPOINT_ID; // we_…
let publicKeys = {};

async function refreshKeys() {
  const { data } = await loomgate.webhookSigningKeys.list();
  publicKeys = Object.fromEntries(data.map((key) => [key.kid, key.public_key_pem]));
}

const app = express();

// Cần body gốc (raw), không dùng express.json() cho route này.
app.post('/webhooks/loomgate', express.raw({ type: 'application/json' }), async (req, res) => {
  const verify = () => verifyWebhook(req.body, req.get('Loomgate-Signature'), { endpointId, publicKeys });
  let result = verify();
  if (!result.ok && result.code === 'unknown_key') {
    await refreshKeys(); // khóa ký mới: tải lại một lần
    result = verify();
  }
  if (!result.ok) return res.status(400).send(result.code);

  const event = JSON.parse(req.body.toString('utf8'));
  if (event.type === 'payment_intent.succeeded') {
    const payment = event.data.object;
    await markOrderPaid(payment.order_reference, payment.id); // bỏ qua nếu event.id đã xử lý
  }
  res.sendStatus(200);
});
```

Xem [Xác minh chữ ký](/webhooks/verify-signatures.md) cho PHP, Python và cách tự kiểm tra.
{% endstep %}

{% step %}

### Thử một thanh toán thật

Tạo một payment nhỏ (tổng tối thiểu 1,00 USD hoặc 1,00 EUR), thanh toán bằng thẻ của bạn, kiểm tra:

* webhook tới máy chủ và đơn chuyển sang đã thanh toán;
* giao dịch hiện trong mục **Giao dịch** của dashboard.

Sau đó hoàn tiền từ dashboard hoặc bằng [API refunds](/api-reference/refunds.md). Phí của giao dịch không được hoàn.
{% endstep %}
{% endstepper %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.loomgate.io/getting-started/quickstart.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
