Changelog
Release history and breaking changes for better-payment.
The version history was reset. 0.0.1 is the first release of the reworked package. Releases 1.x–3.x had incorrect provider integrations and callback checks that could be bypassed. Do not use them. See What's New in 0.0.1 for the reasoning and a step-by-step migration guide.
0.6.0
Added
- Elysia adapter:
better-payment/elysiamounts the handler on Elysia (Bun) in one line:app.all('/api/pay/*', toElysiaHandler(payment), { parse: 'none' }). Thanks to @ScottHallchico.
0.5.2
Added
- Localized error messages: Russian (
ru).Accept-Language: ru-RUpicks Russian in the handler. Thanks to @ius-sharma.
0.5.1
Added
- Localized error messages: German (
de).Accept-Language: de-DEpicks German in the handler. Thanks to @ius-sharma.
Changed
- The repository moved to czaydev/better-payment.
0.5.0
Plugins and typed payment events. Breaking: betterPayment() replaces new BetterPayment(), and providers are created with factories. See Migrating to 0.5.
Changed (breaking)
betterPayment({ providers, plugins })replaces theBetterPaymentclass, and providers are created withiyzico(),paytr(),akbank()andparampos(). Theenabledflag is gone.- Any key of
providersis a provider id;payment.<id>is typed per provider.getEnabledProviders()returnsstring[].
Added
- Plugins: before/after hooks for provider operations, typed methods, handler endpoints and error codes. Write your own in a few lines.
- Payment events: one typed listener for every provider and flow, emitted only for verified results.
- Localized error messages: the first official plugin, in
better-payment/plugins. Customer-facing messages in English and Turkish. - Custom providers with
defineProvider(), and WebCrypto helpers for signing.
0.4.0
Testing without a sandbox and one-line framework integration. Existing 0.3.0 code keeps working.
Added
better-payment/testing:MockProvider, an in-memory provider for application tests, with no credentials and no network access. Magic card numbers cover declines, 3D Secure failures, lost responses and failing refunds; 3D Secure callbacks go through the real handler.- Framework adapters:
better-payment/next,better-payment/express,better-payment/fastifyandbetter-payment/honomount the handler in one line;toFetchHandlercovers Workers, Deno and Bun. - The documentation is available in Turkish.
Changed
PROVIDER_DEFAULT_URLSis keyed byRemoteProviderType(every provider exceptmock).
0.3.0
Edge runtimes and zero runtime dependencies. Application code keeps working unchanged.
Added
- Edge runtime support: the same package runs on Node.js 20+, Vercel Edge, Cloudflare Workers, Deno and Bun. It only uses
fetchand WebCrypto. fetchoption for a custom fetch implementation (proxy, test stubs).HttpClientandHttpErrorare exported for custom providers.
Changed
- No runtime dependencies: axios is removed. Timeouts, logging and retry rules are unchanged.
- Custom providers that extend
PaymentProviderusecreateHttpClient()instead ofsetupAxiosLogging()/setupAxiosRetry().
0.2.0
Pre-authorization and stored cards. Existing 0.1.0 code keeps working. Both features are tested against the real iyzico sandbox every night.
Added
- Pre-authorization:
authorize()/initThreeDSAuthorize()block an amount,capture()charges it (partial capture supported) andvoidAuthorization()releases it. iyzico, Parampos and Akbank; PayTR is not supported yet. - Stored cards: save a card with
saveCardduring a payment, pay later withstoredCard: { customerToken, cardToken }, and manage cards withsaveCard(),listCards()anddeleteCard(). iyzico and PayTR; Parampos and Akbank are not supported yet. - Handler routes and browser client methods for both. Capture, void and the card routes require an
authorizehook.
Changed
PaymentRequest.paymentCardis optional in the types; one ofpaymentCardorstoredCardis required at runtime.- The handler answers
400instead of500when a provider does not support an operation.
0.1.0
The first feature release after the reset. Existing 0.0.1 code keeps working; behavior changes are listed under "Changed". The iyzico integration is now tested against the real iyzico sandbox every night.
Added
- Unified error codes: every failed result carries
code(INSUFFICIENT_FUNDS,CARD_DECLINED,THREEDS_FAILED,INVALID_HASH, ...).errorCodekeeps the provider's raw code. - Request validation before the provider is called. Invalid requests return
code: 'INVALID_REQUEST'with every invalid field listed. Turn it off withvalidate: false. - Parampos installments:
installmentInfo(),calculatePaidPrice()andgetInstallmentRates()use your Param rates. - Duplicate callbacks are processed once, and mutating handler routes accept an
Idempotency-Keyheader. TheIdempotencyStoreis in-memory by default; use Redis or a database with several instances. PaymentErrorCodeandPaymentStatusare exported frombetter-payment/client.
Changed
- Request validation errors return
errorCode: 'VALIDATION_ERROR';errorCodeused to be empty. - Stricter public types, with no
any:rawResponseand the handler bodies areunknown, andonCallback/callbackRedirectreceive aPaymentResponse. NETWORK_ERRORmessages name the transport error (ECONNRESET, timeout, ...).- The published build is minified: about 19 kB gzip instead of 29.6 kB.
0.0.1
Security
- Parampos: the 3D callback hash is verified only with the GUID from your configuration. Earlier versions read the GUID from the callback payload, which let anyone forge a successful payment.
- Parampos / Akbank: a 3D callback no longer counts as a successful payment on its own. Parampos finalizes with
TP_WMD_Pay; Akbank uses the 3D_PAY model with an HMAC-signed result. A missing result code is no longer treated as success. - HTTP handler: secure by default. By default it exposes only callbacks and card queries. Refund, cancel, payment lookup and subscription management require an
authorizehook. - Retries only re-send idempotent requests, so a timeout can no longer cause a double charge or double refund.
- Signatures are compared in constant time.
Provider fixes
- PayTR: requests are signed with
merchant_key, withmerchant_saltappended to the signed data.user_basketis base64 JSON with TL prices.merchant_oidmust be alphanumeric.test_modefollows sandbox mode. Refund amounts are in TL. Status queries use/odeme/durum-sorgu. Notifications are answered with plainOK. Secrets are no longer sent in request bodies. - Parampos: uses
TP_WMD_UCD/TP_WMD_Pay, comma-decimal amounts and the documented hash. Refunds and cancels useTP_Islem_Iptal_Iade_Kismi2, status queriesTP_Islem_Sorgulama4and BIN lookupsBIN_SanalPos. Made-up installment rates were removed. - Akbank: rewritten against the Akbank Sanal POS JSON API.
- iyzico: Checkout Form and PWI results report the payment's own status (
paymentStatus). 3DS completion requiresmdStatus=1.retrieveSubscriptionuses GET.
Breaking changes (compared with 3.x)
| Area | Before | Now |
|---|---|---|
| PayTR config | apiKey, secretKey, merchantId, merchantSalt | merchantId, merchantKey, merchantSalt, testMode? |
| Akbank config | merchantId, terminalId, storeKey, … | merchantSafeId, terminalSafeId, secretKey |
| Parampos config | also required apiKey/secretKey | clientCode, clientUsername, clientPassword, guid |
paymentId (PayTR/Akbank/Parampos) | provider token / transaction GUID | your order id |
| Timeouts | failure | pending + errorCode: 'NETWORK_ERROR' |
| Handler | every route public, always HTTP 200 | opt-in routes, authorize, 422 on failure |
| Unsupported currency | silently TRY | error |