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.

[1.0.0] - 2026-06-21

Verified

  • The one remaining paysafe-specific Dialyzer note after the spec fix above — client.ex: the catch-all clause in Paysafe.Client.ensure_map/1 can never match — was confirmed via req's own published documentation to be a test-double limitation, not a real defect: Req.Response.t()'s body field is officially typed as binary() | %Req.Response.Async{} | term(), i.e. genuinely unconstrained, because different req decode steps can leave it as a map, raw binary, async stream struct, or nil. No finite-clause stand-in module can express that, so Dialyzer narrows its inferred type to only the shapes the stand-in actually constructs — making the real library's necessary defensive catch-all look dead code only in this constrained local analysis. The catch-all is correct, intentional defensive code against the real dependency and was deliberately left unchanged.

Fixed (Dialyzer audit)

  • Paysafe.Telemetry.span/6 had its 3rd and 4th @spec parameter types transposed — declared as (Config.t(), atom(), String.t(), atom(), String.t(), fun) when the actual function head and every call site pass (config, api, method, path, operation, fun), i.e. atom() then String.t(). Since Elixir doesn't enforce specs at runtime this never caused incorrect behavior, but it broke Dialyzer's contract checking for every call into Telemetry.span/6 — and because Paysafe.Client.request/6 (used by every public API function in the library) routes through it, one transposed spec fanned out into 200+ "has no local return" warnings across nearly the entire codebase. Verified the fix collapses the warning count from 268 to 59 (all 59 remaining are pre-existing Elixir 1.14 standard-library warnings unrelated to this package, confirmed by running the same Dialyzer pass against a clean checkout of Elixir's own stdlib in isolation) when run with a complete PLT against the real Elixir/OTP standard library.

Fixed (critical — core Payments API URL structure)

The entire core Payments API surface (PaymentHandles, Payments, Settlements, Refunds, Payouts, Verifications, Customers) was built with /accounts/{account_id}/...-nested URLs, modeled on the legacy Alternative Payments API convention. Verification against eight+ independently fetched real request/response examples confirmed the modern Payments API actually uses flat URLs with no account ID anywhere in the pathaccountId is instead an optional field inside the JSON request body, used only when an API key has multiple accounts configured for the same payment method/currency combination. This was the single highest-impact bug in the library, since it affected the most-used part of the surface. Specific corrections:

  • PaymentHandles.create/3: POST /accounts/{id}/paymenthandlesPOST /paymenthandles with accountId merged into the body.
  • Payments.create/3, get/3, list/2, cancel/3: all /accounts/{id}/payments... paths → flat /payments... (payments are scoped by the payment handle token, not an account ID).
  • Settlements.create/4: flattened to /payments/{paymentId}/settlements (still nested under the payment, just without the account prefix).
  • Settlements.get/3, cancel/3: changed from /accounts/{id}/payments/{paymentId}/settlements/{settlementId} (3 path params) to the flat /settlements/{settlementId} (1 path param) — function arity changed, dropping the now-unnecessary payment_id argument.
  • Refunds.create/4: structural correction, not just a path-prefix fix — refunds are nested under /settlements/{settlementId}/refunds, never under /payments/{paymentId}/refunds. The settlement ID equals the payment ID only when settleWithAuth was true on the original payment.
  • Refunds.get/3, cancel/3: changed to the flat /refunds/{refundId}function arity changed, dropping the payment_id argument.
  • Payouts.standalone_credit/2, original_credit/2: flattened to /standalonecredits and /originalcredits with accountId in the body.
  • Verifications.create/2: flattened to /verifications.
  • Customers.create/2, get/2, update/3, delete/2, and all payment-handle sub-resources: flattened to /customers..., with accountId in the body for create/2.
  • Paysafe.cancel_settlement/3, Paysafe.create_refund/3, Paysafe.cancel_refund/3 facade functions: arity changed to match the corrected underlying module signatures.

Added

  • Customers.get_by_merchant_customer_id/2GET /customers?merchantCustomerId={id}, previously missing entirely.
  • Customers.create_single_use_customer_token/2POST /customers/{id}/singleusecustomertokens, a real, previously unimplemented endpoint used to tokenize a customer's entire saved profile (cards, addresses, bank mandates) for 900 seconds, e.g. for one-time CVV re-collection on a saved card.

Fixed (post-release audit against verified API examples — earlier round)

  • Applications API: base path corrected from the fabricated /accountmanagement/v1 to the verified /merchant/v1; added the missing submit/3 (PATCH) and get_terms_and_conditions/3 operations.
  • Customer Identity API: base path corrected from /paymenthub/v1/accounts/{id}/customeridentity to the verified flat resource /customeridentification/v1/identityprofiles (no account ID in the path); decision enum corrected from a fabricated :success | :error | :pending to the verified :success | :error | :fail | :outsort; added the rerun/3 operation with a documented warning not to rerun :fail decisions.
  • Bank Account Validation API: base path corrected from /paymenthub/v1/... to the verified /bankaccountvalidator/v1/accounts/{id}/verifications; the entire request/response shape was rebuilt from a fabricated micro-deposit-style flow to the verified redirect-based open-banking session flow.
  • Payment Scheduler API: base path corrected from the fabricated /recurring/v1 to the verified /subscriptionsplans/v1.

Removed

  • Paysafe.NetworkTokenization — this was calling fabricated provision/get/delete endpoints that do not exist. Network tokenization is not a separate API; it is accessed via card.network_token fields on Paysafe.Payments.PaymentHandles.create/3, now documented there instead.
  • Paysafe.AccountUpdater's REST functions — this product has no HTTP/JSON API at all; it is delivered via SFTP + PGP-encrypted batch files or automatic back-office configuration. The module now exists solely as a @moduledoc explaining this and pointing to the real process.

Added

  • Paysafe.InteracVerificationService — Interac AML Assist identity verification (Canada), verified against real endpoint examples.
  • Paysafe.Types.BankVerification, Paysafe.Types.IdentityProfile, Paysafe.Types.Application — typed structs for the corrected APIs above.
  • Config.bank_account_validator_url/1 and Config.customer_identification_url/1.
  • Documentation for the Partial Authorization Service (PAS) fields (allow_partial_auth, group_id) on Payments.create/3 — confirmed to be parameters on the existing payment call, not a separate endpoint.
  • A "Known limitations" section in the README documenting two products (Merchant Termination Inquiry API, PayFac Sub-merchant API) whose API reference pages render client-side and expose no verifiable endpoint shape through any available documentation source — these were deliberately left unimplemented rather than guessed.

Added (initial release)

  • Initial release.
  • Payments API: Payment Handles, Payments, Settlements, Refunds, Payouts (standalone credits & original credits), Verifications, and Customer Vault (profiles, multi-use payment handles).
  • Payment Scheduler API: Plans and Subscriptions with full lifecycle management (suspend, reactivate, cancel).
  • Applications API: Programmatic merchant onboarding, document upload.
  • Value Added Services: FX Rates, Customer Identity (KYC), Bank Account Validation, Network Tokenization, Account Updater.
  • Webhooks: HMAC-SHA256 signature verification with constant-time comparison, typed event parsing, and topic-based event routing.
  • Configurable HTTP client with exponential backoff retry, token-bucket rate limiting (ex_rated), and full Telemetry instrumentation.
  • Strongly-typed response structs for every API resource (Paysafe.Types.*).
  • Structured, typed error handling via Paysafe.Error with retryability classification.
  • Config validation via NimbleOptions, including a base_url_override escape hatch for testing and proxying.
  • Comprehensive test suite (unit tests + Bypass-based HTTP integration tests) covering every public function.