Corsair Razorpay 插件实战:25 个类型化支付端点、API Key 鉴权与 Webhook 签名校验全指南 Corsair Razorpay 插件实战25 个类型化支付端点、API Key 鉴权与 Webhook 签名校验全指南【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair本篇技术指南以 Corsair 仓库中的corsair-dev/razorpay插件packages/razorpay/README.md为核心系统讲解如何把印度主流支付网关 Razorpay 接入 Corsair从安装注册、租户 API Key 鉴权到 25 个类型化端点、7 个本地同步实体与 4 类 Webhook 事件的完整用法并深入源码剖析底层请求封装、HMAC 签名校验、幂等键与错误重试机制。读完你将能基于该插件为多租户应用快速实现订单、收款、退款、分账与订阅的端到端能力。插件概览一个包覆盖 Razorpay 全业务面corsair-dev/razorpay是 Corsair 官方的 Razorpay 集成插件版本见 packages/razorpay/package.json当前为0.1.5。它在 Corsair 的插件框架内把 Razorpay 的能力收敛为三类资产25 个类型化 API 操作覆盖customers、orders、payments、payouts、refunds、settlements、subscriptions七大资源域7 个本地同步实体调用 API 的同时把结果写入本地数据库支持.search()/.list()快速检索4 类入站 Webhook 事件支付捕获、支付失败、订单支付成功、退款处理完成均带签名校验。插件本身以 ESM 构建仅依赖corsair0.1.0与zod^4.1.13作为 peer dependency类型定义dist/index.d.ts随包发布因此所有razorpay.api.*调用在编译期即有完整输入/输出类型约束。安装与接入安装在 README 中推荐使用 pnpm 安装pnpm add corsair-dev/razorpay当然你也可以使用仓库其他 demo 中常见的 npm / yarn / bun 等价命令安装corsair与corsair-dev/razorpay两个包具体多包管理器示例参见 docs/plugins/razorpay/overview.mdx。注册插件在创建 Corsair 实例时把razorpay()放入plugins数组即可import Database from better-sqlite3; import { createCorsair } from corsair; import { razorpay } from corsair-dev/razorpay; export const corsair createCorsair({ plugins: [ razorpay(), ], database: new Database(corsair.db), kek: process.env.CORSAIR_KEK!, hub: { projectApiKey: process.env.CORSAIR_API_KEY!, signingSecret: process.env.CORSAIR_SIGNING_SECRET!, }, });从 packages/razorpay/index.ts 的工厂函数实现可以看到razorpay()默认的authType是api_key返回的插件对象携带了id: razorpay、认证配置、数据 Schema、端点/Webhook 映射、风险元数据、签名匹配器与 keyBuilder 等完整插件协议。多租户是 Corsair 的默认形态所有调用通过corsair.withTenant(id)限定租户上下文参见 docs/concepts/multi-tenancy.mdx。连接租户租户的凭证通过 Hub 托管的分发页完成首次连接const { connectUrl } await corsair.manage.connect.createLink({ plugin: razorpay, tenantId: acme, }); // 将用户的浏览器重定向到 connectUrl连接完成后Hub 把结果回传给应用完整流程见 docs/management/connect.mdx。认证API Key 与首次使用提示README 明确说明Auth 为 API KeyCorsair 会在租户首次使用时提示其填写凭证。也就是说接入方无需在初始化时硬编码密钥租户侧首次发起请求时会被引导补充keyId与keySecret。这个按需取钥的逻辑实现在 packages/razorpay/index.ts 的keyBuilder中它按调用来源分四种情况取凭证Webhook 且配置了webhookSecret直接返回该选项值Webhook 未配置从ctx.keys.get_webhook_signature()读取Endpoint 且传入keyId/keySecret选项拼接为keyId:keySecretEndpoint 且认证类型为api_key从ctx.keys.get_api_key()读取。任一来源都取不到凭证时抛出AuthMissingError(razorpay, api_key)这正是首次使用提示的触发点。对应地插件声明了api_key与account_id的关联razorpayAuthConfig租户凭证按账户维度管理。Basic Auth 底层实现Razorpay 使用keyId:keySecret做 Basic Auth。packages/razorpay/client.ts 的makeRazorpayRequest展示了底层细节请求基址固定为https://api.razorpay.com/v1Authorization头为Basic base64(keyId:keySecret)Content-Type: application/json写方法POST/PUT/PATCH发送body读方法GET携带query参数。25 个类型化端点端点总表以下是 README 中列出的全部端点、Operation ID 与风险等级写入时由 packages/razorpay/index.ts 的razorpayEndpointMeta定义与表完全一致OperationOperation IDRiskDescriptioncustomers.createrazorpay.api.customers.createwriteCreate a Razorpay customercustomers.getrazorpay.api.customers.getreadFetch a Razorpay customer by IDcustomers.listrazorpay.api.customers.listreadList Razorpay customerscustomers.updaterazorpay.api.customers.updatewriteUpdate a Razorpay customerorders.createrazorpay.api.orders.createwriteCreate a Razorpay orderorders.getrazorpay.api.orders.getreadFetch a Razorpay order by IDorders.listrazorpay.api.orders.listreadList Razorpay orderspayments.capturerazorpay.api.payments.capturewriteCapture an authorized Razorpay paymentpayments.getrazorpay.api.payments.getreadFetch a Razorpay payment by IDpayments.listrazorpay.api.payments.listreadList Razorpay paymentspayouts.createrazorpay.api.payouts.createwriteCreate a Razorpay payoutpayouts.getrazorpay.api.payouts.getreadFetch a Razorpay payout by IDpayouts.listrazorpay.api.payouts.listreadList Razorpay payoutsrefunds.createrazorpay.api.refunds.createwriteCreate a refund for a Razorpay paymentrefunds.getrazorpay.api.refunds.getreadFetch a specific refund for a Razorpay paymentrefunds.listrazorpay.api.refunds.listreadList refunds for a Razorpay paymentsettlements.getrazorpay.api.settlements.getreadFetch a Razorpay settlement by IDsettlements.listrazorpay.api.settlements.listreadList Razorpay settlementssubscriptions.cancelrazorpay.api.subscriptions.canceldestructiveCancel a Razorpay subscription [DESTRUCTIVE]subscriptions.createrazorpay.api.subscriptions.createwriteCreate a Razorpay subscriptionsubscriptions.getrazorpay.api.subscriptions.getreadFetch a Razorpay subscription by IDsubscriptions.listrazorpay.api.subscriptions.listreadList Razorpay subscriptionssubscriptions.pauserazorpay.api.subscriptions.pausewritePause a Razorpay subscriptionsubscriptions.resumerazorpay.api.subscriptions.resumewriteResume a paused Razorpay subscriptionsubscriptions.updaterazorpay.api.subscriptions.updatewriteUpdate a Razorpay subscription风险分级read / write / destructive端点元数据把每个操作分为三级风险供权限系统决策docs/concepts/permissions.mdx 有更系统的说明read查询类操作get / listwrite变更类操作create / update / capture / pause / resumedestructive唯一的一处是subscriptions.cancel元数据中额外标记了irreversible: true——取消订阅不可逆应作为高危操作对待。端点调用方式在业务代码中所有操作统一挂在corsair.razorpay.api命名空间下多租户场景先withTenantconst tenant corsair.withTenant(acme); // 创建客户 const customer await tenant.razorpay.api.customers.create({ name: Acme Customer, email: customeracme.com, contact: 9123456789, }); // 创建订单金额单位为印度卢比的最小货币单位 const order await tenant.razorpay.api.orders.create({ amount: 100, currency: INR, receipt: corsair-order-12345, notes: { order_source: corsair-test }, }); // 捕获已授权支付 await tenant.razorpay.api.payments.capture({ id: pay_xxxxxxxx, amount: 100, currency: INR, });每个操作的具体输入/输出字段必填项、类型、默认值都有对应的 Zod Schema 生成并发布在 docs/plugins/razorpay/api.mdx例如customers.create必填name可选email、contact、gstin、notes、fail_existingorders.create必填amount与currency可选receipt、notespayouts.create必填account_number、fund_account_id、amount、currency、mode、purpose可选queue_if_low_balance、reference_id、narrationrefunds.create必填paymentId可选amount、speednormal | optimum、receipt、notes列表类操作orders.list、payments.list等支持from、to、count、skip分页过滤参数。端点实现剖析以 Orders 为例以 packages/razorpay/endpoints/orders.ts 为样本可以看到每个端点的统一执行模式调用makeRazorpayRequest请求 Razorpay REST 接口如POST /orders、GET /orders/{id}、GET /orders结果返回后若ctx.db.orders存在则用upsertByEntityId(result.id, ...)把远端实体写入本地库通过logEventFromContext(ctx, razorpay.orders.create, ...)记录操作事件返回原始响应对象。特别值得注意的细节Razorpay 的created_at是秒级 Unix 时间戳插件在入库前会乘以 1000 转换为毫秒并构造Date对象见 orders.ts 中new Date(result.created_at * 1000)与本地库约定的createdAt: date字段对齐。这一转换在 orders、payments、webhook 各处理路径中保持一致。幂等性与特殊请求头makeRazorpayRequest的最后一个参数addIdempotencyKey专为 Payouts 设计开启后会在请求头注入X-Payout-Idempotency: uuidpackages/razorpay/client.ts避免资金类操作因网络重试而重复执行。测试中payouts.create/payouts.list均以true传入该参数见 packages/razorpay/api.test.ts。错误处理与重试策略插件内置了分层错误处理packages/razorpay/error-handlers.ts同时允许通过razorpay({ errorHandlers })覆盖或扩展RATE_LIMIT_ERROR命中429状态码或消息含rate_limited/429时触发返回maxRetries: 5并尽量携带服务端retryAfter作为退避时长AUTH_ERROR命中401或unauthorized/invalid_auth时触发maxRetries: 0认证失败重试无意义DEFAULT兜底策略maxRetries: 0。同时Razorpay 的错误响应会携带结构化错误体code、description、reason、source。makeRazorpayRequest的 catch 分支会把这些字段提取并包装为RazorpayAPIError重新抛出见 packages/razorpay/client.ts上层可以直接读取error.code/error.reason定位问题。更完整的错误处理概念见 docs/concepts/error-handling.mdx。本地数据同步7 个可检索实体Schema 与实体插件把 Razorpay 的 7 类资源建模为本地实体packages/razorpay/schema/index.tsSchema 版本为1.0.0export const RazorpaySchema { version: 1.0.0, entities: { orders: RazorpayOrder, payments: RazorpayPayment, payouts: RazorpayPayout, refunds: RazorpayRefund, customers: RazorpayCustomer, settlements: RazorpaySettlement, subscriptions: RazorpaySubscription, }, } as const;每个实体的字段定义在 packages/razorpay/schema/database.tsAPI 响应侧的 Zod Schema如RazorpayOrderSchema描述远端字段之后通过.extend({ createdAt: z.coerce.date() })派生出数据库侧 Schema实现远端数据与本地存储的无缝映射。所有 Schema 均以.loose()结尾即未知字段不会导致校验失败兼容 Razorpay API 的演进。查询同步数据同步后的实体支持search与list两种查询完整过滤字段与操作符见 docs/plugins/razorpay/database.mdxconst rows await corsair.razorpay.db.orders.search({ data: { status: paid, // string 字段支持 equals / contains / startsWith / endsWith / in amount: { gte: 1000 }, // number 字段支持 equals / gt / gte / lt / lte / in createdAt: { after: new Date(2026-01-01) }, // date 字段支持 equals / before / after / between }, limit: 100, offset: 0, });各实体的可检索字段覆盖了核心业务维度如orders可按amount_paid、amount_due、status、receipt过滤payments可按order_id、method、captured过滤subscriptions则暴露了plan_id、total_count、paid_count、remaining_count、current_start/current_end等订阅专属字段。数据同步与过滤操作符的通用语义参见 docs/concepts/database.mdx。Webhook 集成4 类事件与签名校验README 指出插件处理4 个 Webhook 事件。它们来自 Razorpay 的订单、支付、退款三类资源映射关系定义在 packages/razorpay/index.ts 与 packages/razorpay/webhooks/index.tsorders.paid订单支付成功payments.captured支付被捕获payments.failed支付失败refunds.processed退款处理完成。HTTP Handler 接入将 Razorpay 侧的订阅 URL 指向你的 Corsair HTTP Handler然后在 handler 中调用processWebhook示例见 docs/plugins/razorpay/webhooks.mdximport { processWebhook } from corsair; import { corsair } from /server/corsair; export async function POST(request: Request) { const headers Object.fromEntries(request.headers); const body await request.json(); const result await processWebhook(corsair, headers, body); return result.response; }事件匹配与签名校验每个事件处理器都做了两道防护实现见 packages/razorpay/webhooks/types.ts事件匹配createRazorpayMatch(order.paid)之类的匹配器要求请求头包含x-razorpay-signature且请求体中的event字段与事件类型严格相等HMAC 签名校验处理器内部调用verifyRazorpayWebhookSignature(request, ctx.key)用corsair/http的verifyHmacSignature基于原始请求体与x-razorpay-signature头做校验。签名不合法或缺失 secret 时返回401见 packages/razorpay/webhooks/orders.ts 等处理器。Webhook 处理完成后同样会执行本地实体 upsert 与事件日志记录返回{ success, corsairEntityId, data }。事件载荷的结构化类型RazorpayPaymentCapturedEvent、RazorpayOrderPaidEvent等由 Zod Schema 定义并从包中导出各事件 payload 与 response 的完整类型声明见 docs/plugins/razorpay/webhooks.mdx。多租户路由Razorpay 的事件外层信封带有account_id字段。packages/razorpay/webhooks/tenant-matcher.ts 的matchRazorpayTenantWebhook正是从请求体解析account_id把它映射为{ linkType: account_id, externalId: accountId }从而在不依赖 URL 路径的情况下把事件准确路由到对应租户——这是多租户隔离在 Webhook 侧的关键一环。webhookHooks 使用示例在插件工厂中通过webhookHooks挂载钩子即可在事件处理前后插入业务逻辑如通知、对账razorpay({ webhookHooks: { orders: { paid: { before(ctx, args) { return { ctx, args }; }, after(ctx, response) { // 订单支付成功后的业务处理 }, }, }, payments: { captured: { before() {}, after() {} }, failed: { before() {}, after() {} }, }, refunds: { processed: { before() {}, after() {} }, }, }, })测试与类型验证插件自带针对真实 Razorpay API 的类型级集成测试packages/razorpay/api.test.ts通过环境变量注入凭证与测试资源后运行环境变量用途RAZORPAY_API_KEY/RAZORPAY_SECRET_KEY拼接keyId:keySecret用于 Basic AuthRAZORPAY_TEST_PAYOUT_ID等Payouts 测试的既有资源 IDRAZORPAY_TEST_PAYMENT_ID等支付捕获、退款测试资源RAZORPAY_TEST_SUBSCRIPTION_PLAN_ID等订阅测试资源每个用例调用makeRazorpayRequest后都会用对应的输出 Schema 做.parse()校验确保响应结构与类型定义一致。在包目录下执行pnpm testjest即可运行。参考资料插件包入口与端点/Webhook 注册packages/razorpay/index.ts请求封装与幂等键packages/razorpay/client.ts端点实现示例packages/razorpay/endpoints/orders.ts实体 Schemapackages/razorpay/schema/database.tsWebhook 签名与匹配packages/razorpay/webhooks/types.ts、packages/razorpay/webhooks/tenant-matcher.ts官方站点文档概览 docs/plugins/razorpay/overview.mdx、API 参考 docs/plugins/razorpay/api.mdx、数据库 docs/plugins/razorpay/database.mdx、Webhook docs/plugins/razorpay/webhooks.mdx相关概念API Key docs/concepts/api-key.mdx、多租户 docs/concepts/multi-tenancy.mdx、Webhook 路由 docs/concepts/webhooks.mdx、错误处理 docs/concepts/error-handling.mdxLicense本插件以 Apache-2.0 协议开源见 packages/razorpay/package.json 的license字段可自由用于商业项目。【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考