> ## Documentation Index
> Fetch the complete documentation index at: https://ifdocdemo.lumivescence.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Nhận thanh toán từ agent

> Thu phí agent cho các endpoint của bạn bằng Pay (x402 và MPP) và các SDK người bán Node.js của InFlow. Đặt giá, loại tiền, rail và gói thuê bao.

Thu phí agent cho các endpoint của bạn bằng Pay (x402 và MPP) và các SDK người bán Node.js của InFlow. Endpoint được bảo vệ trả về `402 Payment Required` cho đến khi agent thanh toán. InFlow kiểm tra khoản thanh toán trước khi code của bạn chạy. Khoản thanh toán bằng stablecoin quyết toán vào tài khoản InFlow của bạn. Khoản thanh toán bằng thẻ quyết toán vào tài khoản Stripe của bạn.

Để đi theo cách ngắn nhất, đọc [Bắt đầu nhanh](https://ifdocdemo.lumivescence.com/vi/sellers/get-started.md) trước. Với Python, Go và Rust, xem [Python, Go và Rust](https://ifdocdemo.lumivescence.com/vi/sellers/protect-an-endpoint-other-languages.md).

## Trước khi bắt đầu

| Mục | Chi tiết |
| - | - |
| Node | Phiên bản 22 LTS trở lên. Các package khai báo `engines.node: >=22.0.0`. |
| Tài khoản | Một tài khoản Seller và một API key. Xem [Tạo tài khoản Seller](https://ifdocdemo.lumivescence.com/vi/sellers/create-a-seller-account.md). |
| Môi trường | `sandbox` hoặc `production`. Giá trị này phải khớp với dashboard cấp key. |

## Chấp nhận MPP và x402

Tích hợp cả hai giao thức trong cùng một dịch vụ, để agent dùng giao thức nào cũng có thể thanh toán. Dùng chung logic định giá và logic xử lý đơn hàng cho hai giao thức. Một request không bao giờ được đi qua hai luồng thu phí độc lập. [Cách Pay hoạt động](https://ifdocdemo.lumivescence.com/vi/concepts/payments.md) so sánh các giao thức.

Số dư InFlow là cách thanh toán mặc định của agent. Đó là rail `balance` trong MPP và scheme `balance` trong x402. Agent cũng có thể thanh toán onchain hoặc bằng thẻ đã liên kết.

## Cài đặt các package

| Giao thức | Framework | Cài đặt |
| - | - | - |
| x402 | Express | `pnpm add @inflowpayai/x402-seller @x402/express @x402/core @x402/extensions express` |
| x402 | Fastify | `pnpm add @inflowpayai/x402-seller @x402/fastify @x402/core @x402/extensions fastify` |
| x402 | Next 16 App Router | `pnpm add @inflowpayai/x402-seller @x402/next @x402/core @x402/extensions next` |
| MPP | Bất kỳ | `pnpm add @inflowpayai/mpp-seller mppx@^0.8.17` |

Với MPP, `mppx` là một peer dependency. Mỗi framework có một ví dụ chạy được trong [Tham khảo SDK](https://ifdocdemo.lumivescence.com/vi/reference/sdk-packages.md#ví-dụ).

## Bảo vệ endpoint bằng x402

Server này từ ví dụ `x402-seller-express` bảo vệ hai endpoint và để một endpoint miễn phí.

```ts server.ts theme={null}
import 'dotenv/config';
import { paymentMiddlewareFromConfig } from '@x402/express';
import express from 'express';
import {
  createInflowFacilitator,
  createInflowSellerClient,
  inflowAccepts,
  inflowSchemeRegistrations,
} from '@inflowpayai/x402-seller';

const environment = process.env['INFLOW_ENVIRONMENT'] as 'sandbox' | 'production';
const apiKey = process.env['INFLOW_API_KEY'];
if (!environment || !apiKey) {
  console.error('Set INFLOW_ENVIRONMENT and INFLOW_API_KEY in your environment.');
  process.exit(1);
}

const inflow = createInflowFacilitator({ environment, apiKey });
const seller = await createInflowSellerClient({ environment, apiKey });

const app = express();
app.use(express.json());

app.use(
  paymentMiddlewareFromConfig(
    {
      'GET /api/widgets': {
        accepts: await inflowAccepts(seller, {
          price: '$0.01',
          schemes: ['exact'],
        }),
      },
      'POST /api/upload': {
        accepts: await inflowAccepts(seller, {
          price: '0.10 USDC',
          schemes: ['balance', 'exact'],
        }),
      },
    },
    [inflow],
    await inflowSchemeRegistrations(seller),
  ),
);

app.get('/api/widgets', (_req, res) => {
  res.json({ widgets: [1, 2, 3] });
});

app.post('/api/upload', (_req, res) => {
  res.json({ status: 'received' });
});

app.get('/free', (_req, res) => {
  res.json({ ok: true, note: 'no payment required' });
});

app.listen(Number(process.env['PORT'] ?? 3000));
```

### Chức năng của từng phần

| Hàm | Chức năng |
| - | - |
| `createInflowFacilitator` | Tạo client facilitator. Client này kiểm tra và quyết toán các khoản thanh toán qua InFlow. Hàm này là hàm đồng bộ. |
| `createInflowSellerClient` | Tạo client người bán. Client này đọc cấu hình người bán của bạn và tìm các signer của bạn. Hàm này là hàm bất đồng bộ và lưu cache trong 60 phút. |
| `inflowAccepts` | Chuyển một mức giá thành các tùy chọn thanh toán. Hàm này xác định số tiền atomic và địa chỉ tài sản cho mỗi tùy chọn. |
| `inflowSchemeRegistrations` | Đăng ký một scheme server cho mỗi cặp scheme và network mà bạn có thể công bố. |

Truyền `inflowSchemeRegistrations` làm đối số thứ ba. Nếu thiếu đối số này, middleware dừng lúc khởi động với `RouteConfigurationError`.

### Biết khi nào khoản thanh toán quyết toán

Middleware kiểm tra khoản thanh toán trước khi handler của bạn chạy. Middleware quyết toán khoản thanh toán sau khi handler của bạn chạy. Vì vậy, không cộng credit hoặc làm việc khác mà bạn không thể hoàn tác trong handler. Xem [Bán credit trả trước](https://ifdocdemo.lumivescence.com/vi/sellers/sell-prepaid-credits.md).

### Viết giá

`inflowAccepts` đọc ba định dạng giá. Mỗi định dạng chấp nhận tối đa 8 chữ số thập phân.

| Định dạng | Ví dụ | Loại tiền |
| - | - | - |
| Số tiền đô la | `'$0.01'` | USD |
| Số tiền có hậu tố loại tiền | `'0.10 USDC'` | Hậu tố đó |
| Số thuần | `'0.01'` | Trường `currency` của giá |

Với các offer `balance` và `exact`, `USD` khớp với mọi stablecoin trong cấu hình người bán của bạn. Với một offer `instrument`, `USD` là USD pháp định.

Khoản thanh toán bằng stablecoin không có số tiền tối thiểu. Giá `instrument` phải bằng USD, tối thiểu USD 0.50 và là số cent nguyên.

### Giới hạn các scheme

Truyền `schemes` để giới hạn các tùy chọn thanh toán trên một endpoint, như `POST /api/upload` ở trên.

| Scheme | Chức năng | Có trong mặc định |
| - | - | - |
| `balance` | Chuyển số dư InFlow của agent. | Có |
| `exact` | Quyết toán onchain. | Có |
| `upto` | Quyết toán onchain một số tiền tính theo mức dùng, không vượt quá một mức tối đa. | Không. Thêm scheme này vào `schemes`. |
| `instrument` | Thu phí bằng USD trên một thẻ đã liên kết của người mua. | Không. Thêm scheme này vào `schemes`. |

### Dùng nhiều facilitator

Middleware chấp nhận một danh sách facilitator. Các khoản thanh toán của một cặp scheme và network thuộc về facilitator đầu tiên nhận quyền với cặp đó.

```ts theme={null}
paymentMiddlewareFromConfig({/* routes */}, [
  inflow, // claims (balance, inflow), (exact, eip155:8453), …
  otherFacilitator, // claims what inflow does not claim
  partnerFacilitator,
]);
```

### Lưu cache

Middleware x402 đặt các header cache. Một challenge chưa thanh toán nhận `no-store`. Một response đã quyết toán nhận `private`.

## Bảo vệ endpoint bằng MPP

MPP dùng package `mppx`. Thêm phương thức InFlow vào `Mppx.create`. Sau đó `charge()` trả về một challenge `402` cho request chưa thanh toán.

```ts theme={null}
import { Mppx, inflow, paymentHttpTransport } from '@inflowpayai/mpp-seller';

const mppx = Mppx.create({
  transport: paymentHttpTransport(),
  methods: [
    inflow({
      apiKey: process.env.INFLOW_API_KEY!,
      environment: 'sandbox',
    }),
  ],
  secretKey: process.env.MPP_SECRET_KEY,
});

export async function handler(req: Request) {
  const r = await mppx.charge({ amount: '0.01', currency: 'USDC' })(req);
  if (r.status === 402) return r.challenge;
  return r.withReceipt(Response.json({ data: '…' }));
}
```

Handler này dùng Web Fetch API. Với Express, Hono, Next.js hoặc Elysia, dùng `mppx/express`, `mppx/hono`, `mppx/nextjs` hoặc `mppx/elysia`. [Bắt đầu nhanh](https://ifdocdemo.lumivescence.com/vi/sellers/get-started.md) trình bày adapter Express.

Truyền `paymentHttpTransport()` làm `transport`. Nếu thiếu giá trị này, một lỗi server cũng gửi một challenge thanh toán.

### Đặt khóa bí mật

`Mppx.create` cần một `secretKey`. Hàm này cũng đọc biến `MPP_SECRET_KEY`. Tạo khóa bí mật.

```bash theme={null}
openssl rand -base64 32
```

Khóa bí mật phải dài ít nhất 32 byte. Giữ nguyên khóa này qua các lần deploy, vì server dùng khóa này để kiểm tra các challenge mà server cấp.

### Chọn loại tiền

Loại tiền của khoản thu quyết định rail. SDK đọc rail của mỗi loại tiền từ `GET /v1/mpp/config`, một lần lúc khởi động.

| Loại tiền của khoản thu | Rail | Kết quả |
| - | - | - |
| Tiền mã hóa, ví dụ `USDC` | `balance` | Một challenge. Không có tham số bổ sung. |
| Tiền pháp định, ví dụ `USD` | `instrument` | Một challenge. Người mua chọn một thẻ đã liên kết hoặc dùng thẻ chính. Không đặt ID instrument của người mua vào challenge. |
| Không hỗ trợ, ví dụ `JPY` | Không có | `MppUnsupportedCurrencyError` |

Khoản thanh toán bằng stablecoin không có số tiền tối thiểu. Khoản thanh toán thẻ bằng USD có mức tối thiểu USD 0.50 và phải là số cent nguyên. Điều này áp dụng cho rail `instrument`, `card` và `stripe`.

### Chấp nhận nhiều loại tiền

`charge()` công bố một loại tiền. Với nhiều loại tiền, dùng `inflowChargesNodeListener` hoặc `inflowCharges`. Endpoint gửi một challenge cho mỗi mức giá. Phương thức suy ra rail từ loại tiền.

```ts theme={null}
import { Mppx, inflow, inflowChargesNodeListener, paymentHttpTransport } from '@inflowpayai/mpp-seller';

// Core instance from mppx/server. It keeps compose().
const mppx = Mppx.create({
  transport: paymentHttpTransport(),
  methods: [inflow({ apiKey: process.env.INFLOW_API_KEY!, environment: 'sandbox' })],
  secretKey: process.env.MPP_SECRET_KEY,
});

// USD → instrument rail, USDC → balance rail.
const checkout = inflowChargesNodeListener(mppx, [
  { amount: '1.0', currency: 'USD' },
  { amount: '0.0095', currency: 'USDC' },
]);
```

Gắn `checkout(req, res)` vào một server `http` của Node hoặc vào một endpoint Express. Với handler Web Fetch, dùng `inflowCharges(mppx, prices)`.

Các adapter framework chỉ cung cấp `charge` và loại bỏ `compose`. Do đó, endpoint nhiều loại tiền cần instance `Mppx` lõi từ `@inflowpayai/mpp-seller`. Instance này cũng có thể phục vụ các endpoint một loại tiền.

Người mua thanh toán một challenge. Mỗi số tiền là độc lập. Số tiền không phải là tỷ giá.

### Kiểm soát các offer xuất hiện

Hai hook chọn các challenge cho một request. `canOffer` hoạt động trên một phương thức. `selectOffers` hoạt động trên server và chạy sau mọi `canOffer`.

```ts theme={null}
const mppx = Mppx.create({
  transport: paymentHttpTransport(),
  methods: [
    inflow({
      apiKey: process.env.INFLOW_API_KEY!,
      environment: 'sandbox',
      canOffer: ({ input }) => input.headers.get('x-market') !== 'blocked',
    }),
  ],
  selectOffers: (offers, { request }) =>
    request.headers.get('x-usdc-only') === 'true'
      ? offers.filter((offer) => offer.request.currency === 'USDC')
      : offers,
  secretKey: process.env.MPP_SECRET_KEY,
});
```

> [!WARNING]
>
> Các hook này không phải là bước kiểm tra phân quyền. Người mua vẫn có thể dùng một credential sau khi offer của credential đó dừng. Áp dụng chính sách truy cập riêng.

Khi `canOffer` từ chối mọi offer, handler thất bại với `No payment offers are available for this request`. `selectOffers` phải trả về ít nhất một offer.

### Cung cấp gói thuê bao

Gói thuê bao bảo vệ một resource bằng các điều khoản thanh toán định kỳ. Người mua phê duyệt các điều khoản và InFlow thu phí kỳ đầu tiên. Sau đó InFlow quản lý các khoản thu tiếp theo, các trạng thái quá hạn và hủy thuê bao. Gói thuê bao dùng rail `balance` của InFlow. Người mua và người bán không cần ví blockchain.

| Trường | Ý nghĩa |
| - | - |
| `amount` | Số tiền thập phân dương cho mỗi kỳ. |
| `currency` | Mã loại tiền. Loại tiền này phải là loại tiền mà rail balance hỗ trợ. |
| `periodUnit` | `hour`, `day`, `week`, `month`, `quarter` hoặc `year`. |
| `periodCount` | Số dương các đơn vị kỳ giữa các khoản thu. |
| `subscriptionExpires` | Một dấu thời gian RFC 3339. Sau thời điểm này, gói thuê bao không thể gia hạn. |
| `externalId` | Một mã tham chiếu tùy chọn để đối soát. Mã này không phải là khóa phân quyền hoặc khóa tra cứu. |

```ts theme={null}
import express from 'express';
import { Mppx } from 'mppx/server';
import { inflow, inflowSubscriptionsNodeListener, paymentHttpTransport } from '@inflowpayai/mpp-seller';

const subscriptions = Mppx.create({
  transport: paymentHttpTransport(),
  methods: [
    inflow.subscription({
      apiKey: process.env.INFLOW_API_KEY!,
      environment: 'sandbox',
    }),
  ],
  secretKey: process.env.MPP_SECRET_KEY,
});

const subscribe = inflowSubscriptionsNodeListener(subscriptions, [
  {
    amount: '9.99',
    currency: 'USDC',
    periodUnit: 'month',
    periodCount: 1,
    subscriptionExpires: '2027-12-31T23:59:59Z',
    externalId: 'pro-monthly',
  },
]);

const app = express();
app.get('/api/subscribe', async (request, response) => {
  const result = await subscribe(request, response);
  if (result.status === 402) return;
  response.json({ access: 'granted' });
});
```

Với handler Web Fetch, dùng `inflowSubscriptions(mppx, plans)`.

SDK cũng chấp nhận `minute` để kiểm thử, với `periodCount` tối thiểu là `5`. Không cung cấp gói theo phút cho khách hàng.

### Thu phí bằng token TIP-20 trên Tempo

`@inflowpayai/mpp-seller` cũng export phương thức `tempo` cho các khoản thu TIP-20 trên Tempo. Truyền phương thức này vào `Mppx.create` dưới dạng `tempo({ apiKey, currency, recipient })`. Tài trợ fee-payer mặc định tắt. Đặt `methodDetails.feePayer: true` để cấp một challenge có tài trợ.

Package này cũng export `card` cho thanh toán USD bằng network token của Visa và `stripe` cho thanh toán USD bằng Stripe Shared Payment Tokens. Ví dụ `mpp-card-seller-express` dùng `card`.

## Chấp nhận cả hai giao thức trên một endpoint

Một endpoint có thể bán cùng một resource qua MPP và x402. Một request chưa thanh toán nhận một response `402` với các tùy chọn của cả hai giao thức. Người mua chọn một giao thức và gửi credential của giao thức đó đến cùng URL.

| Request | Endpoint làm gì |
| - | - |
| Không có credential thanh toán | Endpoint trả về một response `402`. Các tùy chọn MPP nằm trong `WWW-Authenticate`. Các tùy chọn x402 nằm trong `PAYMENT-REQUIRED`. |
| Một credential MPP | Endpoint chấp nhận khoản thanh toán MPP. Sau đó, endpoint trả về resource cùng receipt MPP. |
| Một credential x402 | Endpoint kiểm tra và quyết toán khoản thanh toán x402. Sau đó, endpoint trả về resource cùng payment response của x402. |
| Hai credential thanh toán | Endpoint từ chối request bằng `400` trước khi một khoản thanh toán bắt đầu. |

Ví dụ Express này bán một báo cáo với giá 0.01 USDC. Ví dụ kết nối hai middleware trong code của ứng dụng. Đây không phải một helper của SDK. Ví dụ dùng các giá trị sau từ các phần ở trên:

| Tên | Giá trị |
| - | - |
| `options` | Các tùy chọn của method `inflow`: môi trường và API key Seller của bạn. |
| `secretKey` | Khóa bí mật MPP. |
| `route` | Cấu hình endpoint x402, với `accepts` từ `inflowAccepts`. Ví dụ dùng scheme `balance`. |
| `facilitator` | Client từ `createInflowFacilitator`. |
| `registrations` | Kết quả của `inflowSchemeRegistrations`. |
| `resourceUrl` | URL công khai đầy đủ của endpoint này. |

```ts theme={null}
import { Mppx } from 'mppx/express';
import { Mppx as MppxServer } from 'mppx/server';
import { inflow, paymentHttpTransport } from '@inflowpayai/mpp-seller';
import { paymentMiddlewareFromConfig } from '@x402/express';
import type { RequestHandler } from 'express';

const method = inflow(options);
const realm = 'report-service';
const payments = Mppx.create({
  methods: [method], realm, secretKey, transport: paymentHttpTransport(),
});
const challenges = MppxServer.create({
  methods: [method], realm, secretKey, transport: paymentHttpTransport(),
});
const terms = {amount: '0.01', currency: 'USDC'};
const mppPayment = payments.charge(terms);
const mppChallenge = challenges.charge(terms);
const x402Payment = paymentMiddlewareFromConfig(
  {'GET /api/report': route}, [facilitator], registrations,
);
const report: RequestHandler = (_request, response) => {
  response.set('Cache-Control', 'no-store');
  response.json({report: 'Your sample report is ready.'});
};

app.get('/api/report', async (request, response, next) => {
  response.set('Cache-Control', 'no-store');
  const authorization = request.get('Authorization') ?? '';
  const hasMpp = /^Payment(?:\s|$)/i.test(authorization);
  const hasX402 = request.get('Payment-Signature') !== undefined
    || request.get('X-Payment') !== undefined;
  const duplicate = ['authorization', 'payment-signature', 'x-payment']
    .some(name => (request.headersDistinct[name]?.length ?? 0) > 1);
  if (duplicate || (hasMpp && hasX402)
    || (request.get('Payment-Signature') !== undefined
      && request.get('X-Payment') !== undefined)) {
    response.status(400).json({error: 'Send one payment credential.'});
    return;
  }
  if (hasMpp) return mppPayment(request, response, next);
  if (hasX402) return x402Payment(request, response, next);

  const unpaid = await mppChallenge(new Request(resourceUrl));
  if (unpaid.status !== 402) throw new Error('Expected an MPP challenge.');
  const challenge = unpaid.challenge;
  if (challenge.status !== 402) {
    challenge.headers.forEach((value, name) => response.set(name, value));
    response.status(challenge.status).send(await challenge.text());
    return;
  }
  const header = challenge.headers.get('WWW-Authenticate');
  if (header === null) throw new Error('Missing MPP challenge header.');
  response.append('WWW-Authenticate', header);
  return x402Payment(request, response, next);
}, report);
```

Dùng một app Express. Chạy các kiểm tra xác thực và kiểm tra đầu vào của bạn trước endpoint này. Không đăng ký thêm middleware x402 ở cấp toàn cục cho endpoint này. Giữ `response.json`, vì middleware MPP gắn receipt của nó vào đó.

Với Python, Go và Rust, không có ví dụ SDK nào cho một endpoint. Xây dựng cùng cách chọn request, hoặc dùng một endpoint cho mỗi giao thức.

### Kiểm tra kích thước các header của response

Một response `402` với cả hai giao thức có các header lớn hơn. Proxy có thể từ chối response đó. Ví dụ, NGINX có thể trả về `502` với `upstream sent too big header`. Gửi một request chưa thanh toán qua hostname công khai của bạn, không chỉ đến server của ứng dụng.

Nếu các header quá lớn, làm một trong các việc sau:

* Công bố ít tùy chọn hơn.
* Tăng bộ đệm cho các header của response trong proxy. Với NGINX, đó là `proxy_buffer_size`.
* Dùng một endpoint cho mỗi giao thức.

### Tính đến thời điểm thanh toán

Hai middleware chạy handler của bạn vào các thời điểm khác nhau.

| Middleware | Thứ tự | Lỗi cần tính đến |
| - | - | - |
| MPP | Middleware chấp nhận khoản thanh toán. Sau đó, handler của bạn chạy. | Khoản thanh toán thành công, sau đó handler của bạn lỗi. |
| x402 | Middleware kiểm tra khoản thanh toán và chạy handler của bạn. Sau đó, middleware quyết toán khoản thanh toán và trả về response. | Handler của bạn chạy, sau đó quyết toán lỗi. |

Không bao giờ tự động thử lại một khoản thanh toán lỗi qua giao thức còn lại. Một timeout có thể để khoản thanh toán đầu tiên ở trạng thái không xác định. Kiểm tra trạng thái của khoản thanh toán đó trước. [Webhook](https://ifdocdemo.lumivescence.com/vi/sellers/webhooks.md) cho bạn biết trạng thái của từng khoản thanh toán.

## Kiểm tra chữ ký trusted agent

`@inflowpayai/tap-seller` kiểm tra chữ ký request của trusted agent trên chính server của bạn. Xem [Tham khảo SDK](https://ifdocdemo.lumivescence.com/vi/reference/sdk-packages.md).

## Bước tiếp theo

> [card] [Kiểm thử thanh toán](https://ifdocdemo.lumivescence.com/vi/sellers/test-a-payment.md)
>
> Thanh toán cho endpoint của chính bạn trong sandbox.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.