@crvouga/mockingbird-service-paddle 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog — @crvouga/mockingbird-service-paddle
2
2
 
3
+ ## 2.3.0 (2026-10-09)
4
+
5
+ ### Features
6
+
7
+ - match Emulate provider coverage ([16b65ce](https://github.com/crvouga/mockingbird/commit/16b65ce791fa216f28ec1521df636f57771d9c5e))
8
+
9
+ ### Dependencies
10
+
11
+ - `@crvouga/mockingbird-service-sqlite`
12
+
13
+ ## 2.2.0 (2026-10-08)
14
+
15
+ ### Features
16
+
17
+ - redesign emulator experience ([d6038c2](https://github.com/crvouga/mockingbird/commit/d6038c27eebbaa1751f02bc9305002a5b1304779))
18
+
3
19
  ## 2.1.0 (2026-10-07)
4
20
 
5
21
  ### Features
package/DISCOVERY.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @crvouga/mockingbird-service-paddle discovery
2
2
 
3
3
  This is the installed-package index for coding agents and tooling. All relative links resolve
4
- inside `node_modules/@crvouga/mockingbird-service-paddle/`; no repository checkout is needed to discover the mock's
4
+ inside `node_modules/@crvouga/mockingbird-service-paddle/`; no repository checkout is needed to discover the emulator's
5
5
  supported surface or documented behavior.
6
6
 
7
7
  ## Capability and behavior sources
@@ -48,7 +48,7 @@ designed for assertions and diagnosis by consuming test suites.
48
48
  ## Report a mismatch or missing capability
49
49
 
50
50
  Follow the [agent reporting contract](https://github.com/crvouga/mockingbird/blob/main/docs/REPORTING_ISSUES.md). Include package version,
51
- operation/command, a minimal redacted request, actual mock result, expected oracle result or vendor
51
+ operation/command, a minimal redacted request, actual emulator result, expected oracle result or vendor
52
52
  documentation, and whether the mismatch appears in the matrix. Never include keys, tokens,
53
53
  customer data, prompts, PHI, card data, or unredacted recordings.
54
54
 
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # @crvouga/mockingbird-service-paddle
2
2
 
3
- > Familiar calls. Faithful echoes. Part of [Mockingbird](https://github.com/crvouga/mockingbird).
3
+ > Local emulators. Real API contracts. Part of [Mockingbird](https://github.com/crvouga/mockingbird).
4
4
 
5
- Stateful mock of the **Paddle Billing** API for test suites. Customers, addresses, businesses,
5
+ Stateful emulator of the **Paddle Billing** API for test suites. Customers, addresses, businesses,
6
6
  products and prices behave as Paddle's do (validation, `invalid_field` errors, `include=`,
7
7
  cursor pagination). Transactions carry **computed totals**. Subscriptions are created the way
8
- Paddle creates them, when a transaction with recurring prices is paid, and the mock's admin
8
+ Paddle creates them, when a transaction with recurring prices is paid, and the emulator's admin
9
9
  routes stand in for the hosted checkout and the billing engine: **pay a transaction, complete a
10
10
  checkout in one call, run a renewal, fail a payment**. Every change produces the event Paddle
11
11
  would emit, listed at `GET /events` and delivered as a `Paddle-Signature` webhook that the
@@ -28,7 +28,7 @@ any Fetch server.
28
28
  ## Usage
29
29
 
30
30
  The SDK maps `environment` to a base URL and otherwise uses the value verbatim, so point it at
31
- the mock by passing the mock's URL as the environment. Give the app the same `pdl_ntfset_…`
31
+ the emulator by passing the emulator's URL as the environment. Give the app the same `pdl_ntfset_…`
32
32
  secret as `--webhook-secret`.
33
33
 
34
34
  ```bash
@@ -43,9 +43,9 @@ PADDLE_API_BASE_URL=http://127.0.0.1:8795 node app.js
43
43
  import { createServer } from "@crvouga/mockingbird-service-paddle/server"
44
44
  import { Paddle } from "@paddle/paddle-node-sdk"
45
45
 
46
- const mock = await createServer({ paymentLink: "https://pay.example.com/checkout" })
47
- // In TypeScript: `{ environment: mock.url as Environment }`.
48
- const paddle = new Paddle("pdl_sdbx_apikey_test", { environment: mock.url })
46
+ const emulator = await createServer({ paymentLink: "https://pay.example.com/checkout" })
47
+ // In TypeScript: `{ environment: emulator.url as Environment }`.
48
+ const paddle = new Paddle("pdl_sdbx_apikey_test", { environment: emulator.url })
49
49
 
50
50
  const product = await paddle.products.create({ name: "Pro plan", taxCategory: "saas" })
51
51
  const price = await paddle.prices.create({
@@ -64,11 +64,11 @@ const transaction = await paddle.transactions.create({
64
64
  // transaction.status === "ready", transaction.details.totals.grandTotal === "5800",
65
65
  // transaction.checkout.url === "https://pay.example.com/checkout?_ptxn=txn_…"
66
66
 
67
- // The customer pays on the hosted checkout: the mock's admin route stands in for it.
68
- await fetch(`${mock.url}/__admin/transactions/${transaction.id}/pay`, { method: "POST" })
67
+ // The customer pays on the hosted checkout: the emulator's admin route stands in for it.
68
+ await fetch(`${emulator.url}/__admin/transactions/${transaction.id}/pay`, { method: "POST" })
69
69
  const [subscription] = await paddle.subscriptions.list({ customerId: [customer.id] }).next()
70
70
  // subscription.status === "active", subscription.nextBilledAt one month out
71
- await mock.close()
71
+ await emulator.close()
72
72
  ```
73
73
 
74
74
  Without the SDK, the same over HTTP:
@@ -76,18 +76,18 @@ Without the SDK, the same over HTTP:
76
76
  ```ts
77
77
  import { createServer } from "@crvouga/mockingbird-service-paddle/server"
78
78
 
79
- const mock = await createServer({ fixtures: true })
79
+ const emulator = await createServer({ fixtures: true })
80
80
  const headers = { authorization: "Bearer pdl_sdbx_apikey_test", "content-type": "application/json" }
81
- const { data: products } = await (await fetch(`${mock.url}/products?include=prices`, { headers })).json()
81
+ const { data: products } = await (await fetch(`${emulator.url}/products?include=prices`, { headers })).json()
82
82
  const checkout = await (
83
- await fetch(`${mock.url}/__admin/checkout`, {
83
+ await fetch(`${emulator.url}/__admin/checkout`, {
84
84
  method: "POST",
85
85
  headers,
86
86
  body: JSON.stringify({ email: "new@example.com", items: [{ price_id: products[0].prices[0].id }] }),
87
87
  })
88
88
  ).json()
89
89
  // checkout.transaction.status === "completed", checkout.subscription.status === "active"
90
- await mock.close()
90
+ await emulator.close()
91
91
  ```
92
92
 
93
93
  ### Routes
@@ -112,7 +112,7 @@ transactions 30; more than the maximum gets the maximum, as Paddle documents) an
112
112
  | `PATCH /transactions/{id}` | `draft` and `ready` transactions take every create field again (totals are recomputed); `billed` and `past_due` ones only `{status: "canceled"}`; anything else is 400 `transaction_immutable`. |
113
113
  | `POST /transactions/preview` | Totals for `{items, customer_id?, address_id?, currency_code?, address?: {country_code}}` without storing anything (non-catalog `price` objects are priced, not created); `include_in_totals: false` items are listed, not summed. |
114
114
  | `GET /transactions/{id}/invoice` | `{url}` for `billed`, `paid` and `completed` transactions; else 400 `transaction_invoice_not_available`. |
115
- | `GET /subscriptions`, `GET /subscriptions/{id}` | Filters: `id`, `customer_id`, `address_id`, `price_id`, `status`, `collection_mode`, `scheduled_change_action`; `include=next_transaction,recurring_transaction_details` embeds the previews. `management_urls` are placeholder links on the mock's origin. |
115
+ | `GET /subscriptions`, `GET /subscriptions/{id}` | Filters: `id`, `customer_id`, `address_id`, `price_id`, `status`, `collection_mode`, `scheduled_change_action`; `include=next_transaction,recurring_transaction_details` embeds the previews. `management_urls` are placeholder links on the emulator's origin. |
116
116
  | `PATCH /subscriptions/{id}` | `custom_data`, `next_billed_at`, `collection_mode` + `billing_details`, `customer_id`/`address_id`/`business_id`, `scheduled_change: null` (removes a scheduled pause or cancel), and `items` with a required `proration_billing_mode`: `*_immediately` modes bill what the change adds (new prices, quantity increases) in full at once as a `subscription_update` transaction, with no proration and no credit for what it removes; the other modes bill nothing now. Canceled subscriptions are 400 `subscription_update_when_canceled`. |
117
117
  | `POST /subscriptions/{id}/activate` | `trialing` → `active`: bills the first period now and starts the billing cycle. |
118
118
  | `POST /subscriptions/{id}/pause` | `{effective_from?: next_billing_period (default) \| immediately, resume_at?}`. Scheduled: `scheduled_change: {action: "pause", effective_at: next_billed_at}`. Immediate: `paused`, `paused_at`, no `next_billed_at`; with `resume_at` a `resume` change is scheduled. |
@@ -166,7 +166,7 @@ Types: `customer|address|business|product|price.created|updated`, `transaction.c
166
166
  billed|paid|completed|canceled|payment_failed|past_due|updated`, `subscription.created|
167
167
  activated|trialing|updated|paused|resumed|canceled|past_due`. Each delivery carries
168
168
  `Paddle-Signature: ts=<unix seconds>;h1=<hex HMAC-SHA256(secret, "<ts>:<raw body>")>` with a
169
- wall-clock `ts` (even when the mock clock moves), which `paddle.webhooks.unmarshal(body,
169
+ wall-clock `ts` (even when the emulator clock moves), which `paddle.webhooks.unmarshal(body,
170
170
  secret, signature)` and `isSignatureValid` accept. Non-2xx answers are retried (immediately,
171
171
  5 s, 5 min, 30 min, 2 h). `GET /__admin/webhooks`, `…/events`, `…/flush`, `…/:id/replay` and
172
172
  `PUT /__admin/webhook-endpoints` (per-namespace receivers, `events: ["subscription.*"]`-style
@@ -212,8 +212,8 @@ map API keys to namespaces: `PUT /__admin/credentials {"credentials": {"<PADDLE_
212
212
 
213
213
  | Export | Kind | Description |
214
214
  | --- | --- | --- |
215
- | `PaddleAPI` | class | The in-process mock: `fetch(request)`, `reset()`, `events()`, `state`, and the billing methods the admin routes call: `createCustomer`, `createAddress`, `createBusiness`, `createProduct`, `createPrice`, `createTransaction`, `payTransaction`, `checkout`, `renewSubscription`, `failPayment`, `seedFixtures`. Options: `sqlite`, `now`, `namespace`, `publicNamespace`, `paymentLink`, `onEvent`, `fixtures`. |
216
- | `createRuntime` | function | The mock with the full service contract (health, admin, namespaces, credentials, presets, `Paddle-Signature` webhooks, checkout and billing routes). Options: `webhooks: {url, secret, events?, retryDelaysMs?, fetch?}`, `paymentLink`, `fixtures`, `clock`, `seed`, `adminKey`, `onLog`, `sqlite`. |
215
+ | `PaddleAPI` | class | The in-process emulator: `fetch(request)`, `reset()`, `events()`, `state`, and the billing methods the admin routes call: `createCustomer`, `createAddress`, `createBusiness`, `createProduct`, `createPrice`, `createTransaction`, `payTransaction`, `checkout`, `renewSubscription`, `failPayment`, `seedFixtures`. Options: `sqlite`, `now`, `namespace`, `publicNamespace`, `paymentLink`, `onEvent`, `fixtures`. |
216
+ | `createRuntime` | function | The emulator with the full service contract (health, admin, namespaces, credentials, presets, `Paddle-Signature` webhooks, checkout and billing routes). Options: `webhooks: {url, secret, events?, retryDelaysMs?, fetch?}`, `paymentLink`, `fixtures`, `clock`, `seed`, `adminKey`, `onLog`, `sqlite`. |
217
217
  | `paddleSigner` | function | The `Paddle-Signature` webhook signer (`ts=…;h1=…`). |
218
218
  | `PADDLE_PRESETS` | object | Every named fault preset. |
219
219
  | `PADDLE_NAMESPACE` | string | The service name, `"paddle"`. |
package/SUPPORT.md CHANGED
@@ -3,10 +3,10 @@
3
3
  Generated from `openapi.yaml`; do not edit by hand.
4
4
 
5
5
  - operations in spec: **50**
6
- - supported by the mock: **42**
6
+ - supported by the emulator: **42**
7
7
  - parity enabled: **42**
8
8
 
9
- | operationId | route | mock | parity | notes |
9
+ | operationId | route | emulator | parity | notes |
10
10
  | --- | --- | --- | --- | --- |
11
11
  | `ListCustomers` | `GET /customers` | ✅ supported | ✅ | |
12
12
  | `CreateCustomer` | `POST /customers` | ✅ supported | ⚠️ unsafe (opt-in) | |
@@ -47,7 +47,7 @@ Generated from `openapi.yaml`; do not edit by hand.
47
47
  | `CancelSubscription` | `POST /subscriptions/{subscription_id}/cancel` | ✅ supported | ⚠️ unsafe (opt-in) | |
48
48
  | `CreateSubscriptionCharge` | `POST /subscriptions/{subscription_id}/charge` | ✅ supported | ⚠️ unsafe (opt-in) | |
49
49
  | `PreviewSubscriptionCharge` | `POST /subscriptions/{subscription_id}/charge/preview` | ❌ unsupported | — | One-time charge previews are not modelled; create the charge instead. |
50
- | `GetSubscriptionPaymentMethodChangeTransaction` | `GET /subscriptions/{subscription_id}/update-payment-method-transaction` | ❌ unsupported | — | Payment method changes go through the hosted checkout, which the mock does not serve. |
50
+ | `GetSubscriptionPaymentMethodChangeTransaction` | `GET /subscriptions/{subscription_id}/update-payment-method-transaction` | ❌ unsupported | — | Payment method changes go through the hosted checkout, which the emulator does not serve. |
51
51
  | `ListEvents` | `GET /events` | ✅ supported | ✅ | |
52
52
  | `ListNotificationSettings` | `GET /notification-settings` | ✅ supported | ✅ | |
53
53
  | `CreateNotificationSetting` | `POST /notification-settings` | ✅ supported | ⚠️ unsafe (opt-in) | |