better-payment

What's New in 0.0.1

Why the version history was reset, how the library works now, and how to migrate from 3.x.

better-payment 0.0.1 is a fresh start. We reset the version number on purpose. It is the first release of a library that was rebuilt around one rule: a payment is successful only when the provider confirms it, and every callback is verified with your own credentials.

Releases 1.x, 2.x and 3.x are deprecated. They contained provider integrations that did not match the providers' real APIs, and callback checks that could be bypassed. If you use one of them, upgrade to 0.0.1.

Why go back to 0.0.1?

Normally a big release would be 4.0.0. We did not do that, because a higher number would suggest that 4.0.0 builds on 3.x. It doesn't. A review of the package found that the old releases could not be trusted:

AreaProblem in 1.x–3.x
ParamposThe 3D callback hash was checked with a GUID taken from the callback itself. Anyone could forge a "successful" payment.
Parampos / AkbankA bank callback was treated as a completed payment. The payment was never finalized with the bank, and Akbank defaulted to "success" when the result code was missing.
PayTRSignatures used the wrong key. Real notifications were rejected, and test_mode never turned on in sandbox.
AkbankRequests went to endpoints that do not exist on Akbank's API.
iyzicoCheckout Form reported success when the API call succeeded, even if the payment failed.
HTTP handlerEvery route was public, including refunds, cancellations and subscription management. The amount came from the browser.
RetriesA timed-out payment could be sent again and charge the customer twice.

Fixing these required breaking changes to configs, IDs, statuses and the handler. Instead of adding one more major version on top of those releases, we started the version history again. 0.0.1 is the baseline; everything before it is legacy.

Because 0.0.1 is lower than 3.0.1, a range like "better-payment": "^3.0.1" will never pick up the new version. Upgrade explicitly: npm install better-payment@latest.

How the new system works

1. The provider decides, not the callback

Browser callbacks and 3D Secure returns are only claims. The library checks each one before it trusts it:

ProviderWhat happens after 3D Secure
iyzicoRequires status=success and mdStatus=1, then authorizes with iyzico.
ParamposVerifies islemHash with your GUID, requires mdStatus=1, then finalizes with TP_WMD_Pay. Only a returned Dekont_ID means success.
AkbankVerifies the HMAC-SHA512 signature, which must cover the result code, the order id and your terminal. Only VPS-0000 means success.
PayTRVerifies the server-to-server notification signed with your merchant_key. The browser redirect never carries the result.

A forged, tampered or failed callback always returns status: 'failure'.

2. Four clear statuses

statusMeaningWhat you do
successThe provider confirmed itFulfil the order
failureThe provider rejected itShow errorMessage, let the customer retry
pendingWaiting for the customer (3DS, bank transfer) or the outcome is unknownSee below
cancelledVoided or fully refunded (status queries)Update the order

3. Unknown is not failure

If a payment request times out, the bank may still have charged the card. Such requests now return status: 'pending' with errorCode: 'NETWORK_ERROR' instead of failure. Payment, refund and cancel requests are never retried automatically; only read-only queries are.

const result = await payment.paytr.createPayment(request);

if (result.errorCode === 'NETWORK_ERROR') {
  const status = await payment.paytr.getPayment(result.paymentId!);
  // decide based on the real outcome
}

4. Your order id is the payment id

For PayTR, Parampos and Akbank, paymentId is your order id: the conversationId you send, or a generated alphanumeric id if you don't send one. Store it with your order. refund(), cancel() and getPayment() use it.

5. A handler that is closed by default

The HTTP handler exposes only what a payment flow needs from the outside: provider callbacks and card queries. Everything else must be enabled by you, and sensitive actions also need an authorize hook:

handler: {
  allowedActions: ['payment/init-3ds', 'payment/complete-3ds', 'callback', 'refund'],
  authorize: async (ctx) => /* your auth */ true,
  transformRequest: async (ctx) => buildRequestFromOrder(ctx.body.orderId), // amounts from your DB
  onCallback: async (result) => updateOrder(result.paymentId, result.status),
}

See BetterPaymentHandler for the details.

6. One config per provider

Each provider takes only the credentials it actually uses. Missing values fail right away with a ConfigurationError that lists the missing fields. Setting mode: 'sandbox' switches to the sandbox URLs and the provider test modes.

Migrating from 3.x

Step 1: Install

npm install better-payment@latest

Check that package.json now shows "better-payment": "^0.0.1".

Step 2: Update provider configs

// 3.x
paytr: { enabled: true, config: { apiKey: KEY, secretKey: KEY, merchantId, merchantSalt } }
akbank: { enabled: true, config: { apiKey, secretKey, merchantId, terminalId, storeKey } }
parampos: { enabled: true, config: { apiKey: GUID, secretKey: PASS, clientCode, clientUsername, clientPassword, guid } }

// 0.0.1
paytr: { enabled: true, config: { merchantId, merchantKey, merchantSalt } }
akbank: { enabled: true, config: { merchantSafeId, terminalSafeId, secretKey } }
parampos: { enabled: true, config: { clientCode, clientUsername, clientPassword, guid } }

iyzico is unchanged: { apiKey, secretKey }. Add mode: 'sandbox' while you test.

Akbank now uses the Sanal POS JSON API. Get merchantSafeId, terminalSafeId and the secret key from the Akbank Sanal POS panel. The old storeKey values do not work with it.

Step 3: Store the order id

Send your order id as conversationId (letters and digits only for PayTR), and use the returned paymentId for refund, cancel and getPayment.

Step 4: Handle pending and NETWORK_ERROR

Anywhere you checked status === 'failure' after a timeout, check the real outcome with getPayment() before you tell the customer the payment failed.

Step 5: Configure the handler

If you use payment.handler, list the actions you need in allowedActions. Add authorize if you enable refunds, cancellations, payment lookups or subscription management. Set transformRequest so that amounts come from your server, not from the browser.

Step 6: Point callbacks at the new routes

ProviderURL
iyzico / Parampos / AkbankcallbackUrl: https://yoursite.com/api/pay/<provider>/payment/complete-3ds
PayTRNotification URL in the PayTR panel: https://yoursite.com/api/pay/paytr/callback (answers OK)

Step 7: Test in sandbox

Run one successful payment, one declined payment, one refund and one status query per provider with mode: 'sandbox' before you go live.

FAQ

Will 3.x keep working? The old releases stay on npm, but they are deprecated and will not receive fixes. Several of their provider flows never worked against the real APIs.

Why not 4.0.0? 4.0.0 would suggest an upgrade path from 3.x. 0.0.1 makes it clear that this is a new baseline, and future releases follow semantic versioning from here.

Is the API stable? 0.x releases can still change the API. Breaking changes are always listed in the changelog, with migration notes.

On this page