zoho-api

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Zoho API Integration

Zoho API集成

Quick Reference

快速参考

APIBase URL
Zoho Inventory
https://www.zohoapis.com/inventory/v1
Zoho Books
https://www.zohoapis.com/books/v3
Organization ID:
748369814
API基础URL
Zoho Inventory
https://www.zohoapis.com/inventory/v1
Zoho Books
https://www.zohoapis.com/books/v3
组织ID
748369814

Code Files

代码文件

FilePurpose
src/lib/zoho/client.ts
OAuth client, token caching,
zohoFetch
src/lib/zoho/products.ts
Products, stock extraction
src/lib/zoho/price-lists.ts
Price list constants and fetching
src/lib/zoho/customers.ts
Customer lookup by email
src/lib/zoho/orders.ts
Sales orders
src/lib/zoho/invoices.ts
Invoices
src/lib/zoho/payments.ts
Payments
src/lib/zoho/credit-notes.ts
Credit notes
文件用途
src/lib/zoho/client.ts
OAuth客户端、令牌缓存、
zohoFetch
方法
src/lib/zoho/products.ts
产品、库存提取
src/lib/zoho/price-lists.ts
价格列表常量与获取
src/lib/zoho/customers.ts
通过邮箱查找客户
src/lib/zoho/orders.ts
销售订单
src/lib/zoho/invoices.ts
发票
src/lib/zoho/payments.ts
付款
src/lib/zoho/credit-notes.ts
贷项通知单

Using zohoFetch

使用zohoFetch

typescript
import { zohoFetch } from '@/lib/zoho/client';

// GET request
const data = await zohoFetch('/inventory/v1/items', {
  params: {
    organization_id: process.env.ZOHO_ORGANIZATION_ID,
    page: 1,
    per_page: 100,
  },
});

// Single item with locations
const item = await zohoFetch(`/inventory/v1/items/${itemId}`, {
  params: { organization_id: process.env.ZOHO_ORGANIZATION_ID },
});
typescript
import { zohoFetch } from '@/lib/zoho/client';

// GET请求
const data = await zohoFetch('/inventory/v1/items', {
  params: {
    organization_id: process.env.ZOHO_ORGANIZATION_ID,
    page: 1,
    per_page: 100,
  },
});

// 包含库存位置的单个商品
const item = await zohoFetch(`/inventory/v1/items/${itemId}`, {
  params: { organization_id: process.env.ZOHO_ORGANIZATION_ID },
});

Token Caching (CRITICAL)

令牌缓存(至关重要)

Memory Cache → Upstash Redis → Zoho OAuth Refresh
              (50-min TTL)    (rate limit: 10s guard)
If all prices show "Contact for price", check:
  1. Upstash env vars in Vercel
  2. UPSTASH_REDIS_REST_URL
    and
    UPSTASH_REDIS_REST_TOKEN
  3. Run:
    curl https://www.tsh.sale/api/debug/token
内存缓存 → Upstash Redis → Zoho OAuth刷新
              (50分钟TTL)    (速率限制:10秒防护)
如果所有商品都显示“联系我们获取价格”,请检查:
  1. Vercel中的Upstash环境变量
  2. UPSTASH_REDIS_REST_URL
    UPSTASH_REDIS_REST_TOKEN
  3. 运行:
    curl https://www.tsh.sale/api/debug/token

Common Endpoints

常用端点

Inventory API

Inventory API

GET /items                      # List products
GET /items/{id}                 # Single product (includes locations)
GET /categories                 # List categories
GET /pricebooks/{id}            # Pricebook with items
GET /items                      # 列出产品
GET /items/{id}                 # 单个产品(包含库存位置)
GET /categories                 # 列出分类
GET /pricebooks/{id}            # 包含商品的价格手册

Books API

Books API

GET /contacts                   # List customers
GET /salesorders                # Sales orders
GET /invoices                   # Invoices
GET /customerpayments           # Payments
GET /creditnotes                # Credit notes
GET /contacts                   # 列出客户
GET /salesorders                # 销售订单
GET /invoices                   # 发票
GET /customerpayments           # 客户付款
GET /creditnotes                # 贷项通知单

Caching with unstable_cache

使用unstable_cache进行缓存

typescript
import { unstable_cache } from 'next/cache';

const getCachedProducts = unstable_cache(
  async () => await fetchProducts(),
  ['products'],
  { revalidate: 3600, tags: ['products'] }
);
Revalidate cache:
bash
curl "https://www.tsh.sale/api/revalidate?tag=products&secret=tsh-revalidate-2024"
typescript
import { unstable_cache } from 'next/cache';

const getCachedProducts = unstable_cache(
  async () => await fetchProducts(),
  ['products'],
  { revalidate: 3600, tags: ['products'] }
);
刷新缓存:
bash
curl "https://www.tsh.sale/api/revalidate?tag=products&secret=tsh-revalidate-2024"

Error Handling Pattern

错误处理模式

typescript
try {
  const data = await zohoFetch('/inventory/v1/items', { ... });
} catch (error) {
  if (error.message.includes('401')) {
    // Token expired - auto-refreshes
  } else if (error.message.includes('429')) {
    // Rate limited - wait and retry
  }
}
typescript
try {
  const data = await zohoFetch('/inventory/v1/items', { ... });
} catch (error) {
  if (error.message.includes('401')) {
    // 令牌过期 - 自动刷新
  } else if (error.message.includes('429')) {
    // 速率受限 - 等待并重试
  }
}

Creating New API Route

创建新API路由

typescript
// src/app/api/zoho/[resource]/route.ts
import { NextResponse } from 'next/server';
import { zohoFetch } from '@/lib/zoho/client';

export async function GET(request: Request) {
  const data = await zohoFetch('/inventory/v1/items', {
    params: {
      organization_id: process.env.ZOHO_ORGANIZATION_ID,
    },
  });

  return NextResponse.json(data);
}
typescript
// src/app/api/zoho/[resource]/route.ts
import { NextResponse } from 'next/server';
import { zohoFetch } from '@/lib/zoho/client';

export async function GET(request: Request) {
  const data = await zohoFetch('/inventory/v1/items', {
    params: {
      organization_id: process.env.ZOHO_ORGANIZATION_ID,
    },
  });

  return NextResponse.json(data);
}

Rate Limits

速率限制

APILimit
OAuth Refresh~100/minute
Inventory API100/minute
Books API100/minute
API限制
OAuth刷新~100次/分钟
Inventory API100次/分钟
Books API100次/分钟

Debug Commands

调试命令

bash
undefined
bash
undefined

Check token

检查令牌

Check prices

检查价格

Check stock

检查库存

Revalidate cache

刷新所有缓存