paystack-setup
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePaystack Setup
Paystack 环境搭建
Set up the foundational Paystack API client and environment configuration for TypeScript/JavaScript server-side applications.
为TypeScript/JavaScript服务端应用搭建Paystack API客户端及环境配置的基础框架。
API Fundamentals
API 基础信息
| Property | Value |
|---|---|
| Base URL | |
| Auth Header | |
| Content Type | |
| Response Format | |
| Amount Unit | Subunit of currency (multiply display amount × 100) |
| Transaction ID | Unsigned 64-bit integer — use |
| 属性 | 值 |
|---|---|
| 基础URL | |
| 认证请求头 | |
| 内容类型 | |
| 响应格式 | |
| 金额单位 | 货币的Subunit(将显示金额 × 100) |
| 交易ID | 无符号64位整数 — 在TypeScript中使用 |
Supported Currencies & Subunits
支持的货币及Subunit
| Currency | Code | Subunit | Multiplier |
|---|---|---|---|
| Nigerian Naira | NGN | kobo | ×100 |
| US Dollar | USD | cent | ×100 |
| Ghanaian Cedi | GHS | pesewa | ×100 |
| South African Rand | ZAR | cent | ×100 |
| Kenyan Shilling | KES | cent | ×100 |
| West African CFA | XOF | — | ×100 |
| Egyptian Pound | EGP | piaster | ×100 |
| Rwandan Franc | RWF | — | ×100 |
| 货币 | 代码 | Subunit | 转换乘数 |
|---|---|---|---|
| 尼日利亚奈拉 | NGN | kobo | ×100 |
| 美元 | USD | cent | ×100 |
| 加纳塞地 | GHS | pesewa | ×100 |
| 南非兰特 | ZAR | cent | ×100 |
| 肯尼亚先令 | KES | cent | ×100 |
| 西非金融共同体法郎 | XOF | — | ×100 |
| 埃及镑 | EGP | piaster | ×100 |
| 卢旺达法郎 | RWF | — | ×100 |
Environment Variables
环境变量
Create a file (or for Next.js):
.env.env.localenv
PAYSTACK_SECRET_KEY=sk_test_xxxxx # Server-side only, NEVER expose to client
NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY=pk_test_xxxxx # Safe for client-side (Popup/InlineJS)The secret key () must NEVER appear in client-side code, browser bundles, or public repositories. The public key () is safe for front-end use with Paystack Popup/InlineJS only.
sk_*pk_*Test keys start with / . Live keys start with / . Get them from the Paystack Dashboard under Settings → API Keys & Webhooks.
sk_test_pk_test_sk_live_pk_live_创建文件(Next.js项目使用):
.env.env.localenv
PAYSTACK_SECRET_KEY=sk_test_xxxxx # 仅用于服务端,绝不可暴露给客户端
NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY=pk_test_xxxxx # 可安全用于客户端(Popup/InlineJS)密钥()绝不能出现在客户端代码、浏览器打包文件或公开代码仓库中。公钥()仅可安全用于前端的Paystack Popup/InlineJS功能。
sk_*pk_*测试密钥以 / 开头,正式密钥以 / 开头。可在Paystack控制台的“设置 → API密钥与Webhooks”中获取。
sk_test_pk_test_sk_live_pk_live_Install Dependencies
安装依赖
bash
undefinedbash
undefinedFor client-side Popup/InlineJS checkout
用于客户端Popup/InlineJS结账功能
npm install @paystack/inline-js
npm install @paystack/inline-js
Or with pnpm / yarn
或使用pnpm / yarn
pnpm add @paystack/inline-js
yarn add @paystack/inline-js
No server-side SDK is needed — use the built-in `fetch` API with the helper below.pnpm add @paystack/inline-js
yarn add @paystack/inline-js
无需安装服务端SDK — 使用内置的`fetch` API结合下方的辅助工具即可。TypeScript API Client Helper
TypeScript API客户端辅助工具
Create a reusable, type-safe Paystack client. Every other Paystack skill depends on this pattern:
typescript
// lib/paystack.ts
const PAYSTACK_SECRET_KEY = process.env.PAYSTACK_SECRET_KEY;
if (!PAYSTACK_SECRET_KEY) {
throw new Error("PAYSTACK_SECRET_KEY is not set in environment variables");
}
export interface PaystackResponse<T = unknown> {
status: boolean;
message: string;
data: T;
}
export interface PaystackListResponse<T = unknown> {
status: boolean;
message: string;
data: T[];
meta: {
total: number;
skipped: number;
perPage: number;
page: number;
pageCount: number;
};
}
export class PaystackError extends Error {
constructor(
message: string,
public statusCode: number,
public response?: unknown
) {
super(message);
this.name = "PaystackError";
}
}
export async function paystackRequest<T>(
endpoint: string,
options: RequestInit = {}
): Promise<PaystackResponse<T>> {
const url = `https://api.paystack.co${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${PAYSTACK_SECRET_KEY}`,
"Content-Type": "application/json",
...options.headers,
},
});
const data = await response.json();
if (!response.ok) {
throw new PaystackError(
data.message || `Paystack API error: ${response.status}`,
response.status,
data
);
}
return data as PaystackResponse<T>;
}创建一个可复用、类型安全的Paystack客户端。所有其他Paystack技能均依赖此模式:
typescript
// lib/paystack.ts
const PAYSTACK_SECRET_KEY = process.env.PAYSTACK_SECRET_KEY;
if (!PAYSTACK_SECRET_KEY) {
throw new Error("PAYSTACK_SECRET_KEY is not set in environment variables");
}
export interface PaystackResponse<T = unknown> {
status: boolean;
message: string;
data: T;
}
export interface PaystackListResponse<T = unknown> {
status: boolean;
message: string;
data: T[];
meta: {
total: number;
skipped: number;
perPage: number;
page: number;
pageCount: number;
};
}
export class PaystackError extends Error {
constructor(
message: string,
public statusCode: number,
public response?: unknown
) {
super(message);
this.name = "PaystackError";
}
}
export async function paystackRequest<T>(
endpoint: string,
options: RequestInit = {}
): Promise<PaystackResponse<T>> {
const url = `https://api.paystack.co${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${PAYSTACK_SECRET_KEY}`,
"Content-Type": "application/json",
...options.headers,
},
});
const data = await response.json();
if (!response.ok) {
throw new PaystackError(
data.message || `Paystack API error: ${response.status}`,
response.status,
data
);
}
return data as PaystackResponse<T>;
}Pagination
分页机制
All list endpoints accept (default: 50, max: 100) and (default: 1) as query parameters. The response includes a object:
perPagepagemetajson
{
"meta": {
"total": 243,
"skipped": 0,
"perPage": 50,
"page": 1,
"pageCount": 5
}
}Build paginated queries like so:
typescript
const params = new URLSearchParams({
perPage: "20",
page: "2",
from: "2024-01-01T00:00:00.000Z",
to: "2024-12-31T23:59:59.000Z",
});
const result = await paystackRequest<Transaction[]>(`/transaction?${params}`);所有列表接口均接受(默认值:50,最大值:100)和(默认值:1)作为查询参数。响应结果包含一个对象:
perPagepagemetajson
{
"meta": {
"total": 243,
"skipped": 0,
"perPage": 50,
"page": 1,
"pageCount": 5
}
}可按如下方式构建分页查询:
typescript
const params = new URLSearchParams({
perPage: "20",
page: "2",
from: "2024-01-01T00:00:00.000Z",
to: "2024-12-31T23:59:59.000Z",
});
const result = await paystackRequest<Transaction[]>(`/transaction?${params}`);Amount Conversion
金额转换
Always convert display amounts to subunits before sending to Paystack, and convert back when displaying:
typescript
// Display → Paystack (multiply by 100)
const amountInKobo = Math.round(amountInNaira * 100);
// Paystack → Display (divide by 100)
const amountInNaira = amountInKobo / 100;Use to avoid floating-point issues like .
Math.round()19.99 * 100 = 1998.9999999999998在向Paystack发送金额前,务必将显示金额转换为subunit,显示时再转换回来:
typescript
// 显示金额 → Paystack格式(乘以100)
const amountInKobo = Math.round(amountInNaira * 100);
// Paystack格式 → 显示金额(除以100)
const amountInNaira = amountInKobo / 100;使用避免浮点数问题,例如。
Math.round()19.99 * 100 = 1998.9999999999998HTTP Methods
HTTP方法
| Method | Usage |
|---|---|
| POST | Create resources, initiate actions |
| GET | Fetch, list, verify resources |
| PUT | Update resources |
| DELETE | Deactivate or remove resources |
| 方法 | 用途 |
|---|---|
| POST | 创建资源、发起操作 |
| GET | 获取、列出、验证资源 |
| PUT | 更新资源 |
| DELETE | 停用或删除资源 |
Error Handling
错误处理
Wrap Paystack calls in try/catch and handle the class:
PaystackErrortypescript
import { paystackRequest, PaystackError } from "@/lib/paystack";
try {
const result = await paystackRequest<Transaction>("/transaction/verify/ref_123");
} catch (error) {
if (error instanceof PaystackError) {
console.error(`Paystack error ${error.statusCode}: ${error.message}`);
// Handle specific status codes
if (error.statusCode === 400) { /* bad request / validation error */ }
if (error.statusCode === 401) { /* invalid secret key */ }
if (error.statusCode === 404) { /* resource not found */ }
}
throw error;
}将Paystack调用包裹在try/catch中,并处理类:
PaystackErrortypescript
import { paystackRequest, PaystackError } from "@/lib/paystack";
try {
const result = await paystackRequest<Transaction>("/transaction/verify/ref_123");
} catch (error) {
if (error instanceof PaystackError) {
console.error(`Paystack error ${error.statusCode}: ${error.message}`);
// 处理特定状态码
if (error.statusCode === 400) { /* 请求无效 / 验证错误 */ }
if (error.statusCode === 401) { /* 密钥无效 */ }
if (error.statusCode === 404) { /* 资源未找到 */ }
}
throw error;
}Security Checklist
安全检查清单
- Store in environment variables only, never in code
PAYSTACK_SECRET_KEY - Add and
.envto.env.local.gitignore - All Paystack API calls must run server-side (API routes, server actions, backend)
- Use HTTPS for all callback and webhook URLs
- Validate amounts server-side before initializing transactions
- Always verify transaction status server-side after payment, never trust client-side callbacks alone
- 仅将存储在环境变量中,绝不要写入代码
PAYSTACK_SECRET_KEY - 将和
.env添加到.env.local.gitignore - 所有Paystack API调用必须在服务端运行(API路由、服务端操作、后端)
- 所有回调和Webhook URL均使用HTTPS
- 在初始化交易前,在服务端验证金额
- 支付完成后,务必在服务端验证交易状态,绝不要仅信任客户端回调