All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[1.1.1] - 2026-05-21
Added
- Optional Python SDK contract suite that drives the official
stripePython package against PaperTiger over local HTTP, tagged:python_sdkand enabled withVALIDATE_PYTHON_SDK=true.
Fixed
- Stripe list filters and CustomerSession component parsing now accept Python SDK boolean serialization (
True/False) in addition to lowercase boolean strings. - PaymentIntent creation with a
payment_methodnow entersrequires_confirmation, matching Stripe's pre-confirmation state. - Predefined
pm_card_*test PaymentMethods now materialize fresh attachable PaymentMethod objects in attach/setup flows, so the same test fixture can be reused across customers without rebinding a singleton.
[1.1.0] - 2026-05-20
Added
- Pluggable webhook delivery adapter.
PaperTiger.WebhookDelivery.Adapterbehaviour with a singledeliver/1callback taking aPaperTiger.WebhookDelivery.Request(the signed payload, full headers, signature header, url, event, webhook, namespace) and returning{:ok, %PaperTiger.WebhookDelivery.Response{}}(terminal success / ownership taken) or{:error, reason}(PaperTiger retries with its existing exponential backoff). Configure withconfig :paper_tiger, webhook_delivery_adapter: MyApp.Sink. Lets a host embedding PaperTiger take durable ownership of webhook delivery via an explicit, enforced contract — a missing or crashing host cannot silently drop webhooks. Default isPaperTiger.WebhookDelivery.HTTPAdapter, which performs the HTTP POST exactly as before; no behavior change when the adapter is not configured. [:paper_tiger, :webhook, :delivering]telemetry event, emitted for every delivery in every adapter immediately before the adapter is invoked. Metadata:event,webhook,url,payload(exact signed bytes),signature_header,headers,timestamp,namespace. Observability only — not the delivery mechanism (that is the adapter behaviour). Use for metrics/tracing.- Stripe-compatible signed webhook request helpers and coverage:
PaperTiger.WebhookDelivery.build_signed_request/3builds the exact raw JSON body, headers, andStripe-Signaturevalue without delivery, collected webhooks now store the signed body/header, andPaperTiger.Test.signed_webhook_request/1exposes those fields for controller tests that callStripe.Webhook.construct_event/5. - CI quality checks now include Elixir
1.20.0-rc.5, and the package version requirement accepts the1.20release-candidate line. - Side-by-side contract drift harness for running a scenario against both PaperTiger and Stripe test mode in the same test, normalizing volatile fields, and reporting the first mismatch with both backend shapes. The first live drift scenario covers customer create/retrieve/update/delete.
- Deterministic automatic-tax fixtures for Checkout Session totals, PaymentIntent amounts, initial subscription invoices, and subscription renewal invoices.
- PaymentIntent lifecycle endpoints for
POST /v1/payment_intents/:id/cancelandPOST /v1/payment_intents/:id/capture, including manual-capture authorization, partial capture,amount_capturable/amount_received, captured Charge state, and Stripe-shaped invalid-state errors. - Checkout Session update and line-item retrieval endpoints, including metadata merge/delete semantics, paginated
GET /v1/checkout/sessions/:id/line_items,expand[]=line_items, and preview-style full-array line item updates for dynamic Checkout tests. - SetupIntent lifecycle endpoints for
POST /v1/setup_intents/:id/confirm,POST /v1/setup_intents/:id/cancel,POST /v1/setup_intents/:id/verify_microdeposits, andGET /v1/setup_attempts, including customer attachment for successful card setup, bank-account microdeposit verification, cancellation reasons, failed/abandoned/succeeded SetupAttempt records, and live Stripe contract coverage for confirm/cancel/list-attempts. - Stripe-style search endpoints for
GET /v1/customers/search,GET /v1/subscriptions/search,GET /v1/payment_intents/search,GET /v1/charges/search, andGET /v1/invoices/search, backed by a shared query parser/evaluator with resource field schemas, metadata predicates, boolean connectors, negation, numeric comparisons, substring matching, search-result pagination, and Stripe-shaped unsupported-query errors. - Modern payment-method adjunct APIs: test-helper ConfirmationToken creation plus retrieval, PaymentMethodDomain create/retrieve/update/list, PaymentMethodConfiguration create/retrieve/update/list, CustomerSession creation with client secrets, and stored Mandate retrieval for successful mandate-bearing SetupIntent/PaymentIntent flows.
- Invoice lifecycle endpoints for
POST /v1/invoices/:id/send,POST /v1/invoices/:id/mark_uncollectible, and PaymentIntent-backedPOST /v1/invoices/:id/attach_payment, includinginvoice.sent,invoice.marked_uncollectible, andinvoice.voidedwebhook emission plus status-transition timestamps. - Hosted product APIs for Payment Links and Billing Portal:
POST/GET/POST/GET-list /v1/payment_links, paginatedGET /v1/payment_links/:id/line_items, browser-visible Payment Link URLs backed by deterministic Checkout Session completion,POST /v1/billing_portal/sessions, and Billing Portal Configuration create/retrieve/update/list endpoints. - Billing discount and credit APIs: Promotion Codes, Customer Balance Transactions, Customer Cash Balance, and Credit Notes with invoice/customer balance mutation for common credit workflows.
- Properly scoped Connect platform APIs: legacy
/v1/accounts,POST /v1/account_links, per-requestStripe-Accountresource isolation,POST/GET/POST/GET-list /v1/transfers, nested transfer reversals, and nested application fee refunds with transfer/fee/reversal balance transaction effects. - Complete Stripe-style list filtering for Products, Prices, and Refunds, including created-range predicates, documented scalar filters, array filters (
ids[],lookup_keys[]), Price recurring child filters, list-item expansion such asexpand[]=data.product, and Refund filtering by Charge or PaymentIntent before cursor pagination. - SubscriptionSchedule phase/date fidelity for immediate, future, multi-phase, and
from_subscriptionschedules: contiguous phase normalization,durationand legacyiterationsderivation, activecurrent_phase, schedule-driven subscription/item updates, cancel/release/completion side effects, documented list filters, and live Stripe contract coverage for create/update/cancel/release flows.
Fixed
POST /v1/payment_methods/:id/attachand/detachnow emitpayment_method.attachedandpayment_method.detachedwebhooks.- Removed unreachable invoice-proration fallback branches flagged by the Elixir
1.20.0-rc.5type checker; generated proration lines are unchanged. - Webhook delivery adapter requests now carry the captured PaperTiger namespace through chaos buffering, async task delivery, retry scheduling, telemetry, and delivery-attempt updates, so host-owned durable adapters can preserve tenant context.
- Subscription item direct store helpers now respect the active PaperTiger namespace for lookup and bulk deletion, matching the shared store isolation used by standard CRUD operations.
Hardened
- Adapter invocation is wrapped: a raising, exiting, throwing, undefined, missing-
deliver/1, or wrong-shape-returning adapter is normalized into{:error, reason}so PaperTiger's own backoff/retry runs and a delivery attempt is recorded — instead of the spawned delivery task crashing before the retry machinery (and, in sync mode, the linkedTask.asynctaking downPaperTiger.WebhookDelivery). This is what actually enforces the "a missing or crashing host cannot silently drop webhooks" guarantee. - Updated direct and transitive dependencies to current compatible releases, including Bandit/Plug security fixes, Credo's Elixir
1.20.0-rccompatibility fixes, and the Stripity Stripe/Hackney 4 line used by the sandbox HTTP adapter.
Notes
- The
:webhook_modeconfig (:sync/:async/:collect) is unchanged and continues to control when delivery is dispatched. The new adapter controls where/how the signed request goes. The two are orthogonal. deliver_event_sync/2's public contract is unchanged ({:ok, :delivered | :failed} | {:error, term()}); adapter handoff does not leak a new return value.- Connect support intentionally targets Stripe API v1 compatibility first. Accounts v2, Persons, external-account mutation, capability review workflows, Treasury, and Issuing remain explicitly unsupported rather than partially mocked.
[1.0.2] - 2026-02-28
Added
- PaymentIntent confirm endpoint (
POST /v1/payment_intents/:id/confirm) with full Charge and BalanceTransaction creation on success. ChargeHelpermodule centralizing Charge + BalanceTransaction creation from succeeded PaymentIntents, used by checkout session completion, BillingEngine, and the new confirm endpoint.latest_chargefield on PaymentIntent objects, populated after successful payment.- Contract test validating full PaymentIntent -> Charge -> BalanceTransaction chain against real Stripe API.
Fixed
- Checkout session completion now creates Charge and BalanceTransaction (previously only created the PaymentIntent).
- Currency derivation from
line_items[].price_data.currencywhen not explicitly set on checkout session.
[1.0.1] - 2026-02-27
Fixed
- Metadata updates now use Stripe merge semantics: new keys are merged into existing metadata instead of replacing the entire map, and keys set to empty string are deleted.
Changed
- Nix/direnv developer setup is now opt-in (not auto-activated).
[1.0.0] - 2026-02-13
Added
- Invoice preview endpoints:
GET /v1/invoices/upcomingandPOST /v1/invoices/create_preview, including proration line generation and Stripe-style quantity validation. - Subscription payment-intent lifecycle coverage for
payment_behavior: "default_incomplete", including support forexpand=["latest_invoice.payment_intent"]. - Coupon/discount support in subscription create/update flows with discount reflection in invoice previews.
- Nix/direnv developer setup support (
flake.nix,flake.lock,.envrc) and corresponding README guidance.
Changed
phx.serverstartup detection now relies on Phoenix's:serve_endpointssignal instead of argv pattern matching.- Proration invoice creation is constrained to billable subscription changes, reducing spurious invoice generation.
- Chaos API override matching now supports endpoint-specific custom responses and wildcard-prefix path handling.
Fixed
customer.subscription.updatedwebhooks now includeprevious_attributes, matching Stripe behavior.- No-op subscription updates no longer emit redundant webhooks.
- Hydrator expansion now traverses list paths (for example
items.data.price.product) correctly. - Customer filtering by
emailand form-encoded card expiration field coercion better match Stripe-compatible behavior.
[0.9.25] - 2026-02-10
Added
PaperTiger.flush_all/0to flush data across all namespaces (legacy behavior).
Changed
PaperTiger.flush/0now flushes only the current namespace (safe forasync: truetest suites usingPaperTiger.Testsandboxing).- Updated dependencies:
quokka2.12.0,plug_cowboy2.8.0,ex_doc0.40.1.
Fixed
- ChaosCoordinator config/state is now correctly isolated per namespace, preventing intermittent test failures from cross-test chaos leakage.
- Reduced flaky test assertions in billing engine chaos tests.
[0.9.24] - 2026-02-02
Added
- Automatic checkout session completion: Checkout sessions now auto-complete asynchronously, simulating customer completion of the hosted checkout page. For setup mode sessions, when a customer adds a payment method to an incomplete subscription, the first invoice is automatically paid, matching Stripe's production behavior.
[0.9.23] - 2026-01-30
Fixed
- Random port with runtime config: Port selection is now deterministic even when called from
config/runtime.exsbefore PaperTiger starts. The newPaperTiger.Portresolver caches the selected port on first call, ensuring both config evaluation and server startup use the same port. This eliminates connection refused errors when usingstripity_stripe_config()without explicit port. Works with any HTTP client - not tied to stripity_stripe.
[0.9.22] - 2026-01-28
Changed
- Random high ports by default: PaperTiger now picks a random available port in the 59000-60000 range by default, eliminating port conflicts when running multiple instances (tests + dev server, parallel test suites). Port availability is checked before binding, with automatic retry if port is in use. Set explicit port via
PAPER_TIGER_PORTenv var orport:option if needed. NewPaperTiger.get_port/0function returns the actual port selected. - Fixed all Elixir 1.20 type checker warnings: removed 33 unused
require Loggerstatements and fixed typing violation in hydrator module. Paper Tiger now compiles cleanly with Elixir 1.20.0-rc.1 with zero type warnings.
[0.9.21] - 2026-01-19
Changed
- Reduced startup log noise by changing initialization messages from
Logger.infotoLogger.debugacross all Store modules, Application, Bootstrap, and core services. Startup messages are only visible at debug level while operational logs remain at appropriate levels.
[0.9.20] - 2026-01-11
Changed
- Checkout sessions now auto-complete transparently: Checkout session URLs now point to PaperTiger's own endpoint (
/checkout/:id/complete) instead of fake Stripe URLs. When visited, the session auto-completes and redirects tosuccess_url. This eliminates the need forpaper_tiger_enabled?checks in application code - checkout flows now work identically in dev/test and production.
[0.9.19] - 2026-01-10
Added
- Chaos testing cleanup function: Added
ChaosCoordinator.cleanup/0andChaosHelpers.cleanup_chaos/0that reset chaos configuration AND flush all Paper Tiger stores. Call after chaos testing to prevent test data from being synced to the host application's database.
[0.9.18] - 2026-01-08
No changes yet.
[0.9.17] - 2026-01-08
Added
- Bootstrap worker for async data loading: Refactored startup initialization into a dedicated
PaperTiger.Bootstrapworker that handles async data loading without blocking application startup - DataSource behaviour: New
PaperTiger.DataSourcebehaviour enables synchronization of initial billing data from external application databases - Payment method sync from database: StripityStripe adapter now loads existing payment methods and generates placeholders for missing customer
default_sourcetokens
Changed
- Trialing → active subscription transitions: Subscriptions updated with a past or
:nowtrial_endnow correctly transition fromtrialingtoactivestatus interval_countoptional in recurring prices: Matches Stripe API specs whereinterval_countdefaults to 1 if not provided
[0.9.16] - 2026-01-07
Changed
StripityStripe adapter now syncs from database instead of Stripe API: Completely rewrote
PaperTiger.Adapters.StripityStripeto query local database tables (billing_customers,billing_subscriptions,billing_products,billing_prices,billing_plans) instead of calling the real Stripe API. This properly mocks Stripe for dev/PR environments using stripity_stripe's local data.Configuration required: Add to your config:
config :paper_tiger, repo: MyApp.RepoNote: Auto-sync on startup is disabled when using database sync (repo isn't available at PaperTiger startup). You must manually trigger sync after your application starts, typically in your application's
start/2callback after the repo is started:# In your application.ex def start(_type, _args) do children = [MyApp.Repo, ...] opts = [strategy: :one_for_one, name: MyApp.Supervisor] result = Supervisor.start_link(children, opts) # Sync PaperTiger from database after repo is started if Application.get_env(:paper_tiger, :repo) do PaperTiger.Adapters.StripityStripe.sync_all() end result end
Added
- User adapter architecture: New
PaperTiger.UserAdapterbehavior allows customizing how user information (name, email) is retrieved for customers during sync - Auto-discovering user adapter:
PaperTiger.UserAdapters.AutoDiscoverautomatically discovers common user schema patterns including:- Email fields:
email,email_address, or foreign keyprimary_email_id→emails.address - Name fields:
name,full_name, orfirst_name + last_name - User tables:
usersoruser
- Email fields:
- Plan ID support for subscriptions: Subscription creation now accepts both
price_idandplan_id(legacy) for the:priceparameter, matching Stripe API behavior. Plans are automatically converted to price format in responses.
[0.9.15] - 2026-01-06
Added
- Stripe data sync adapter: Automatically syncs customer, subscription, product, price, and plan data from real Stripe API on startup when stripity_stripe is detected. Solves the problem of dev/PR apps losing subscription data on restart. Sync adapter is pluggable via
PaperTiger.SyncAdapterbehavior for custom implementations.
Changed
- Reduced debug logging: Removed per-operation debug logs from store operations (insert/update/delete/clear) and resource creation. Startup logging is now more concise with single-line summaries.
[0.9.14] - 2026-01-05
Fixed
init_datapriv paths now work in releases: Paths starting withpriv/(e.g.,init_data: "priv/paper_tiger/init_data.json") are now automatically resolved by searching all loaded applications' priv directories. This fixes init_data not loading in releases where the working directory differs from the project root.
[0.9.13] - 2026-01-04
Fixed
- Mix.env() check at runtime, not compile time: Dependencies are compiled in
:devenvironment by default, so the compile-time@mix_envmodule attribute was always:deveven when running tests. Now checksMix.env()at runtime (after verifying Mix is available) to correctly detect test environment.
[0.9.12] - 2026-01-04
Fixed
- Namespace isolation for InvoiceItem: Fixed
InvoiceItem.listto properly filter by namespace, preventing test isolation leaks when listing invoice items.
[0.9.11] - 2026-01-04
Fixed
- Mix.env() called at compile time: Fixed crash in releases where
Mix.env()was called at runtime but Mix isn't available in releases. Now captured at compile time via module attribute.
[0.9.10] - 2026-01-04
Added
PaperTiger.StripityStripeHackneyfor automatic sandbox isolation: New HTTP module that wraps:hackneyand injects namespace headers for test isolation when using stripity_stripe- Configure stripity_stripe with
http_module: PaperTiger.StripityStripeHackney - Works with child processes (LiveView, async tasks) via shared namespace in Application env
checkout_paper_tiger/1now automatically sets up shared namespace for child process support
- Configure stripity_stripe with
Changed
checkout_paper_tiger/1sets shared namespace via Application env: Child processes (like Phoenix LiveView) can now automatically access the same PaperTiger sandbox as the test process without additional configuration
[0.9.9] - 2026-01-04
Added
- Pre-defined Stripe test payment method tokens: PaperTiger now provides all standard Stripe test tokens (
pm_card_visa,pm_card_mastercard,pm_card_amex,tok_visa, etc.) out of the box- Card brand tokens: visa, mastercard, amex, discover, diners, jcb, unionpay (plus debit/prepaid variants)
- Decline test cards:
pm_card_chargeDeclined,pm_card_chargeDeclinedInsufficientFunds,pm_card_chargeDeclinedFraudulent, etc. - Tokens are loaded at startup and persist across
flush()calls - Test tokens work in namespace-isolated tests via global namespace fallback
[0.9.8] - 2026-01-03
Fixed
- Contract tests use pmcard* tokens: PaymentMethod contract tests now use Stripe test tokens (
pm_card_visa,pm_card_mastercard,pm_card_amex) instead of raw card data, which works with both PaperTiger mock and real Stripe API
[0.9.7] - 2026-01-03
Fixed
- Invoice
chargefield matches real Stripe behavior: Draft invoices no longer include thechargekey at all (not nil, just absent), matching real Stripe API behavior
Added
- Centralized test card helpers:
TestClient.test_card/0for real Stripe API testing andTestClient.test_card_simple/0for PaperTiger-style card data
[0.9.6] - 2026-01-03
Added
get_optional_integer/2helper: Distinguishes "key not present" from "0" for optional integer params liketrial_endnormalize_integer_map/1helper: Converts string integer values in maps (e.g., form-encoded params) to actual integers- Auto-create Plan for recurring Prices: When
Price.createis called withrecurringparams, a matching Plan object is automatically created (Stripe legacy API compatibility)
Fixed
- Subscription default status is "active": Fixed bug where subscriptions without trial periods incorrectly defaulted to "trialing" status
- Explicit status parameter respected:
Subscription.createnow respects explicitstatusparam instead of always computing it - Subscription list filtering: Uses
list_namespace/1for proper namespace-scoped queries instead of undefined store methods
[0.9.5] - 2026-01-03
Added
- Subscription items include
planfield for backwards compatibility: Stripe API populates bothplanandpriceon subscription items. PaperTiger now does the same viabuild_plan_from_price/1. - PaymentMethod.create supports custom IDs: Use
idparameter to create payment methods with deterministic IDs for testing. - Contract tests for subscription item plan field and payment method custom IDs
Fixed
- Price.recurring now includes
interval_count: Addedbuild_recurring/1function that defaultsinterval_countto 1 when not specified, matching Stripe API behavior. - Invoice list filtering by status: Fixed status filter to work correctly with string status values.
- PaymentMethod.list now requires customer parameter: Matches real Stripe API behavior. Returns empty list without customer param.
- PaymentMethods.find_by_customer uses proper namespacing: Fixed ETS query to use namespace-scoped keys for test isolation.
[0.9.4] - 2026-01-02
Added
ChaosCoordinator for unified chaos testing: New module consolidating all chaos testing capabilities
- Payment chaos: configurable failure rates, decline codes, per-customer overrides
- Event chaos: out-of-order delivery, duplicate events, buffered delivery windows
- API chaos: timeout simulation, rate limiting, server errors
- Statistics tracking for all chaos types
- Integrated with
Invoice.payfor realistic payment failure simulation
Contract tests for InvoiceItem, Invoice finalize/pay, and card decline errors
Fixed
- Subscription status now matches Stripe API exactly (e.g.,
activevstrialing) - TestClient normalizes delete responses with
deleted=truefield - Card decline test assertions check correct fields
Changed
- Clock uses ETS for lock-free reads:
now/0reads directly from ETS instead of GenServer call, avoiding bottleneck under load - Hydrator uses compile-time prefix registry: No runtime map traversal for ID prefix lookups
- Idempotency uses atomic select_delete: Fixes potential race condition
- ChaosCoordinator uses namespace isolation: Per-namespace ETS state with proper timer cancellation on reset
- Store modules export
prefixoption for Hydrator registry - Tests use
assert_receiveinstead ofProcess.sleepfor reliability
[0.9.3] - 2026-01-02
Added
- Test sandbox for concurrent test support: New
PaperTiger.Testmodule provides Ecto SQL Sandbox-style test isolation- Use
setup :checkout_paper_tigerto isolate test data per process - Tests can now run with
async: truewithout data interference - All stores now support namespace-scoped operations
- Automatic cleanup on test exit
- Use
- HTTP sandbox via headers:
PaperTiger.Plugs.Sandboxenables sandbox isolation for HTTP API tests- Include
x-paper-tiger-namespaceheader to scope HTTP requests to a test namespace - New
PaperTiger.Test.sandbox_headers/0returns headers for sandbox isolation - New
PaperTiger.Test.auth_headers/1combines auth + sandbox headers - New
PaperTiger.Test.base_url/1helper for building PaperTiger URLs
- Include
Changed
- Storage layer uses namespaced keys: All ETS stores now key data by
{namespace, id}instead of justid- Backwards compatible: non-sandboxed code uses
:globalnamespace automatically - New functions:
clear_namespace/1,list_namespace/1on all stores - Idempotency cache also supports namespacing
- Backwards compatible: non-sandboxed code uses
[0.9.2] - 2026-01-02
Fixed
- Proper Stripe error responses for missing resources: Instead of crashing, PaperTiger now returns the same error format as Stripe when a resource doesn't exist
- Returns
resource_missingerror code with proper message format: "No such <resource>: '<id>'" - Includes correct
paramvalues matching Stripe (e.g.,idfor customers,pricefor prices) - HTTP 404 status code for not found errors
- Returns
Added
- Contract tests verifying error responses match Stripe's format
[0.9.1] - 2026-01-02
Fixed
- Events missing
delivery_attemptsfield: Events created via telemetry now includedelivery_attempts: []field, fixing KeyError when accessing this field
Added
- Auto-register webhooks from application config on startup: PaperTiger now automatically registers webhooks configured via
config :paper_tiger, webhooks: [...]when the application starts, eliminating need for manual registration in your Application module
[0.9.0] - 2026-01-02
Fixed
- Subscription
latest_invoice: Now populated with the actual latest Invoice object for the subscription instead of always being null - PaymentIntent
chargesfield removed: Real Stripe API does not includechargeson PaymentIntent - charges are accessed via separate endpointGET /v1/charges?payment_intent=pi_xxx. PaperTiger now matches this behavior. - Charge
balance_transaction: Successful charges now create and link a BalanceTransaction with proper fee calculation (2.9% + $0.30) - Refund
balance_transaction: Refunds now create and link a BalanceTransaction with negative amounts - Contract tests now run against real Stripe: Removed all
paper_tiger_onlytagged tests. All contract tests now pass against both PaperTiger mock and real Stripe API.
Added
- Checkout Session completion support: New endpoints for completing and expiring checkout sessions
POST /v1/checkout/sessions/:id/expire- Expires an open session (matches Stripe API)POST /_test/checkout/sessions/:id/complete- Test helper to simulate successful checkout completion- Based on mode, creates appropriate side effects:
payment: Creates a succeeded PaymentIntentsubscription: Creates an active Subscription with itemssetup: Creates a succeeded SetupIntent
- Fires
checkout.session.completedandcheckout.session.expiredwebhook events - Creates PaymentMethod and fires
payment_method.attachedevent on completion
- Environment-specific port configuration: New env vars
PAPER_TIGER_PORT_DEVandPAPER_TIGER_PORT_TESTallow different ports per Mix environment. Enables running dev server and tests simultaneously without port conflicts. Precedence:PAPER_TIGER_PORT_{ENV}>PAPER_TIGER_PORT> config > 4001. PaperTiger.BalanceTransactionHelpermodule for creating balance transactions with Stripe-compatible fee calculations
Removed
- PaymentMethod raw card number support tests: Tests using raw card numbers don't work with real Stripe API. Use test tokens like
pm_card_visainstead.
[0.8.5] - 2026-01-02
Added
- Synchronous webhook delivery mode: Configure
webhook_mode: :syncto have API calls block until webhooks are delivered. Useful for testing where you need to assert on webhook side effects immediately after API calls. WebhookDelivery.deliver_event_sync/2function for explicit synchronous delivery
[0.8.4] - 2026-01-02
Fixed
- Subscription items now return full price object:
subscription.items.data[].priceis now a full price object (withid,object,currency, etc.) instead of just the price ID string, matching real Stripe API behavior - Same fix applied to
subscription_item.pricewhen creating/updating subscription items directly - When price doesn't exist in the store, returns a minimal price object with required fields for API compatibility
Added
- Contract test validating subscription item price structure against real Stripe API
TestClient.create_product/1andTestClient.create_price/1helpers for contract testing
[0.8.3] - 2026-01-02
Fixed
- Remove unused
cleanup_payment_method/1function that caused warnings-as-errors CI failure
[0.8.2] - 2026-01-02
Fixed
- Contract test fixes: Tests now properly pass the API key to all stripity_stripe calls
- PaymentMethod and Subscription tests: Skipped for real Stripe (require tokens/pre-created prices that can't be created via API) - these test PaperTiger's convenience features
- Clearer test mode messaging: Contract tests now display "RUNNING AGAINST REAL STRIPE TEST API" with explicit "API key validated as TEST MODE"
[0.8.1] - 2026-01-02
Added
Live key safety guard:
TestClientnow performs two-layer validation before running contract tests against real Stripe:- Validates API key prefix (rejects
sk_live_*,rk_live_*) - Makes a live API call to
/v1/balanceand verifieslivemode: false
This prevents accidental production usage even if someone crafts a key with a fake prefix
- Validates API key prefix (rejects
Fixed
- BillingEngine now retries existing open invoices instead of creating duplicates on each billing cycle
- Subscriptions correctly marked
past_dueafter 4 failed payment attempts
[0.8.0] - 2026-01-01
Added
PaperTiger.BillingEngineGenServer for subscription billing lifecycle simulation- Processes subscriptions whose
current_period_endhas passed - Creates invoices, payment intents, and charges automatically
- Fires all relevant telemetry events for webhook delivery (invoice.created, charge.succeeded, etc.)
- Two billing modes:
:happy_path(all payments succeed) and:chaos(random failures) - Per-customer failure simulation via
BillingEngine.simulate_failure/2 - Configurable chaos mode with custom failure rates and decline codes
- Integrates with PaperTiger's clock modes (real, accelerated, manual)
invoice.upcomingtelemetry event support in TelemetryHandler- Enable with config:
config :paper_tiger, :billing_engine, true
[0.7.1] - 2026-01-01
Added
- Custom ID support for deterministic data - pass
idparameter to create endpoints for Customer, Subscription, Invoice, Product, and Price resources - Enables stable
stripe_idvalues across database resets for testing scenarios PaperTiger.Initializermodule for loading initial data from config on startup- Config option
init_dataaccepts JSON file path or inline map with products, prices, and customers - Initial data loads automatically after ETS stores initialize, ensuring data is available before dependent apps start
[0.7.0] - 2026-01-01
Added
- Automatic event emission via telemetry - resource operations (create/update/delete) now automatically emit Stripe events and deliver webhooks
PaperTiger.TelemetryHandlermodule for bridging resource operations to webhook delivery- Comprehensive Stripe API coverage including Customers, Subscriptions, Invoices, PaymentMethods, Products, Prices, and more
- ETS-backed storage layer with concurrent reads and serialized writes
- HMAC-signed webhook delivery with exponential backoff retry logic
- Dual-mode contract testing (PaperTiger vs real Stripe API)
- Time control (real, accelerated, manual modes)
- Idempotency key support with 24-hour TTL
- Object expansion (hydrator system for nested resources)
PaperTiger.stripity_stripe_config/1helper for easy stripity_stripe integrationPaperTiger.register_configured_webhooks/0for automatic webhook registration from config- Environment variable support:
PAPER_TIGER_AUTO_STARTandPAPER_TIGER_PORT - Phoenix integration helpers and documentation
- Interactive Livebook tutorial (
examples/getting_started.livemd)