- Node.js
- PayTR
- Express
Node.js ile PayTR iFrame API entegrasyonu
PayTR iFrame API'yi Node.js'te kullanmak: token oluşturma, ödeme sayfasını gösterme, Bildirim URL'ini doğrulama ve OK yanıtı.
Yazar:Furkan Özay
1 dk okuma
PayTR iFrame API'de kart bilgisi sizin sunucunuza hiç gelmez: müşteri kartını PayTR'nin sayfasında girer. Bu iyi bir şey, ama entegrasyonun iki noktası sık sık yanlış yapılır:
- Token imzası: Her istek
base64(HMAC-SHA256(veri + merchant_salt, merchant_key))ile imzalanır ve alanların sırası önemlidir. - Sonuç nereden gelir: Müşterinin döndüğü
merchant_ok_urlödemenin sonucunu taşımaz. Sonuç, PayTR panelinde tanımladığınız Bildirim URL'ine sunucudan sunucuya gelir. Bu bildirimin hash'i doğrulanmalı ve yanıt olarak düz metinOKdönülmelidir.
Bu yazıda ikisini de Better Payment ile Express üzerinde kuruyoruz. Next.js kullanıyorsanız aynı yapı Next.js adaptörüyle de çalışır.
1. Kurulum
npm install better-payment expressPAYTR_MERCHANT_ID=...
PAYTR_MERCHANT_KEY=...
PAYTR_MERCHANT_SALT=...
APP_URL=https://magazaniz.com2. Ödeme nesnesi
// payment.ts
import { betterPayment, paytr } from 'better-payment';
export const payment = betterPayment({
mode: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox', // sandbox: test_mode=1
providers: {
paytr: paytr({
merchantId: process.env.PAYTR_MERCHANT_ID!,
merchantKey: process.env.PAYTR_MERCHANT_KEY!,
merchantSalt: process.env.PAYTR_MERCHANT_SALT!,
}),
},
handler: {
// Yalnızca hash'i doğrulanmış bildirimler buraya gelir
onCallback: async (result) => {
// result.paymentId === merchant_oid (sipariş numaranız)
await db.orders.updatePayment(result.paymentId, result.status);
},
},
});3. Ödeme sayfasını başlatın
Token'ı sunucuda, siparişinizin tutarıyla oluşturun:
// server.ts
import express from 'express';
import { toExpressHandler } from 'better-payment/express';
import { payment } from './payment';
const app = express();
// PayTR bildirimleri: POST /api/pay/paytr/callback
app.all('/api/pay/*path', toExpressHandler(payment)); // Express 4: '/api/pay/*'
app.post('/checkout/:orderId', async (req, res) => {
const order = await db.orders.find(req.params.orderId);
const init = await payment.paytr.initThreeDSPayment({
price: order.total,
paidPrice: order.total,
currency: 'TRY',
basketId: order.id,
conversationId: order.id, // merchant_oid: yalnızca harf ve rakam
callbackUrl: `${process.env.APP_URL}/orders/${order.id}`, // müşterinin döneceği sayfa
installment: undefined, // undefined: tüm taksitler, 1: tek çekim, n: en fazla n taksit
buyer: { ...order.buyer, ip: req.ip! },
shippingAddress: order.shippingAddress,
billingAddress: order.billingAddress,
basketItems: order.items,
});
if (init.status !== 'pending') {
return res.status(422).send(init.errorMessage);
}
// PayTR iframe'ini içeren hazır sayfa (ya da init.redirectUrl'e yönlendirin)
res.send(init.threeDSHtmlContent);
});
app.listen(3000);Notlar:
payment_amountkuruş cinsinden gönderilir;paidPricedeğerini TL olarak vermeniz yeterli.- Sepet ürünleri
user_basketalanına TL fiyatlarıyla yazılır. buyer.gsmNumbervebillingAddress.addressPayTR'ye gönderilir, boş bırakmayın.
4. Bildirim URL'i
PayTR panelinde Bildirim URL'ini https://magazaniz.com/api/pay/paytr/callback olarak ayarlayın. Handler bu adrese gelen her bildirimde şunları yapar:
hashalanını sizinmerchant_keyvemerchant_saltdeğerlerinizle doğrular. Hash tutmazsa sonuçINVALID_HASHolur ve sipariş güncellenmez.- Sonucu
successveyafailureolarakonCallback'e verir. - PayTR'ye düz metin
OKdöner. Bu yanıt gelmezse PayTR bildirimi tekrar tekrar gönderir.
PayTR aynı bildirimi birden fazla kez gönderebilir. Sipariş güncellemenizi idempotent yazın: zaten ödenmiş bir siparişi tekrar işlemeyin.
Handler kullanmak istemiyorsanız aynı doğrulamayı elle de çağırabilirsiniz:
app.post('/paytr/bildirim', express.urlencoded({ extended: false }), async (req, res) => {
const result = await payment.paytr.completeThreeDSPayment(req.body);
if (result.errorCode === 'INVALID_HASH') return res.status(400).send('bad hash');
await db.orders.updatePayment(result.paymentId, result.status);
res.type('text/plain').send('OK');
});5. İade, iptal ve durum sorgusu
await payment.paytr.refund({ paymentId: 'ORDER123', price: '50.00', currency: 'TRY', ip: '1.2.3.4' });
// PayTR'de void yoktur; cancel() tam iade yapar
await payment.paytr.cancel({ paymentId: 'ORDER123', ip: '1.2.3.4' });
const status = await payment.paytr.getPayment('ORDER123');Bu işlemleri HTTP üzerinden açacaksanız handler'da allowedActions ve bir authorize hook'u tanımlayın; aksi halde iade gibi işlemler hiç açılmaz. Bkz. HTTP handler.
Sonraki adımlar
- Tüm seçenekler için PayTR sayfası.
- Test kartları ve sandbox ayarları için test kartları.
- Aynı uygulamaya iyzico ya da bir banka sanal POS'u eklemek için Next.js ile iyzico 3D Secure yazısına bakın; istek yapısı aynıdır.
Okumaya devam edin
- Next.js
- iyzico
- 3D Secure
Next.js ile iyzico 3D Secure entegrasyonu: adım adım rehber
Next.js App Router'da iyzico ile 3D Secure ödeme almak: kurulum, sunucuda istek oluşturma, callback doğrulama ve siparişi güncelleme.
- Node.js
- iyzico
- Geçiş
iyzipay paketinden Better Payment'a geçiş
Resmi iyzipay Node.js paketini kullanıyorsanız Better Payment'a nasıl geçersiniz: kod karşılaştırması, callback doğrulama ve adım adım geçiş listesi.