Repository navigation
Added
-
Add the Albaraka Türk provider (e-POS, Posnet JSON API): non-3D sale, 3D Secure completed with the 3D data and the order values from your records, refund, cancel, order inquiry and pre-authorization. Generate bank-compatible order ids when conversationId is omitted. Thanks to @sizinajans (#197).
-
Add
commissionRouter(subpathbetter-payment/commission-router), which routes each payment to the cheapest eligible provider from merchant-defined commission rate tables, with quotes that explain every choice, tie/share-cap/override policies, safe fallbacks and same-provider refunds Thanks to @Enard52. -
Plugin: currency conversion with TCMB exchange rates, markup, and refund protection Thanks to @Enard52.
-
Add the Denizbank provider (InterVPos): non-3D sale, 3D Secure (3D Model) verified from HASHPARAMS and charged with the 3D Secure data, refund and cancel Thanks to @sizinajans (#208).
-
EST: Girogate alternative payment methods (iDEAL, Sofort, giropay, EPS, Bancontact, Przelewy24, Trustly and more) with
initThreeDSPayment({ girogate: { method, country } })on the 3d_pay store types. APendingcallback returnspendingwith the order id. The EST currency list gains the methods' currencies (PLN, SEK, CHF, ...) Thanks to @sizinajans (#210). -
Add two banks to the EST provider:
est({ bank: 'akbankest' })for Akbank's older virtual POS (sanalakpos.com) andest({ bank: 'halkbankmkd' })for Halkbank Macedonia, with Macedonian denars (MKD, sent as807) Thanks to @sizinajans (#209). -
Plugin: provider failover with circuit breaker and zero double-charge guarantee Thanks to @Enard52 (#247).
-
Add the
fraudRulesplugin for card-testing protection: velocity counters per IP, card, BIN, email or buyer (with consecutive-decline and distinct-value rules), blocked BIN ranges, an amount limit, your own rule functions and an optional challenge hook, all checked before the provider is called. Every counter key is a salted HMAC digest, so a card number never reaches the store, the log or a message, and nothing is read fromX-Forwarded-Forunless you name that header and your proxies. It ships as its ownbetter-payment/fraudentry point, sobetter-payment/pluginsstays the same size. Closes #105 Thanks to @Enard52. -
Add the Garanti BBVA provider (GVP, XML API 512): non-3D and 3D Secure payments (3D, 3D_PAY), GarantiPay (
securityLevel: 'GARANTI_PAY'), refund, void and order inquiry. 3D callbacks are verified fromhashparamsand confirmed with the bank Thanks to @sizinajans (#195). -
Rename the
localizedErrorsplugin toi18nand make it work like the i18n plugin of Better Auth:i18n({ translations, defaultLocale, detection, localeCookie, getLocale }). English is built in, and the built-in Turkish, German, Russian and Arabic translations come inlocales. HTTP handler responses pick their language with theheader(Accept-Language),cookieandcallbackstrategies, in the order you choose;payment.errors.detectLocale(request)runs the same detection.localizedErrors,errorMessages,LocalizedErrorsOptionsandErrorMessageDictionarystill work and are deprecated. -
Add Model Context Protocol (MCP) server plugin (
mcp) for AI agents and assistants. Thanks to @Enard52. -
Add the
better-payment/nestjsframework adapter:toNestHandler(payment)returns a single request handler for a NestJS controller that works on both the Express and the Fastify platform. It has no@nestjs/*dependency and reads the request and response objects injected by@Req()and@Res()Thanks to @Enard52 (#270). -
Add
examples/next-shop, a complete Next.js App Router and Prisma shop: server-side cart totals, 3D Secure through the handler, orders updated by payment events and a privileged refund route, with a Playwright happy-path test and a MockProvider that needs no credentials Thanks to @Enard52 (#287). -
Add the
openAPIplugin: generates an OpenAPI 3.1.0 document and serves an interactive Scalar API reference page for all HTTP handler routes, configured providers, and registered plugin endpoints. The document lists only the routes the handler serves, under itsbasePath. The reference is off inmode: 'production'unlessdisabled: falseis set, andprivileged: trueputs it behind the handler'sauthorizehook. Plugin endpoint contexts now always havehtml(), and plugins get a read-onlyendpointslist. Thanks to @Enard52. -
PayTR:
threeDSMode: 'direct'runs 3D Secure payments with a new card through the Direct API (your own card form, posted from the customer's browser) instead of the iFrame Thanks to @sizinajans (#199). -
Add
pendingResolverplugin to query and resolve timed out payments (NETWORK_ERROR) on a schedule, preventing double charging and emitting final outcome events. Thanks to @Enard52. -
feat(plugins): add captcha plugin supporting Turnstile, reCAPTCHA, and hCaptcha (#173)
- Add
captchaplugin to verify tokens before payment execution - Support Cloudflare Turnstile, Google reCAPTCHA v3 (with
minScore), and hCaptcha - Read token from HTTP headers or request body candidates
- Normalize failure error codes to
CAPTCHA_MISSINGandCAPTCHA_INVALID - Exclude provider callback and 3DS completion operations Thanks to @Enard52.
- Add
-
Add the QNB provider (PayFor): non-3D sale, 3D Secure (3D Model and 3D Pay) verified with the ResponseHash and confirmed with the bank, refund, cancel and order inquiry Thanks to @sizinajans (#207).
-
Add QNBpay, which runs on Sipay's platform:
qnbpay()orsipay({ brand: 'qnbpay' })uses QNBpay's hosts and provider id, with the same payments, 3D Secure, refunds, pre-authorization and installment queries as Sipay Thanks to @sizinajans (#232). -
Add
rateLimitplugin for HTTP handler endpoints with per-route throttling, a customizable key extractor, sliding window store, and callback exclusion. The client identity defaults to a safe source: proxy headers are only read withtrustProxy, otherwise an explicitkeyis required. Thanks to @Enard52. -
Plugin: redact card data and personal data from logs and events (PCI DSS & KVKK) Thanks to @Enard52.
-
Add the
refundApprovalplugin: requires manager approval before processing refunds and cancellations above a specified threshold, with in-memory or custom store, approval/rejection methods, self-approval prevention, Slack/webhook notify callback, and handler endpoints (/refunds/pending,/refunds/approve,/refunds/reject). Thanks to @Enard52. -
Add
routing(subpathbetter-payment/routing): rule-based provider routing with BIN, bank, card family, card type, card origin, amount, currency, installment, 3D Secure, operation and custom predicate conditions; ordered and deterministic weighted targets; injected health, BIN and key-value store hooks; explainable decisions and audit listeners; follow-up operations that stay on the provider that handled the payment; and no re-routing after a possible charge. Thanks to @Enard52. -
Add the
telemetryplugin: one OpenTelemetry-compatible span per provider call with the provider, operation, result, normalized error code, HTTP status, retry count and duration, plus apayment.callscounter and apayment.durationhistogram. It has no dependencies — it accepts any structurally compatibletracerandmeter, so a real@opentelemetry/apitracer works and applications without OpenTelemetry can pass nothing at all. Attributes are low-cardinality and never contain card data, and a throwing tracer is reported through the logger without changing the payment. Thanks to @Enard52. -
feat(plugins): add threeDSTimeout plugin to expire abandoned 3D Secure payments (#180)
- Add
threeDSTimeoutplugin tracking initiated 3D Secure payments across providers - Support configurable timeout duration (e.g.
'15m'), pluggableThreeDSFlowStore, andonExpirecallback - Query provider before expiring to protect against late successful callbacks
- Provide
payment.threeDS.expireStale()for serverless and cron schedulers Thanks to @Enard52.
- Add
-
Add the VakıfBank provider (VPOS 7/24): non-3D sale, 3D Secure through the MPI and VakıfBank's 3DS product, refund and cancel (no payment status query: VakıfBank's record search cannot see cancels and refunds) Thanks to @sizinajans (#206).
-
Add the
webhookForwardingplugin: forwards payment events to external microservices as HMAC-SHA256 signed JSON webhooks with configurable event filtering, exponential backoff retries, sensitive card data sanitization, and theverifyForwardedWebhookhelper for receivers. Thanks to @Enard52. -
Add the Yapı Kredi provider (Posnet XML): non-3D sale, 3D Secure through Posnet OOS (resolved and charged with a MAC over your order, the bank's answer MACs checked), refund, cancel and order inquiry (agreement). Generate bank-compatible order ids when conversationId is omitted. Thanks to @sizinajans (#205).
Fixed
failover: a payment that failed with an HTTP 500, 502 or 504 answer no longer moves to a backup provider, because the provider may have charged the card before the error. These statuses are out of the defaultoncodes and are ignored even when listed; 503 still fails over.- Serialize PayTR Direct API baskets as JSON for new-card and stored-card payments, including 3D Secure forms. Keep iFrame baskets Base64-encoded and preserve their token signatures. Thanks to @sizinajans (#199).
- Plugin endpoints refuse HTML form bodies on
POSTwith 415, like the built-in actions, so another site cannot post to them with the visitor's cookies (CSRF); an endpoint a third party posts a form to sets the newacceptForm: true. AnonRequesthook that throws now stops the request with 503 instead of letting it through, andrateLimitgetspassOnStoreErrorfor applications that prefer to stay open while their store is down. redact:fieldsandcustomnow add to the defaults instead of replacing them, so listing one extra field no longer unmasks the CVC or the TCKN pattern;cardHolderName,securityCodeand provider secrets (storeKey,hashKey,apiSecret,clientSecret,terminalProvPassword) are masked by default; and the result the call returns keepspaymentId,conversationId, the 3D Secure and checkout form pages and the stored card tokens as the provider sent them.refundApproval: the approve and reject endpoints take the approver from the newgetActoroption instead of the request body (and answer 403 without it), a request body can no longer mark a refund as already approved, cancellations wait for approval unless the newcancelAmountoption puts them under the threshold, amounts are parsed strictly, and approving the same request twice refunds once.