Better Payment

Configuration

Full configuration reference for betterPayment().

betterPayment() options

import { betterPayment, iyzico, paytr, akbank, parampos } from 'better-payment';

const payment = betterPayment({
  providers: {
    iyzico: iyzico({
      apiKey: string,
      secretKey: string,
      baseUrl?: string,  // default from mode
      locale?: 'tr' | 'en',
    }),
    paytr: paytr({
      merchantId: string,
      merchantKey: string,
      merchantSalt: string,
      testMode?: boolean,     // default: mode === 'sandbox'
      timeoutLimit?: number,  // iFrame timeout in minutes, default 30
    }),
    akbank: akbank({
      merchantSafeId: string,
      terminalSafeId: string,
      secretKey: string,
      subMerchantId?: string,
      testMode?: boolean,     // selects the test 3D gateway; default: mode === 'sandbox'
      gateway3dUrl?: string,  // override the securepay URL
    }),
    parampos: parampos({
      clientCode: string,
      clientUsername: string,
      clientPassword: string,
      guid: string,
    }),
  },
  defaultProvider: 'iyzico', // optional; a single provider becomes the default
  mode: 'sandbox',           // 'sandbox' | 'production' (default)
  logger,                    // optional, see below
  retry,                     // optional, see below
  fetch,                     // optional custom fetch (default: globalThis.fetch)
  validate: true,            // default; see Request Validation
  handler,                   // optional HTTP handler options
  plugins: [],               // optional, see Plugins
});

The keys of providers are the provider ids. They name the provider on the payment object (payment.iyzico, payment.use('iyzico')) and in the handler routes (/api/pay/iyzico/...). Any key works, so one provider can be configured twice, for example with two merchant accounts: { iyzicoTR: iyzico({...}), iyzicoEU: iyzico({...}) }. To leave a provider out, leave its key out.

Missing or empty credentials throw a ConfigurationError that lists the missing fields.

Default URLs

Providersandboxproduction
iyzicohttps://sandbox-api.iyzipay.comhttps://api.iyzipay.com
PayTRhttps://www.paytr.com (with test_mode=1)https://www.paytr.com
Akbankhttps://apipre.akbank.com/api/v1/payment/virtualposhttps://api.akbank.com/api/v1/payment/virtualpos
Paramposhttps://test-dmz.param.com.tr/turkpos.ws/service_turkpos_test.asmxhttps://posws.param.com.tr/turkpos.ws/service_turkpos_prod.asmx
Kuveyt Türkhttps://boatest.kuveytturk.com.tr/boa.virtualpos.serviceshttps://sanalpos.kuveytturk.com.tr/ServiceGateWay
Sipayhttps://provisioning.sipay.com.trhttps://app.sipay.com.tr
EST (isbank, ziraat, halkbank, teb, sekerbank)https://entegrasyon.asseco-see.com.trthe bank's host, e.g. https://sanalpos.isbank.com.tr

Set baseUrl in a provider's config to override its default.

Provider Access

payment.iyzico                       // Iyzico; only the configured providers exist
payment.paytr

payment.use('paytr')                 // throws ProviderNotEnabledError for unknown ids
payment.isProviderEnabled('iyzico')  // boolean
payment.getEnabledProviders()        // string[]: the keys of providers

Logging

logger: {
  debug: (message, meta) => {},
  info: (message, meta) => {},
  error: (message, error, meta) => {},
}

The logger receives the HTTP method, URL and status. Request and response bodies, which contain card data and credentials, are never logged.

Custom fetch

Provider API calls use globalThis.fetch with a timeout (30 s; 60 s for Parampos). Pass fetch to use your own implementation, for example a proxy-aware fetch or a stub in tests. It can also be set per provider, in the provider's config.

fetch: (input, init) => myFetch(input, init),

Retry

retry: {
  attempts: 3,              // total attempts including the first
  delay: 1000,              // ms between attempts
  statusCodes: [429, 503],  // retry on these HTTP statuses (network errors are always retried)
}

Only idempotent requests are retried: status, BIN and installment queries. Payment, refund and cancel requests are never retried. If one of them fails without a response, it returns status: 'pending' with errorCode: 'NETWORK_ERROR' so that you check the outcome with getPayment().

Request Validation

Payment and refund requests are checked before anything is sent to the provider. An invalid request returns status: 'failure' with code: 'INVALID_REQUEST' and errorCode: 'VALIDATION_ERROR'. Its errorMessage lists every invalid field:

Invalid request: paymentCard.cardNumber is not a valid card number (Luhn check failed); basketItems prices add up to 1.00 but price is 5.00
CheckApplies to
Card number (12–19 digits, Luhn), holder name, expiry month 1–12, not expired, CVC 3–4 digitsCard payments: iyzico, PayTR non-3D, Parampos, Akbank
price / paidPrice: positive, at most 2 decimalsAll payments and refunds
Basket item prices add up to priceiyzico
Non-empty basketiyzico, PayTR
Buyer email, IPv4/IPv6 and GSM format (when present)All payments
Required fields: buyer id, name, surname, email, identityNumber, registrationAddress, city, country, ip; billing contactName, city, country, addressiyzico (including checkout form and PWI)
Required fields: buyer email, ip, name, surname, gsmNumberPayTR
Required field: buyer ipParampos, Akbank

The checks only reject what the provider would reject anyway. They do not require paidPrice >= price, because discounts can lower it, and they do not verify the TCKN checksum, because foreign customers and placeholder values are accepted by providers.

Turn validation off globally with validate: false, or for a single provider in its config:

betterPayment({
  validate: false, // every provider
  providers: {
    iyzico: iyzico({ apiKey, secretKey, validate: true }), // override
  },
});

HTTP Handler

See BetterPaymentHandler for the options and the framework examples.

On this page