@crvouga/ mockingbird-service-paddle (英语)
本地模拟器。 真正的API合同. 部分 嘲笑鸟。 。 。 .
国家模拟器 划板计费 测试套房的API. 顾客,地址,企业,
产品和价格表现为Paddle的行为(验证, invalid_field 错误, include=, (中文(简体) ),
光标平面)。 交易携带 计算总数。订阅创建方式
当一个反复出现价格的交易被支付, 以及模拟者的管理员时, 板块会创建它们
主机检查和计费引擎的路线: 支付交易, 完成 a
一通电话就退房,续约,不付款。每次更改都会生成事件
将发射,在 GET /events 作为《京都议定书》 Paddle-Signature 网络浏览
(原始内容存档于2018-07-21) (英语). Official SDK's paddle.webhooks.unmarshal 验出.
- 行动范围: 支助
openapi.yaml由 Paddle 的 API 引用和线条类型生成@paddle/[email protected]。 。 。 .
安装
npm install -D @crvouga/mockingbird-service-paddle
仅限无害环境管理。 节点 22或 Bun 1.2. 无土生属地. 给它服务
npx mockingbird-paddle serve, (中文(简体) ), createServer 从 ./server (节点),或 createRuntime 与
任何获取服务器.
使用量
SDK地图 environment 到一个基础 URL , 否则使用 数值 逐字, 因此将其指向
通过将模拟器的 URL 作为环境传递给模拟器。 给同样的应用 pdl_ntfset_…
保密为 --webhook-secret。 。 。 .
npx mockingbird-paddle serve --port 8795 --fixtures \
--webhook-url http://127.0.0.1:3000/webhooks/paddle \
--webhook-secret "$PADDLE_WEBHOOK_SECRET" \
--payment-link https://pay.example.com/checkout
PADDLE_API_BASE_URL=http://127.0.0.1:8795 node app.js
import { createServer } from "@crvouga/mockingbird-service-paddle/server"
import { type Environment, Paddle } from "@paddle/paddle-node-sdk"
const emulator = await createServer({ paymentLink: "https://pay.example.com/checkout" })
// The app's own Paddle client. Only its environment changes.
const paddle = new Paddle("pdl_sdbx_apikey_test", { environment: emulator.url as Environment })
const product = await paddle.products.create({ name: "Pro plan", taxCategory: "saas" })
const price = await paddle.prices.create({
productId: product.id,
description: "Pro monthly",
unitPrice: { amount: "2900", currencyCode: "USD" },
billingCycle: { interval: "month", frequency: 1 },
})
const customer = await paddle.customers.create({ email: "[email protected]" })
const address = await paddle.addresses.create(customer.id, { countryCode: "US" })
const transaction = await paddle.transactions.create({
items: [{ priceId: price.id, quantity: 2 }],
customerId: customer.id,
addressId: address.id,
})
// transaction.status === "ready", transaction.details.totals.grandTotal === "5800",
// transaction.checkout.url === "https://pay.example.com/checkout?_ptxn=txn_…"
// The customer pays on the hosted checkout: the emulator's admin route stands in for it.
await fetch(`${emulator.url}/__admin/transactions/${transaction.id}/pay`, { method: "POST" })
const [subscription] = await paddle.subscriptions.list({ customerId: [customer.id] }).next()
// subscription.status === "active", subscription.nextBilledAt one month out
await emulator.close()
与 fixtures: true 该目录已经播种, 并有一个管理员呼叫完成一个检查:
import { createServer } from "@crvouga/mockingbird-service-paddle/server"
import { type Environment, Paddle } from "@paddle/paddle-node-sdk"
const emulator = await createServer({ fixtures: true })
const paddle = new Paddle("pdl_sdbx_apikey_test", { environment: emulator.url as Environment })
const [product] = await paddle.products.list({ include: ["prices"] }).next()
// Test setup: one admin call stands in for a customer completing the hosted checkout.
const completed = await fetch(`${emulator.url}/__admin/checkout`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email: "[email protected]", items: [{ price_id: product?.prices?.[0]?.id }] }),
})
const checkout = (await completed.json()) as { transaction: { id: string }; subscription: { id: string } }
const transaction = await paddle.transactions.get(checkout.transaction.id)
const subscription = await paddle.subscriptions.get(checkout.subscription.id)
// transaction.status === "completed", subscription.status === "trialing" (the fixture price has a trial)
await emulator.close()
路线
每个回复都是Paddle的信封: {data, meta: {request_id}},列表添加
meta.pagination: {per_page, next, has_more, estimated_total}清单 after=<id> (单位:千美元)
光标进入 next,SDK所遵循的绝对URL as-is,), per_page (违约50,最大200;
交易 30; 超过最大值获得最大值, 如 Paddle 文档)和
order_by=id[ASC]|id[DESC] (默认最新第一). 一个页面显示创建于
after 按此顺序排列的实体,所以将过滤器留在页面之间的光标(存档,例如)
页面打开。 错误是
{error: {type, code, detail, documentation_url, errors?: [{field, message}]}, meta}。 。 。 .
| 路线 | 行为 |
|---|---|
GET/POST /customers, (中文(简体) ), GET/PATCH /customers/{id} | {email, name?, custom_data?, locale?}; 同一邮件的第二个客户是 409 customer_already_exists。过滤器 : id, (中文(简体) ), email, (中文(简体) ), search, (中文(简体) ), status。 。 。 . PATCH {status: "archived"} 档案. |
GET /customers/{id}/credit-balances | 永远 [] (没有信用模式). |
POST /customers/{id}/auth-token | {customer_auth_token: "pca_…", expires_at} (30分钟后). |
…/customers/{id}/addresses, (中文(简体) ), …/businesses (+ 数)/{id}(中文(简体) ) | 在他们的顾客下面,像在Paddle;另一个顾客的地址是404. country_code 需要在一个地址, name 谈生意. |
GET/POST /products, (中文(简体) ), GET/PATCH /products/{id} | {name, tax_category, description?, image_url?, custom_data?}· ; include=prices 嵌入价格. |
GET/POST /prices, (中文(简体) ), GET/PATCH /prices/{id} | {product_id, description, unit_price: {amount, currency_code}, billing_cycle?, trial_period?, quantity?, unit_price_overrides?, tax_mode?}· ; include=product。过滤器 : product_id, (中文(简体) ), recurring, (中文(简体) ), type, (中文(简体) ), status。 。 。 . |
POST /transactions | {items: [{price_id | price: {…non-catalog}, quantity}], customer_id?, address_id?, business_id?, currency_code?, collection_mode?, billing_details?, status?, custom_data?}状态是 ready 有客户和地址,否则 draft· ; collection_mode: manual 需求 billing_details.payment_terms 并可以创建 billed (发票号码, billed_at) (中文(简体) ). details 持有细列项目和总数; checkout.url 实值 <paymentLink>?_ptxn=<id> 当一个付款链接配置时。 经常性项目必须共享一个计费周期;价格必须与交易货币相匹配;地址或业务必须属于客户. 非目录 price 创建对象 type: custom 价格(和产品),只有在整个请求得到验证后. |
GET /transactions, (中文(简体) ), GET /transactions/{id} | 过滤器 : id, (中文(简体) ), customer_id, (中文(简体) ), subscription_id, (中文(简体) ), status, (中文(简体) ), origin, (中文(简体) ), collection_mode, (中文(简体) ), invoice_number, (中文(简体) ), created_at[GTE]- 样式日期时间界限(LT, (中文(简体) ), LTE, (中文(简体) ), GT, (中文(简体) ), GTE 打开 created_at, (中文(简体) ), billed_at, (中文(简体) ), updated_at(三); include=customer,address,business。 。 。 . |
PATCH /transactions/{id} | draft 财务报告和审定财务报表 ready 交易再次取出每个创建字段(总数重新计算); billed 财务报告和审定财务报表 past_due 仅一个 {status: "canceled"}; 其他东西是400 transaction_immutable。 。 。 . |
POST /transactions/preview | 共计 {items, customer_id?, address_id?, currency_code?, address?: {country_code}} 不存储任何东西( 非目录) price 对象被定价,而不是创建; include_in_totals: false 列出物品,而不是总和. |
GET /transactions/{id}/invoice | {url} 页:1 billed, (中文(简体) ), paid 财务报告和审定财务报表 completed 交易; 否则400 transaction_invoice_not_available。 。 。 . |
GET /subscriptions, (中文(简体) ), GET /subscriptions/{id} | 过滤器 : id, (中文(简体) ), customer_id, (中文(简体) ), address_id, (中文(简体) ), price_id, (中文(简体) ), status, (中文(简体) ), collection_mode, (中文(简体) ), scheduled_change_action· ; include=next_transaction,recurring_transaction_details 嵌入预览. management_urls 是模拟器来源的占位符链接. |
PATCH /subscriptions/{id} | custom_data, (中文(简体) ), next_billed_at, (中文(简体) ), collection_mode + 键 billing_details, (中文(简体) ), customer_id编号 :address_id编号 :business_id, (中文(简体) ), scheduled_change: null (取消预定暂停或取消),和 items 附有所需 proration_billing_mode数字 : *_immediately 模式单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单单 subscription_update 交易,其去除的不增减和信用;其他模式现在不收任何费用。 取消的订阅是400 subscription_update_when_canceled。 。 。 . |
POST /subscriptions/{id}/activate | trialing → active:现在第一个周期开始计费周期. |
POST /subscriptions/{id}/pause | {effective_from?: next_billing_period (default) | immediately, resume_at?}列表 : scheduled_change: {action: "pause", effective_at: next_billed_at}立即: paused, (中文(简体) ), paused_at,没有 next_billed_at; 与 resume_at (单位:千美元) resume 已安排更改. |
POST /subscriptions/{id}/resume | {effective_from: "immediately" | <datetime>, on_resume?}立即: active 重新开出账单,现在开出账单,日期安排恢复时间。 在有预定暂停的主动订阅时,它会去除暂停. |
POST /subscriptions/{id}/cancel | {effective_from?: next_billing_period (default) | immediately}列表 : scheduled_change: {action: "cancel", effective_at: next_billed_at},在下次更新时应用。 即时: canceled, (中文(简体) ), canceled_at,项目 inactive。 。 。 . |
POST /subscriptions/{id}/charge | {effective_from: immediately | next_billing_period, items} 非经常性价格。 立即:已完成 subscription_charge 交易; 否则项目将添加到下次续约的交易中. |
GET /events | 每一个事件 账户产生, 最新的第一: {event_id, event_type, occurred_at, notification_id: null, data}。 。 。 . |
认证是 Authorization: Bearer <key>; 任何关键功能。 没有标题是403 authentication_missing, (中文(简体) ),
非Bearer 信头为 403 authentication_malformed验证失败400次
invalid_field 与 errors: [{field, message}] (单位:千美元)email: required field; 变形的 JSON
尸体是400块 invalid_json; 未知实体为 404 not_found (单位:千美元)Entity ctm_… not found) (中文(简体) ).
Ids看起来像Paddle'sctm_01…, (中文(简体) ), pri_01…, (中文(简体) ), txn_01…, (中文(简体) ), sub_01…, (中文(简体) ), evt_01…: 前缀和26
小写字母数),对给定历史具有决定性.
离职和记账(管理)
Paddle 无法支付交易或创建订阅的 API : 主机检出确实
开具账单的引擎会更新 这些路线对他们来说都是 /__admin, (中文(简体) ),
像其它东西一样空格 :
| 路线 | 效果 |
|---|---|
POST /__admin/transactions/:id/pay {card?: {type, last4, expiry_month, expiry_year, cardholder_name}} | A 类 ready, (中文(简体) ), billed 或者说 past_due 交易已支付:缴获的卡片付款, completed,发票号码。 经常项目创建 订阅 (单位:千美元)trialing 当一个价格有一个 trial_period,则 active,与 current_billing_period 财务报告和审定财务报表 next_billed_at页:1 past_due 续约付款后返回订阅 active—— (中文) {transaction, subscription} |
POST /__admin/checkout {email | customer_id, name?, country_code?, postal_code?, address_id?, business_id?, items: [{price_id, quantity?}], custom_data?} | 在一个呼叫中完成主机检查:找到或创建客户(通过电子邮件)和一个地址,创建一个 web 交易和支付。 − 201个 {transaction, subscription} |
POST /__admin/subscriptions/:id/renew | 运行下一个计费日期。 预定取消或暂停生效({subscription, transaction: null}(b) 暂停订阅,并安排复会,从复会开始计费 effective_at; 否则已完成 subscription_recurring 经常性项目的交易(加上排队一次性费用)、计费期预付款和审判结束 active。 。 。 . |
POST /__admin/subscriptions/:id/payment-failed | 延期付款被拒:a past_due 与一项交易 error 尝试支付, 订阅去 past_due支付交易以收回. |
POST /__admin/seed | 固定账户(下). – 201 和每张创造的记录. |
GET /__admin/events?type= | 事件列表,最古老的第一,可选的一类. |
--fixtures (或 createRuntime({fixtures: true})种子每一个命名空间 在第一次使用,和再次
每次重置后, 使用 :
艾达·洛芙蕾丝(Ada Lovelace)[email protected],美国地址,企业),a 计划 产品为
每月(29美元)、每年(290美元)和一次性入职(99美元) 启动计划 带有
审判14天(每月19美元),每月主动订阅,试订起步器
(葛瑞斯・霍伯, [email protected]),未支付 ready 交易和a billed 手动
发票. seedFixtures() 返回记录; 在电子邮件上种子两次名称空间冲突.
固定事件列出(GET /events但从未作为通知送达:
账户是原有状态,不是活动.
网络呼号
每个事件都会被发布到配置的终点作为Paddle的通知机构:
{"event_id": "evt_01…", "event_type": "subscription.created", "occurred_at": "…",
"notification_id": "ntf_01…", "data": {"id": "sub_01…", "status": "active", "transaction_id": "txn_01…", "items": [ … ], … }}
类型 : customer|address|business|product|price.created|updated, (中文(简体) ), transaction.created|ready| billed|paid|completed|canceled|payment_failed|past_due|updated, (中文(简体) ), subscription.created| activated|trialing|updated|paused|resumed|canceled|past_due每次交货都携带
Paddle-Signature: ts=<unix seconds>;h1=<hex HMAC-SHA256(secret, "<ts>:<raw body>")> 带有
时钟 ts (即使模拟时钟移动) paddle.webhooks.unmarshal(body, secret, signature) 财务报告和审定财务报表 isSignatureValid 接受 。 非-2xx的答案被重审(立即,
5分5分30分2小时). GET /__admin/webhooks, (中文(简体) ), …/events, (中文(简体) ), …/flush, (中文(简体) ), …/:id/replay 财务报告和审定财务报表
PUT /__admin/webhook-endpoints (每个名称空间接收器, events: ["subscription.*"]- 风格
过滤器是精确的类型或 *)来与合同.
错误预设( E)POST /__admin/faults {"preset": "<name>", "count"?: n}· ; GET /__admin/faults/presets:
invalid_token (每起请求403起), rate_limited (429个) too_many_requests 与
retry-after: 2,SDK's(SDK's ApiError.retryAfter, (中文(简体) ), transactions_500 (单位:千美元)POST /transactions
答复 500 internal_error, (中文(简体) ), bad_gateway_html (读取了502 HTML 页面),
network_drop (单位:千美元)POST /transactions 放弃连接, webhook_duplicate, (中文(简体) ),
webhook_reorder, (中文(简体) ), webhook_drop。 。 。 .
命名空间
new Paddle(key) 无法单独添加命名空间头( 它可以使用 customHeaders),这样
将 API 密钥映射到命名空间 : PUT /__admin/credentials {"credentials": {"<PADDLE_API_KEY>": "<namespace>"}}此外 x-mockingbird-namespace,或一个 /__admin/ns/<name> 基础 URL 前缀
(单位:千美元)meta.pagination.next 留着吧.
故意不模拟
- 税取而取之. 结转共计
tax: "0"财务报告和审定财务报表discount: "0"无论地址或tax_mode;没有折扣、贴现组或调整(退款、贷项),没有 贷方余额.unit_price_overrides以地址为国为荣. - 作礼. 用 a 更改订阅项目
*_immediately模式账单 变化会增加全额,不计入其去除的内容;下期模式现在不计入.PATCH /subscriptions/{id}/preview财务报告和审定财务报表…/charge/preview没有支持. - 主机取出,客户端门户页面和Paddle.js.
checkout.url, (中文(简体) ),management_urls和门户网站会话链接是URL,而不是页面;POST /__admin/checkout财务报告和审定财务报表…/pay替换 结账.POST /customers/{id}/portal-sessions返回临时 https 会话链接. - 付款方法和付款方法的改变、付款、报告、模拟、发票
订正案、
imported事件、 API 关键事件和客户端符号. - 通知目的地 是销售商API(
GET|POST /notification-settings, (中文(简体) ),GET|PATCH /notification-settings/{id}) (中文(简体) ). 活动url目的地随其 拥有pdl_ntfset_…暗号 秘诀只在创建时返回.--webhook-url财务报告和审定财务报表PUT /__admin/webhook-endpoints作为额外接收者,不由供应商名单所取代. - 时间. 没有东西能靠自己续约:
…/renew(或…/payment-failed当测试时 时钟到达next_billed_at重试过去到期的订阅时间表不是模拟的. - 费率限额除通过
rate_limited。遗留(2025年前)API的关键格式是 和其他钥匙一样被接受.
API 密码
| 导出 | 仁爱 | 说明 |
|---|---|---|
PaddleAPI | 类 | 进程模拟器 : fetch(request), (中文(简体) ), reset(), (中文(简体) ), events(), (中文(简体) ), state,而计费方法 管理员路线呼叫: createCustomer, (中文(简体) ), createAddress, (中文(简体) ), createBusiness, (中文(简体) ), createProduct, (中文(简体) ), createPrice, (中文(简体) ), createTransaction, (中文(简体) ), payTransaction, (中文(简体) ), checkout, (中文(简体) ), renewSubscription, (中文(简体) ), failPayment, (中文(简体) ), seedFixtures选项: sqlite, (中文(简体) ), now, (中文(简体) ), namespace, (中文(简体) ), publicNamespace, (中文(简体) ), paymentLink, (中文(简体) ), onEvent, (中文(简体) ), fixtures。 。 。 . |
createRuntime | 函数 | 全面服务合同的模拟者(保健、管理、命名空间、证书、预设、, Paddle-Signature 网约车、退票和收费路线。 选项 : webhooks: {url, secret, events?, retryDelaysMs?, fetch?}, (中文(简体) ), paymentLink, (中文(简体) ), fixtures, (中文(简体) ), clock, (中文(简体) ), seed, (中文(简体) ), adminKey, (中文(简体) ), onLog, (中文(简体) ), sqlite。 。 。 . |
paddleSigner | 函数 | 这个 Paddle-Signature webhook 签名符 (ts=…;h1=…) (中文(简体) ). |
PADDLE_PRESETS | 对象 | 每个命名的断层预设. |
PADDLE_NAMESPACE | 字符串 | 服务名称, "paddle"。 。 。 . |
AUTH_TOKEN_TTL_MS | 编号 | 公布的客户认证证书的有效期(30分钟). |
PaddleError | 类 | 计费方法抛出的错误 : status, (中文(简体) ), code, (中文(简体) ), detail, (中文(简体) ), errors?, (中文(简体) ), toResponse(requestId)。 。 。 . |
PaddleState | 类 | SQLite 支持的收藏(customers, (中文(简体) ), addresses, (中文(简体) ), businesses, (中文(简体) ), products, (中文(简体) ), prices, (中文(简体) ), transactions, (中文(简体) ), subscriptions, (中文(简体) ), events, (中文(简体) ), notificationSettings, (中文(简体) ), portalSessions和 nextId(kind)。 。 。 . |
ID_PREFIX | 对象 | Paddle 每个实体的 id 前缀(customer: "ctm", (中文(简体) ), price: "pri"(中文(简体) ). |
document, (中文(简体) ), operationIds, (中文(简体) ), supportedOperationIds | 数值 | 销售商为OpenAPI合同及其业务代号. |
createServer, (中文(简体) ), serveTarget, (中文(简体) ), DEFAULT_PORT (单位:千美元)./server(中文(简体) ) | 节点 | 服务过来 node:http; 数据 serve CLI目标(--webhook-url, (中文(简体) ), --webhook-secret, (中文(简体) ), --payment-link, (中文(简体) ), --fixtures; 蚌埠8795. |
部分 嘲笑鸟。 。 。 .