Randevu sistemi çalışıyor, ancak onay yalnızca ekranda kalıyor. Bu rehber, mevcut sisteme dokunmadan eklenecek dört küçük parçayı, hazır kodları, test listesini ve maliyet araştırmasını içerir.
Berber iki paket için fiyat istiyor. Paket A tamamen ücretsiz servislerle çalışır ve önerilen başlangıçtır.
Tüm bildirimler tek bir noktadan tetiklenir: randevu kaydının başarıyla veritabanına yazıldığı an. Bildirim hataları randevu kaydını asla engellememeli; her gönderim ayrı try/catch içinde çalışır ve yalnızca loglanır.
flowchart LR A[Müşteri formu gönderir] --> B[Randevu kaydedilir] B --> C[Onay ekranı] C --> C1[Takvime ekle butonu] B --> D[notifyBooking] D --> E[Telegram: berbere] D --> F[Brevo: müşteriye e-posta] D --> G[SMS: onay şimdi] D --> H[SMS: hatırlatma, 2 saat önce zamanlı]
En etkili ve ücretsiz adım. Randevu müşterinin kendi takvimine girer; telefon 2 saat ve 30 dakika önce kendisi hatırlatır. iPhone'da, özellikle ana ekrana eklenmiş uygulama modunda, tarayıcıda üretilen dosya indirmeleri güvenilir değildir. Bu yüzden .ics dosyasını sunucudan text/calendar başlığıyla verin; Android için Google Takvim linkini ikinci buton olarak ekleyin.
2026-09-26T15:30:00+03:00 biçiminde, Türkiye saatiyle oluşturun. Aşağıdaki kod UTC'ye çevirerek yazar; takvim uygulaması yerel saate doğru döndürür.// Express örneği. Diğer framework'lerde aynı mantık: metni üret, text/calendar olarak dön.
app.get('/randevu/:id.ics', async (req, res) => {
const r = await getBooking(req.params.id); // { id, service, start: Date, minutes }
if (!r) return res.sendStatus(404);
const f = d => d.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
const end = new Date(r.start.getTime() + r.minutes * 60000);
const alarm = t => ['BEGIN:VALARM', `TRIGGER:${t}`, 'ACTION:DISPLAY',
'DESCRIPTION:Modi randevu hatırlatma', 'END:VALARM'];
const ics = ['BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//Modi//Randevu//TR', 'BEGIN:VEVENT',
`UID:${r.id}@modierkekkuaforu.com`, `DTSTAMP:${f(new Date())}`,
`DTSTART:${f(r.start)}`, `DTEND:${f(end)}`,
`SUMMARY:Modi Erkek Kuaförü – ${r.service}`, 'LOCATION:Modi Erkek Kuaförü',
...alarm('-PT2H'), ...alarm('-PT30M'),
'END:VEVENT', 'END:VCALENDAR'].join('\r\n');
res.set('Content-Type', 'text/calendar; charset=utf-8');
res.set('Content-Disposition', `inline; filename="modi-randevu-${r.id}.ics"`);
res.send(ics);
});<a class="btn" href="/randevu/{{id}}.ics">Takvime ekle (iPhone)</a>
<a class="btn" target="_blank" rel="noopener" href="{{googleUrl}}">Google Takvim'e ekle</a>
// googleUrl üretimi (sunucu veya istemci):
const g = d => d.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
const googleUrl = 'https://calendar.google.com/calendar/render?action=TEMPLATE'
+ '&text=' + encodeURIComponent('Modi Erkek Kuaförü – ' + r.service)
+ '&dates=' + g(r.start) + '/' + g(end)
+ '&location=' + encodeURIComponent('Modi Erkek Kuaförü');Telegram, botların kullanıcılarına varsayılan olarak ücretsiz mesaj gönderebildiğini belirtiyor; yalnızca toplu yayında hız sınırı var. Berberin telefonunda Telegram uygulaması olması yeterli.
/newbot → bot adı verilir, token alınır.https://api.telegram.org/bot<TOKEN>/getUpdates açılır; yanıttaki message.chat.id değeri chat_id'dir. Birden fazla çalışan bildirim alacaksa bir Telegram grubu açılıp bot gruba eklenir; grup chat_id'si negatif bir sayıdır.async function notifyBarber(r) {
const text = `✂️ Yeni randevu\n${r.name} · ${r.phone}\n${r.dateText} ${r.timeText} · ${r.service}`;
const res = await fetch(`https://api.telegram.org/bot${process.env.TG_TOKEN}/sendMessage`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: process.env.TG_CHAT_ID, text })
});
if (!res.ok) console.error('Telegram', res.status, await res.text());
}Brevo'nun ücretsiz planı süresiz, kredi kartı istemiyor ve günde 300 gönderim hakkı veriyor. Bu limit pazarlama ve işlem e-postaları arasında ortaktır; bir berber için fazlasıyla yeterli. Ücretsiz planda e-postalarda Brevo logosu yer alır.
randevu@modierkekkuaforu.com) ve alan adı doğrulanır. Brevo, göndericinin kayıtlı ve doğrulanmış olmasını şart koşuyor; bunun için alan adının DNS'ine Brevo'nun verdiği kayıtlar eklenir.async function emailCustomer(r) {
if (!r.email) return;
const res = await fetch('https://api.brevo.com/v3/smtp/email', {
method: 'POST',
headers: { 'api-key': process.env.BREVO_KEY, 'content-type': 'application/json', accept: 'application/json' },
body: JSON.stringify({
sender: { name: 'Modi Erkek Kuaförü', email: 'randevu@modierkekkuaforu.com' },
to: [{ email: r.email, name: r.name }],
subject: `Randevunuz onaylandı · ${r.dateText} ${r.timeText}`,
htmlContent: `<p>Sayın ${r.name},</p>
<p><b>${r.dateText} ${r.timeText}</b> tarihli <b>${r.service}</b> randevunuz onaylandı.</p>
<p><a href="https://modierkekkuaforu.com/randevu/${r.id}.ics">Takviminize ekleyin</a></p>`
})
});
if (!res.ok) console.error('Brevo', res.status, await res.text());
}Firma adıyla (başlıklı) SMS için BTK onaylı gönderici adı ve lisanslı bir sağlayıcı gerekir; ücretsiz ve güvenilir bir seçenek yoktur. Randevu onayı ve hatırlatması bilgilendirme mesajı sayıldığı için İYS izni gerekmez; mesaja kampanya veya indirim eklenirse ticari ileti olur ve İYS onayı gerekir.
Örnek sağlayıcı olarak iletiMerkezi seçildi, çünkü API'si ileri tarihli gönderimi (sendDateTime) destekliyor. Hatırlatma SMS'i randevu anında sağlayıcıda zamanlanır, sunucuda cron kurmak gerekmez. Başlık onayı gelene kadar testler APITEST göndericisiyle yapılabilir. key ve hash değerleri panelden hazır alınır; sağlayıcı hash'in elle üretilmemesini söylüyor.
// iletiMerkezi: POST https://api.iletimerkezi.com/v1/send-sms/json
// iys: "0" = bilgilendirme (randevu). sendDateTime: "dd/MM/yyyy HH:mm", Türkiye saati.
async function sendSms(number, text, sendDateTime) {
const order = { sender: process.env.SMS_SENDER, iys: '0',
message: { text, receipents: { number: [number] } } }; // alan adı dokümandaki gibi: receipents
if (sendDateTime) order.sendDateTime = sendDateTime;
const res = await fetch('https://api.iletimerkezi.com/v1/send-sms/json', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ request: {
authentication: { key: process.env.SMS_KEY, hash: process.env.SMS_HASH }, order } })
});
return res.json(); // yanıttaki sipariş kimliğini randevuya kaydedin (iptal için)
}
const trTime = d => new Intl.DateTimeFormat('tr-TR', { timeZone: 'Europe/Istanbul',
day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })
.format(d).replace(',', '').replace(/\./g, '/'); // "26/09/2026 13:30"
async function smsCustomer(r) { // r.phone: 905XXXXXXXXX
await sendSms(r.phone, `Modi Erkek Kuaforu: ${r.dateText} ${r.timeText} ${r.service} randevunuz onaylandi.`);
const remindAt = new Date(r.start.getTime() - 2 * 3600 * 1000);
if (remindAt > new Date())
await sendSms(r.phone, `Hatirlatma: Bugun ${r.timeText} Modi Erkek Kuaforu randevunuz var.`, trTime(remindAt));
}Mevcut koda eklenecek tek satır, randevunun kaydedildiği yerdir. Bildirimler müşteriyi bekletmemesi için yanıt döndükten sonra da çalıştırılabilir.
async function notifyBooking(r) {
const jobs = [notifyBarber(r), emailCustomer(r)];
if (process.env.SMS_ENABLED === 'true') jobs.push(smsCustomer(r));
const results = await Promise.allSettled(jobs); // biri hata verirse diğerleri yine çalışır
results.filter(x => x.status === 'rejected').forEach(x => console.error('notify', x.reason));
}
// const booking = await saveBooking(form); ← mevcut kod
// notifyBooking(booking); ← eklenecek| Ortam değişkeni | Nereden | Paket |
|---|---|---|
TG_TOKEN | @BotFather | A |
TG_CHAT_ID | getUpdates yanıtı | A |
BREVO_KEY | Brevo → SMTP & API | A |
SMS_ENABLED | true / false | B |
SMS_KEY, SMS_HASH | iletiMerkezi paneli → güvenlik ayarları | B |
SMS_SENDER | Onaylı başlık (test: APITEST) | B |
Teslimde aşağıdaki maddelerin her biri gerçek bir test randevusuyla gösterilmeli.
| Sağlayıcı | Kaynakta yazan paket | Birim | Not |
|---|---|---|---|
| iletiMerkezi | 500 SMS · 189 TL | 0,378 TL | KDV ve ÖİV dahil, taahhüt yok, 1 yıl geçerli. Yeni abonelikte ilk ödemeye 10.000 SMS 879 TL. API'de ileri tarihli gönderim var. |
| VatanSMS | 2.500 SMS · 499 TL | ≈ 0,20 TL | İlk alıma özel. KDV ve ÖTV dahil, son kullanım yok, ulaşmayan SMS iade. |
| Netgsm | 1.000 SMS · 899 TL'den | ≈ 0,90 TL | Yalnızca ulaşan SMS ücretlendirilir. |
| Senaryo (varsayım: günde 15 randevu) | SMS / ay | ≈ 0,20 TL ile | ≈ 0,38 TL ile |
|---|---|---|---|
| Yalnızca hatırlatma SMS'i (onay takvim + e-postayla) | 450 | 90 TL | 170 TL |
| Onay + hatırlatma | 900 | 180 TL | 340 TL |
Karşılaştırma için pazardaki hazır sistemlere bakıldı. Hepsi SMS'li hatırlatma sunuyor, ancak berberin kendi markalı sitesi ve mevcut akışı yerine platformun sayfasına geçmek gerekir.
| Seçenek | Fiyat | Bildirim | Değerlendirme |
|---|---|---|---|
| Mevcut sistem + bu rehber | 0 TL (A) + SMS kullanımı (B) | Takvim, Telegram, e-posta, SMS | Marka, tasarım ve müşteri verisi berberde kalır. Tek seferlik geliştirme maliyeti. |
| RandevuNet | Ücretsiz; Pro 417 TL/ay | Sitesine göre SMS ve WhatsApp hatırlatma ücretsiz | Ücretsiz sürüm reklamlı; markalama Pro'da. Ücretsiz SMS beyanı sitenin kendi ifadesi. |
| Salon Randevu | Silver 779 TL/ay | 100 hediye SMS/ay, SMS hatırlatma | KDV dahil. WhatsApp ve online randevu sitesi ek 225 TL/ay. |
| Fresha | 19,95 USD/ay | Ayda 100 SMS ücretsiz, sonrası 0,02 USD | Pazar yerinden gelen yeni müşteri için %20 komisyon. |
Hepsi 26 Eylül 2026'da kontrol edildi. Fiyatlar değişebilir; satın almadan önce güncelliğini doğrulayın.