plaid

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Plaid Banking API Expert

Plaid银行API专家

Plaid connects applications to users' bank accounts for financial data access, payments, and identity verification.
Plaid可将应用与用户的银行账户连接,实现金融数据访问、支付和身份验证功能。

When to Use

适用场景

  • Connecting bank accounts in fintech apps
  • Implementing Plaid Link flow
  • Retrieving transactions, balances, or account info
  • Setting up ACH transfers
  • Identity/income verification
  • Handling Plaid webhooks
  • 在金融科技应用中连接银行账户
  • 实现Plaid Link流程
  • 获取交易记录、账户余额或账户信息
  • 设置ACH转账
  • 身份/收入验证
  • 处理Plaid webhooks

Core Products

核心产品

ProductPurpose
AuthBank account/routing numbers for ACH
TransactionsTransaction history (up to 24 months)
IdentityVerify user via bank account ownership
BalanceReal-time account balances
InvestmentsHoldings from investment accounts
LiabilitiesLoan and credit card data
产品用途
Auth获取用于ACH的银行账户/路由号码
Transactions获取交易记录(最长24个月)
Identity通过银行账户所有权验证用户身份
Balance查询实时账户余额
Investments获取投资账户的持仓信息
Liabilities获取贷款和信用卡数据

Quick Start

快速开始

1. Install SDK

1. 安装SDK

bash
npm install plaid react-plaid-link
bash
npm install plaid react-plaid-link

2. Create Plaid Client

2. 创建Plaid客户端

typescript
import { Configuration, PlaidApi, PlaidEnvironments } from "plaid";

const client = new PlaidApi(
  new Configuration({
    basePath: PlaidEnvironments.sandbox,
    baseOptions: {
      headers: {
        "PLAID-CLIENT-ID": process.env.PLAID_CLIENT_ID,
        "PLAID-SECRET": process.env.PLAID_SECRET,
      },
    },
  })
);
typescript
import { Configuration, PlaidApi, PlaidEnvironments } from "plaid";

const client = new PlaidApi(
  new Configuration({
    basePath: PlaidEnvironments.sandbox,
    baseOptions: {
      headers: {
        "PLAID-CLIENT-ID": process.env.PLAID_CLIENT_ID,
        "PLAID-SECRET": process.env.PLAID_SECRET,
      },
    },
  })
);

3. Create Link Token (Server)

3. 创建Link Token(服务端)

typescript
// POST /api/plaid/create-link-token
export async function POST(req: Request) {
  const response = await client.linkTokenCreate({
    user: { client_user_id: userId },
    client_name: "Your App",
    products: ["auth", "transactions"],
    country_codes: ["US"],
    language: "en",
  });

  return Response.json({ link_token: response.data.link_token });
}
typescript
// POST /api/plaid/create-link-token
export async function POST(req: Request) {
  const response = await client.linkTokenCreate({
    user: { client_user_id: userId },
    client_name: "Your App",
    products: ["auth", "transactions"],
    country_codes: ["US"],
    language: "en",
  });

  return Response.json({ link_token: response.data.link_token });
}

4. Plaid Link (Client)

4. 集成Plaid Link(客户端)

tsx
import { usePlaidLink } from "react-plaid-link";

function ConnectBank({ linkToken }) {
  const { open, ready } = usePlaidLink({
    token: linkToken,
    onSuccess: async (public_token, metadata) => {
      // Exchange for access_token on server
      await fetch("/api/plaid/exchange-token", {
        method: "POST",
        body: JSON.stringify({ public_token }),
      });
    },
  });

  return (
    <button onClick={() => open()} disabled={!ready}>
      Connect Bank Account
    </button>
  );
}
tsx
import { usePlaidLink } from "react-plaid-link";

function ConnectBank({ linkToken }) {
  const { open, ready } = usePlaidLink({
    token: linkToken,
    onSuccess: async (public_token, metadata) => {
      // 在服务端兑换为access_token
      await fetch("/api/plaid/exchange-token", {
        method: "POST",
        body: JSON.stringify({ public_token }),
      });
    },
  });

  return (
    <button onClick={() => open()} disabled={!ready}>
      连接银行账户
    </button>
  );
}

5. Exchange Token (Server)

5. 兑换Token(服务端)

typescript
// POST /api/plaid/exchange-token
export async function POST(req: Request) {
  const { public_token } = await req.json();

  const response = await client.itemPublicTokenExchange({
    public_token,
  });

  // Store access_token securely (encrypted in database)
  await db.users.update(userId, {
    plaid_access_token: response.data.access_token,
    plaid_item_id: response.data.item_id,
  });

  return Response.json({ success: true });
}
typescript
// POST /api/plaid/exchange-token
export async function POST(req: Request) {
  const { public_token } = await req.json();

  const response = await client.itemPublicTokenExchange({
    public_token,
  });

  // 安全存储access_token(加密后存入数据库)
  await db.users.update(userId, {
    plaid_access_token: response.data.access_token,
    plaid_item_id: response.data.item_id,
  });

  return Response.json({ success: true });
}

Data Retrieval

数据获取

Get Auth (Account/Routing Numbers)

获取Auth信息(账户/路由号码)

typescript
const response = await client.authGet({ access_token });

const ach = response.data.numbers.ach[0];
console.log("Account:", ach.account);
console.log("Routing:", ach.routing);
typescript
const response = await client.authGet({ access_token });

const ach = response.data.numbers.ach[0];
console.log("Account:", ach.account);
console.log("Routing:", ach.routing);

Get Transactions

获取交易记录

typescript
const response = await client.transactionsGet({
  access_token,
  start_date: "2024-01-01",
  end_date: "2024-12-31",
});

let transactions = response.data.transactions;

// Handle pagination
while (transactions.length < response.data.total_transactions) {
  const more = await client.transactionsGet({
    access_token,
    start_date: "2024-01-01",
    end_date: "2024-12-31",
    offset: transactions.length,
  });
  transactions = transactions.concat(more.data.transactions);
}
Transaction object:
typescript
{
  transaction_id: "abc123",
  amount: 12.34,           // Positive = outflow
  date: "2024-11-16",
  name: "Starbucks",
  merchant_name: "Starbucks",
  category: ["Food and Drink", "Coffee Shop"],
  pending: false,
}
typescript
const response = await client.transactionsGet({
  access_token,
  start_date: "2024-01-01",
  end_date: "2024-12-31",
});

let transactions = response.data.transactions;

// 处理分页
while (transactions.length < response.data.total_transactions) {
  const more = await client.transactionsGet({
    access_token,
    start_date: "2024-01-01",
    end_date: "2024-12-31",
    offset: transactions.length,
  });
  transactions = transactions.concat(more.data.transactions);
}
交易对象结构:
typescript
{
  transaction_id: "abc123",
  amount: 12.34,           // 正数表示支出
  date: "2024-11-16",
  name: "Starbucks",
  merchant_name: "Starbucks",
  category: ["Food and Drink", "Coffee Shop"],
  pending: false,
}

Get Balance

获取账户余额

typescript
const response = await client.accountsBalanceGet({ access_token });

response.data.accounts.forEach((account) => {
  console.log(`${account.name}: $${account.balances.current}`);
});
typescript
const response = await client.accountsBalanceGet({ access_token });

response.data.accounts.forEach((account) => {
  console.log(`${account.name}: $${account.balances.current}`);
});

Get Identity

获取身份信息

typescript
const response = await client.identityGet({ access_token });

const owner = response.data.accounts[0].owners[0];
console.log("Name:", owner.names[0]);
console.log("Email:", owner.emails[0].data);
console.log("Phone:", owner.phone_numbers[0].data);
typescript
const response = await client.identityGet({ access_token });

const owner = response.data.accounts[0].owners[0];
console.log("Name:", owner.names[0]);
console.log("Email:", owner.emails[0].data);
console.log("Phone:", owner.phone_numbers[0].data);

Webhooks

Webhooks

Setup Endpoint

设置端点

typescript
// POST /api/plaid/webhook
export async function POST(req: Request) {
  const { webhook_type, webhook_code, item_id } = await req.json();

  switch (webhook_type) {
    case "TRANSACTIONS":
      if (webhook_code === "DEFAULT_UPDATE") {
        // New transactions available - fetch them
        await syncTransactions(item_id);
      }
      break;

    case "ITEM":
      if (webhook_code === "ERROR") {
        // Connection issue - prompt user to re-authenticate
        await notifyUserReauth(item_id);
      }
      break;
  }

  return Response.json({ received: true });
}
Key webhook events:
EventMeaning
TRANSACTIONS: INITIAL_UPDATE
First batch ready
TRANSACTIONS: DEFAULT_UPDATE
New transactions
ITEM: ERROR
Connection issue
ITEM: PENDING_EXPIRATION
Credentials expiring
typescript
// POST /api/plaid/webhook
export async function POST(req: Request) {
  const { webhook_type, webhook_code, item_id } = await req.json();

  switch (webhook_type) {
    case "TRANSACTIONS":
      if (webhook_code === "DEFAULT_UPDATE") {
        // 有新交易记录可用 - 获取数据
        await syncTransactions(item_id);
      }
      break;

    case "ITEM":
      if (webhook_code === "ERROR") {
        // 连接出现问题 - 提示用户重新认证
        await notifyUserReauth(item_id);
      }
      break;
  }

  return Response.json({ received: true });
}
关键Webhook事件:
事件含义
TRANSACTIONS: INITIAL_UPDATE
第一批交易数据已就绪
TRANSACTIONS: DEFAULT_UPDATE
有新的交易记录
ITEM: ERROR
连接出现问题
ITEM: PENDING_EXPIRATION
凭证即将过期

Environments

运行环境

EnvironmentUse CaseBase Path
SandboxTesting
PlaidEnvironments.sandbox
DevelopmentLimited live (100 connections)
PlaidEnvironments.development
ProductionLive
PlaidEnvironments.production
环境适用场景基础路径
Sandbox测试环境
PlaidEnvironments.sandbox
Development有限的真实环境(最多100个连接)
PlaidEnvironments.development
Production生产环境
PlaidEnvironments.production

Sandbox Test Credentials

Sandbox测试凭证

  • Username:
    user_good
  • Password:
    pass_good
  • MFA:
    1234
  • 用户名:
    user_good
  • 密码:
    pass_good
  • 多因素认证码:
    1234

Error Handling

错误处理

Re-authentication (Update Mode)

重新认证(更新模式)

When credentials expire:
typescript
// Create link token for update mode
const response = await client.linkTokenCreate({
  user: { client_user_id: userId },
  client_name: "Your App",
  access_token: existingAccessToken, // Triggers update mode
  country_codes: ["US"],
  language: "en",
});
当凭证过期时:
typescript
// 创建用于更新模式的link token
const response = await client.linkTokenCreate({
  user: { client_user_id: userId },
  client_name: "Your App",
  access_token: existingAccessToken, // 触发更新模式
  country_codes: ["US"],
  language: "en",
});

Common Errors

常见错误

ErrorSolution
ITEM_LOGIN_REQUIRED
Re-authenticate via Link update mode
RATE_LIMIT_EXCEEDED
Implement exponential backoff
PRODUCT_NOT_READY
Wait for webhook or retry
错误解决方案
ITEM_LOGIN_REQUIRED
通过Link更新模式重新认证
RATE_LIMIT_EXCEEDED
实现指数退避机制
PRODUCT_NOT_READY
等待Webhook通知或重试

Security Best Practices

安全最佳实践

DO:
  • Store access tokens encrypted in database
  • Use environment variables for credentials
  • Verify webhook signatures
  • Use HTTPS for all endpoints
DON'T:
  • Expose secret keys client-side
  • Log access tokens
  • Store credentials in code
建议:
  • 将access token加密后存储在数据库中
  • 使用环境变量存储凭证
  • 验证Webhook签名
  • 所有端点使用HTTPS
禁止:
  • 在客户端暴露密钥
  • 记录access token
  • 在代码中硬编码凭证

Next.js App Router Example

Next.js App Router示例

typescript
// app/api/plaid/create-link-token/route.ts
import { NextResponse } from "next/server";
import { Configuration, PlaidApi, PlaidEnvironments } from "plaid";

const client = new PlaidApi(
  new Configuration({
    basePath: PlaidEnvironments.sandbox,
    baseOptions: {
      headers: {
        "PLAID-CLIENT-ID": process.env.PLAID_CLIENT_ID!,
        "PLAID-SECRET": process.env.PLAID_SECRET!,
      },
    },
  })
);

export async function POST(req: Request) {
  const session = await getSession();

  const response = await client.linkTokenCreate({
    user: { client_user_id: session.user.id },
    client_name: "Your App",
    products: ["auth", "transactions"],
    country_codes: ["US"],
    language: "en",
    webhook: `${process.env.NEXT_PUBLIC_URL}/api/plaid/webhook`,
  });

  return NextResponse.json({ link_token: response.data.link_token });
}
typescript
// app/api/plaid/create-link-token/route.ts
import { NextResponse } from "next/server";
import { Configuration, PlaidApi, PlaidEnvironments } from "plaid";

const client = new PlaidApi(
  new Configuration({
    basePath: PlaidEnvironments.sandbox,
    baseOptions: {
      headers: {
        "PLAID-CLIENT-ID": process.env.PLAID_CLIENT_ID!,
        "PLAID-SECRET": process.env.PLAID_SECRET!,
      },
    },
  })
);

export async function POST(req: Request) {
  const session = await getSession();

  const response = await client.linkTokenCreate({
    user: { client_user_id: session.user.id },
    client_name: "Your App",
    products: ["auth", "transactions"],
    country_codes: ["US"],
    language: "en",
    webhook: `${process.env.NEXT_PUBLIC_URL}/api/plaid/webhook`,
  });

  return NextResponse.json({ link_token: response.data.link_token });
}

Implementation Checklist

实施检查清单

  • Sign up for Plaid account
  • Get client ID and secret
  • Install
    plaid
    and
    react-plaid-link
  • Set environment variables
  • Create link token endpoint
  • Implement token exchange endpoint
  • Integrate Plaid Link on frontend
  • Store access tokens securely
  • Set up webhook endpoint
  • Handle re-authentication errors
  • Test with sandbox credentials
  • 注册Plaid账户
  • 获取客户端ID和密钥
  • 安装
    plaid
    react-plaid-link
  • 设置环境变量
  • 创建Link Token端点
  • 实现Token兑换端点
  • 在前端集成Plaid Link
  • 安全存储access token
  • 设置Webhook端点
  • 处理重新认证错误
  • 使用Sandbox测试凭证进行测试

Resources

相关资源