Documentation

better-payment

A unified, type-safe payment gateway library for Node.js applications. Integrate multiple Turkish payment providers with a single consistent API.

Introduction

better-payment is an open-source Node.js library that provides a unified API for integrating multiple Turkish payment gateways. Instead of learning and maintaining separate integrations for each provider, you configure them all once and use the same method calls regardless of the provider.

Supported providers:

  • İyzico — V2 Authorization, Checkout Form, Subscriptions, Installments
  • PayTR — Card Payments, 3D Secure, Hosted Checkout, IBAN Transfer
  • Parampos — SOAP API, 3D Secure, Installments, Refund & Cancellation
Framework-agnostic: Works with Next.js, Express, Fastify, NestJS, and any other Node.js framework. No adapter needed.

Installation

Requires Node.js 20+.

terminal
npm install better-payment

Or using other package managers:

terminal
yarn add better-payment
pnpm add better-payment

Quick Start

Here's everything you need to process your first payment:

lib/payment.ts
import { BetterPayment } from "better-payment";

const payment = new BetterPayment({
  defaultProvider: "iyzico",
  providers: {
    iyzico: {
      enabled: true,
      apiKey: process.env.IYZICO_API_KEY!,
      secretKey: process.env.IYZICO_SECRET_KEY!,
      baseUrl: "https://sandbox-api.iyzipay.com", // or production URL
    },
  },
});

export default payment;
app/api/checkout/route.ts
import payment from "@/lib/payment";

export async function POST(req: Request) {
  const { amount, buyerInfo } = await req.json();

  const result = await payment.createPayment({
    price: amount,
    currency: "TRY",
    buyer: buyerInfo,
    basketItems: [
      { id: "item-1", name: "Product", price: amount, category: "General" },
    ],
    billingAddress: {
      contactName: buyerInfo.name,
      city: "Istanbul",
      country: "Turkey",
      address: "Billing address here",
    },
  });

  return Response.json(result);
}

Configuration

The BetterPayment constructor accepts a configuration object with the following structure:

lib/payment.ts
const payment = new BetterPayment({
  defaultProvider: "iyzico",  // optional: sets the default provider
  providers: {
    iyzico: { ... },
    paytr: { ... },
    parampos: { ... },
  },
});
PropertyTypeRequiredDescription
defaultProvider"iyzico" | "paytr" | "parampos"NoThe provider used when no explicit provider is selected via use().
providersProvidersConfigYesAn object containing the configuration for each provider.

İyzico Configuration

PropertyTypeRequiredDescription
enabledbooleanYesEnable or disable this provider.
apiKeystringYesYour İyzico API key.
secretKeystringYesYour İyzico secret key.
baseUrlstringYesAPI base URL. Use sandbox for testing.
lib/payment.ts
iyzico: {
  enabled: true,
  apiKey: process.env.IYZICO_API_KEY!,
  secretKey: process.env.IYZICO_SECRET_KEY!,
  baseUrl: "https://sandbox-api.iyzipay.com",
  // Production: "https://api.iyzipay.com"
}

PayTR Configuration

PropertyTypeRequiredDescription
enabledbooleanYesEnable or disable this provider.
merchantIdstringYesYour PayTR merchant ID.
merchantKeystringYesYour PayTR merchant key.
merchantSaltstringYesYour PayTR merchant salt.
lib/payment.ts
paytr: {
  enabled: true,
  merchantId: process.env.PAYTR_MERCHANT_ID!,
  merchantKey: process.env.PAYTR_MERCHANT_KEY!,
  merchantSalt: process.env.PAYTR_MERCHANT_SALT!,
}

Parampos Configuration

PropertyTypeRequiredDescription
enabledbooleanYesEnable or disable this provider.
clientCodestringYesYour Parampos client code.
clientUsernamestringYesYour Parampos client username.
clientPasswordstringYesYour Parampos client password.

Environment Variables

Store all credentials in environment variables. Never hardcode them.

.env.local
# İyzico
IYZICO_API_KEY=your_api_key_here
IYZICO_SECRET_KEY=your_secret_key_here

# PayTR
PAYTR_MERCHANT_ID=your_merchant_id
PAYTR_MERCHANT_KEY=your_merchant_key
PAYTR_MERCHANT_SALT=your_merchant_salt

# Parampos
PARAMPOS_CLIENT_CODE=your_client_code
PARAMPOS_USERNAME=your_username
PARAMPOS_PASSWORD=your_password
Never commit .env files to version control. Add them to .gitignore.

API Reference

use(provider)

Switches to a specific provider for the next call. Returns the payment instance for method chaining.

// Use a specific provider for this call
const result = await payment.use("paytr").createPayment({ ... });

// Chain multiple calls
const iyzico = payment.use("iyzico");
const checkout = await iyzico.createCheckoutForm({ ... });

createPayment(options)

Creates a standard (non-3D) payment request.

const result = await payment.createPayment({
  price: "150.00",       // total amount as string
  currency: "TRY",       // ISO 4217 currency code
  buyer: {
    id: "user-123",
    name: "John",
    surname: "Doe",
    email: "john@example.com",
    identityNumber: "11111111111",
    ip: "85.34.78.112",
    city: "Istanbul",
    country: "Turkey",
    address: "Billing address",
    zipCode: "34000",
  },
  basketItems: [
    {
      id: "item-1",
      name: "Product Name",
      price: "150.00",
      category: "Electronics",
      itemType: "PHYSICAL",  // or "VIRTUAL"
    },
  ],
  billingAddress: {
    contactName: "John Doe",
    city: "Istanbul",
    country: "Turkey",
    address: "Nispetiye Cd. No:1",
    zipCode: "34000",
  },
  shippingAddress: { ... }, // optional
});

create3DPayment(options)

Creates a 3D Secure payment. Redirects the user to the bank's verification page.

const result = await payment.create3DPayment({
  ...sameOptionsAsCreatePayment,
  callbackUrl: "https://yoursite.com/payment/callback",
});

// result.redirectUrl — redirect the user here
After the user completes 3D verification, your callbackUrl will receive a POST request with the payment result.

createCheckoutForm(options)

Creates a hosted checkout form (iframe or redirect). Available for İyzico and PayTR.

const result = await payment.createCheckoutForm({
  ...sameOptionsAsCreatePayment,
  callbackUrl: "https://yoursite.com/payment/callback",
});

// result.checkoutFormContent — inject into your page (İyzico)
// result.redirectUrl — redirect user (PayTR)

createSubscription(options)

Creates a recurring/subscription payment. Available for İyzico.

const result = await payment.use("iyzico").createSubscription({
  pricingPlanReferenceCode: "your-plan-code",
  subscriptionInitialStatus: "ACTIVE",
  buyer: { ... },
  billingAddress: { ... },
  shippingAddress: { ... },
});

inquireInstallments(options)

Retrieves available installment options for a card BIN number.

const result = await payment.inquireInstallments({
  binNumber: "454671", // first 6 digits of the card
  price: "500.00",
  currency: "TRY",
});

// result.installmentDetails — array of available installment plans

Guides

3D Secure Payments

3D Secure adds an extra layer of authentication. The flow is:

  1. Call create3DPayment() with a callbackUrl
  2. Redirect the user to result.redirectUrl
  3. The bank authenticates the user and POSTs the result to your callback URL
  4. Verify the result and complete the order
app/api/payment/3d/route.ts
export async function POST(req: Request) {
  const body = await req.json();

  const result = await payment.create3DPayment({
    ...body,
    callbackUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/api/payment/callback`,
  });

  return Response.json({ redirectUrl: result.redirectUrl });
}

// app/api/payment/callback/route.ts
export async function POST(req: Request) {
  const formData = await req.formData();
  const status = formData.get("status");

  if (status === "success") {
    // Update order in database
  }

  return Response.redirect("/payment/result");
}

Subscription Billing

Use İyzico's subscription API for recurring charges.

// 1. First create a subscription plan in İyzico dashboard
// 2. Then subscribe a customer to that plan

const result = await payment.use("iyzico").createSubscription({
  pricingPlanReferenceCode: "MONTHLY_PREMIUM",
  subscriptionInitialStatus: "ACTIVE",
  buyer: {
    gsmNumber: "+905350000000",
    name: "John",
    surname: "Doe",
    identityNumber: "11111111111",
    email: "john@example.com",
    id: "user-123",
  },
  billingAddress: { ... },
  shippingAddress: { ... },
  customer: {
    name: "John",
    surname: "Doe",
    email: "john@example.com",
    gsmNumber: "+905350000000",
    identityNumber: "11111111111",
    billingAddress: { ... },
    shippingAddress: { ... },
  },
});

EFT / IBAN Transfer

PayTR supports EFT and IBAN-based transfers (PWI — Payment With IBAN).

const result = await payment.use("paytr").createPayment({
  paymentMethod: "eft",
  price: "500.00",
  currency: "TRY",
  buyer: { ... },
  basketItems: [ ... ],
  billingAddress: { ... },
});

Error Handling

All methods return a result object with a status field. Wrap calls in try/catch for unexpected errors.

try {
  const result = await payment.createPayment({ ... });

  if (result.status === "success") {
    // Payment succeeded
    console.log("Payment ID:", result.paymentId);
  } else {
    // Payment failed — check error message
    console.error("Payment failed:", result.errorMessage);
  }
} catch (error) {
  // Network error, invalid configuration, etc.
  console.error("Unexpected error:", error);
}
Always check result.status — a resolved promise does not necessarily mean a successful payment.