# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [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
path** — `accountId` 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}/paymenthandles` →
`POST /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/2` —
`GET /customers?merchantCustomerId={id}`, previously missing entirely.
- `Customers.create_single_use_customer_token/2` —
`POST /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.