تخطى إلى المحتوى الرئيسي
دليل عملي

كيفية بناء نظام دفع USDT آلي داخل Telegram Bot باستخدام Go

فريق جليتش نيوز
14 يونيو1 مشاهدة5 دقائق
كيفية بناء نظام دفع USDT آلي داخل Telegram Bot باستخدام Go

تعرف على كيفية بناء نظام دفع USDT آلي وغير احتجازي داخل Telegram Bot باستخدام Go، مع إنشاء الفواتير، التحقق من Webhooks، معالجة المدفوعات، وتأمين النظام ضد التكرار والاحتيال.

مقدمة

إذا كنت تبيع اشتراكات رقمية أو ملفات أو خدمات عبر Telegram، فمن المحتمل أنك بدأت بطريقة بسيطة: ترسل عنوان USDT للمستخدم، تنتظر التحويل، ثم تطلب منه إرسال لقطة شاشة أو Transaction Hash للتحقق من الدفع.

قد تنجح هذه الطريقة مع أول عدة عملاء، لكنها تتحول بسرعة إلى عبء تشغيلي كبير مع زيادة عدد الطلبات. ستجد نفسك تقضي وقتاً أطول في التحقق من المعاملات عبر مستكشفات البلوكتشين بدلاً من تطوير المنتج نفسه.

في هذا الدليل العملي سنتعلم كيفية بناء نظام دفع آلي وغير احتجازي (Non-Custodial) يسمح للمستخدم بالدفع باستخدام USDT بينما تذهب الأموال مباشرة إلى محفظتك، ويتولى النظام التحقق من المعاملة وتسليم المنتج تلقائياً.


ما الذي سنبنيه؟

بنهاية هذا الدليل سيكون لدينا النظام التالي:

  1. Telegram Bot يستقبل طلب الشراء.
  2. خدمة Backend مكتوبة بلغة Go.
  3. قاعدة بيانات PostgreSQL لإدارة الطلبات.
  4. مزود دفع Crypto يدعم Webhooks.
  5. نظام تحقق آمن من المدفوعات.
  6. Queue لمعالجة عمليات التسليم.
  7. تسليم تلقائي للمنتج أو الاشتراك.

المتطلبات الأساسية

قبل البدء تأكد من توفر ما يلي:

البرمجيات

  • Go 1.24 أو أحدث
  • PostgreSQL 16+
  • Docker - اختياري
  • Telegram Bot Token

الحسابات

  • حساب Telegram Bot
  • محفظة USDT
  • حساب لدى مزود الدفع
  • VPS أو Cloud Server

المعرفة المطلوبة

  • أساسيات Go
  • REST APIs
  • PostgreSQL
  • Webhooks


مخطط البنية المعمارية

شرح المكونات

A screenshot of a computer

AI-generated content may be incorrect.

Telegram Bot

واجهة المستخدم الرئيسية.

Go Backend

العقل المركزي للنظام.

PostgreSQL

تخزين الطلبات وحالات الدفع.

Payment Provider

إنشاء الفواتير ومراقبة الشبكة.

Fulfillment Queue

ضمان عدم تسليم المنتج أكثر من مرة.


الخطوة الأولى: إنشاء مشروع Go

أنشئ مجلد المشروع:

mkdir telegram-payment-bot

cd telegram-payment-bot


go mod init telegram-payment-bot

قم بتثبيت الحزم الأساسية:

go get github.com/gin-gonic/gin

go get github.com/jackc/pgx/v5

go get github.com/go-telegram-bot-api/telegram-bot-api/v5

هيكل المشروع المقترح:

cmd/

internal/

├── api/

├── bot/

├── payment/

├── webhook/

├── database/

├── worker/

└── models/


الخطوة الثانية: إنشاء جدول الطلبات

سنبدأ بإنشاء جدول الطلبات.

CREATE TABLE orders (

   id UUID PRIMARY KEY,

   user_id BIGINT NOT NULL,

   product_id VARCHAR(50),

   amount_usd NUMERIC(12,2),

   invoice_id VARCHAR(255),

   status VARCHAR(50),

   created_at TIMESTAMP DEFAULT NOW(),

   updated_at TIMESTAMP DEFAULT NOW()

);


لماذا لا نستخدم is_paid؟

خطأ شائع جداً:

is_paid BOOLEAN

هذا الحقل لا يكفي.

في الواقع توجد حالات كثيرة:

  • Awaiting Payment
  • Paid
  • Underpaid
  • Expired
  • Refunded
  • Fulfilled

لذلك سنستخدم State Machine.


آلة الحالات المقترحة

A screenshot of a computer

AI-generated content may be incorrect.

الخطوة الثالثة: إنشاء الطلب محلياً

قبل إنشاء الفاتورة لدى مزود الدفع يجب إنشاء الطلب داخل قاعدة البيانات.

order := Order{

   ID: uuid.New(),

   UserID: telegramUserID,

   AmountUSD: 29.00,

   Status: "awaiting_payment",

}


err := repository.CreateOrder(order)

لماذا؟

إذا فشل الاتصال بمزود الدفع يمكن إعادة المحاولة دون فقدان البيانات.

الخطوة الرابعة: إنشاء فاتورة دفع

بعد إنشاء الطلب محلياً نرسل طلباً إلى مزود الدفع.

type CreateInvoiceRequest struct {

   Amount     float64 `json:"amount"`

   Currency   string `json:"currency"`

   Description string `json:"description"`

}

إرسال الطلب:

resp, err := client.CreateInvoice(

   order.ID,

   order.AmountUSD,

)

بعد النجاح:

order.InvoiceID = resp.InvoiceID


repository.UpdateInvoice(

   order.ID,

   resp.InvoiceID,

)

A screenshot of a qr code

AI-generated content may be incorrect.

الخطوة الخامسة: إرسال رابط الدفع للمستخدم

بعد إنشاء الفاتورة:

msg := tgbotapi.NewMessage(

   chatID,

   fmt.Sprintf(

       "قم بالدفع من خلال الرابط التالي:\n%s",

       invoiceURL,

   ),

)

bot.Send(msg)

الآن ينتظر المستخدم الدفع بينما يراقب مزود الدفع الشبكة.


ماذا يحدث بعد الدفع؟

هنا تأتي أهمية الـ Webhooks.

بمجرد اكتشاف التحويل يقوم مزود الدفع بإرسال إشعار إلى خادمنا يحتوي على:

  • Invoice ID
  • Amount
  • Network
  • Transaction Hash
  • Payment Status

لكن لا يجب الثقة بهذه البيانات مباشرة.

في القسم التالي سنقوم ببناء Webhook Handler آمن والتحقق من HMAC Signature خطوة بخطوة.

الخطوة السادسة: إنشاء Webhook Handler آمن

بعد أن يرسل المستخدم الدفع، سيقوم مزود الدفع بإرسال Webhook إلى نقطة النهاية الخاصة بك.

مثال:

POST /webhooks/payment


Content-Type: application/json

X-Recv-Signature: abc123...

X-Recv-Timestamp: 1759842842

أحد أكبر الأخطاء التي يرتكبها المطورون هو الثقة مباشرة في البيانات القادمة من الـ Webhook.

لا تفعل ذلك أبداً.

يجب التحقق أولاً من:

  • صحة التوقيع Signature
  • صحة الطابع الزمني Timestamp
  • عدم تكرار الحدث Event Replay
  • تطابق الفاتورة مع قاعدة البيانات


إنشاء Endpoint

router.POST(

   "/webhooks/payment",

   webhookHandler,

)


قراءة الطلب الخام

لا تقم بتحليل JSON مباشرة.

body, err := io.ReadAll(c.Request.Body)


if err != nil {

   c.JSON(400, gin.H{

       "error": "invalid request",

   })

   return

}

سنحتاج إلى البيانات الخام للتحقق من التوقيع.


الخطوة السابعة: التحقق من HMAC Signature

الفكرة بسيطة:

يقوم مزود الدفع بإنشاء بصمة رقمية باستخدام:

  • Secret Key
  • Timestamp
  • Raw Request Body

ثم يرسلها في Header.

يجب علينا إعادة حساب البصمة ومقارنتها.



إنشاء دالة التحقق

func VerifySignature(

   secret string,

   timestamp string,

   payload []byte,

   receivedSignature string,

) bool {


   data := timestamp + "." + string(payload)


   mac := hmac.New(

       sha256.New,

       []byte(secret),

   )


   mac.Write([]byte(data))


   expected := hex.EncodeToString(

       mac.Sum(nil),

   )


   return hmac.Equal(

       []byte(expected),

       []byte(receivedSignature),

   )

}


لماذا نستخدم hmac.Equal ؟

خطأ شائع:

if expected == received {

}

هذه المقارنة قد تكشف معلومات للمهاجم من خلال Timing Attacks.

لذلك استخدم:

hmac.Equal()

التي تنفذ مقارنة ثابتة الزمن.


الخطوة الثامنة: منع Replay Attacks

حتى لو كان التوقيع صحيحاً، يمكن للمهاجم إعادة إرسال نفس الطلب.

لذلك يجب التحقق من Timestamp.

timestampInt, _ := strconv.ParseInt(

   timestamp,

   10,

   64,

)


requestTime := time.Unix(

   timestampInt,

   0,

)


if time.Since(requestTime) >

   5*time.Minute {


   return errors.New(

       "expired webhook",

   )

}

أي Webhook أقدم من 5 دقائق يتم رفضه.


الخطوة التاسعة: معالجة الحدث بطريقة Idempotent

في العالم الحقيقي ستتلقى أحياناً نفس Webhook مرتين أو ثلاث مرات.

هذا أمر طبيعي.

السبب أن مزود الدفع يعمل بطريقة:

At-Least-Once Delivery

وليس:

Exactly-Once Delivery


المشكلة

إذا قمت بإرسال المنتج مباشرة:

deliverProduct()

ثم تعطلت الخدمة قبل إعادة:

200 OK

فسيتم إعادة إرسال الـ Webhook.

والنتيجة؟

قد يحصل العميل على المنتج مرتين أو ثلاث مرات.


الحل: Inbox Pattern

أنشئ جدولاً خاصاً بالأحداث.

CREATE TABLE webhook_events (

   event_id VARCHAR(255) PRIMARY KEY,

   event_type VARCHAR(100),

   received_at TIMESTAMP DEFAULT NOW()

);


معالجة الحدث

INSERT INTO webhook_events (

   event_id,

   event_type

)

VALUES (

   $1,

   $2

)

ON CONFLICT DO NOTHING;

إذا كان الحدث موجوداً مسبقاً فلن يتم تنفيذه مرة أخرى.


الخطوة العاشرة: تحديث حالة الطلب

بعد نجاح التحقق:

UPDATE orders

SET status='paid'

WHERE invoice_id=$1;

لكن لا تقم بتسليم المنتج هنا.


الخطوة الحادية عشرة: إنشاء Queue للتنفيذ

أنشئ جدول الوظائف.

CREATE TABLE fulfillment_jobs (

   id UUID PRIMARY KEY,

   order_id UUID,

   status VARCHAR(50),

   created_at TIMESTAMP DEFAULT NOW()

);


إضافة مهمة جديدة

INSERT INTO fulfillment_jobs (

   id,

   order_id,

   status

)

VALUES (

   gen_random_uuid(),

   $1,

   'pending'

);

الـ Webhook انتهى عمله هنا.


لماذا لا نرسل المنتج مباشرة؟

لأن العمليات التالية قد تفشل:

  • Telegram API
  • SMTP
  • Cloud Storage
  • License Generation

لذلك نستخدم Worker منفصل.


الخطوة الثانية عشرة: بناء Worker

عامل التنفيذ يقرأ المهام باستمرار.

for {


   jobs := repository.GetPendingJobs()


   for _, job := range jobs {


       processJob(job)

   }


   time.Sleep(

       5 * time.Second,

   )

}


تنفيذ المهمة

func processJob(

   job FulfillmentJob,

) {


   sendProductToUser(job)


   repository.MarkCompleted(

       job.ID,

   )

}


الخطوة الثالثة عشرة: تسليم المنتج عبر Telegram

مثال إرسال رابط تحميل.

msg := tgbotapi.NewMessage(

   chatID,

   "شكراً لعملية الشراء.\n\n"+

   "رابط التحميل:\n"+

   downloadURL,

)


bot.Send(msg)



التعامل مع الحالات الخاصة

في الواقع ليست كل المدفوعات مثالية.


حالة Underpayment

المستخدم أرسل:

29.00 USDT

بينما المطلوب:

29.004281 USDT

لا توافق تلقائياً.

قم بوضع الطلب في:

payment_review


حالة Overpayment

قد يدفع المستخدم أكثر من المطلوب.

يمكنك:

  • الموافقة التلقائية
  • إضافة رصيد للحساب
  • إنشاء Refund

بحسب سياسة مشروعك.


حالة Wrong Network

مثال:

المطلوب:

TRC20

لكن المستخدم دفع عبر:

BEP20

هذه حالة تحتاج مراجعة يدوية.


حالة Expired Invoice

إذا انتهت صلاحية الفاتورة:

UPDATE orders

SET status='expired'

ثم اطلب من المستخدم إنشاء فاتورة جديدة.


أفضل الممارسات الأمنية

لا تخزن الأسرار داخل الكود

خطأ:

const Secret =

"123456789"

الصحيح:

os.Getenv(

   "WEBHOOK_SECRET",

)


استخدم HTTPS فقط

لا تستقبل Webhooks عبر HTTP.


سجل جميع الأحداث

logger.Info(

   "invoice paid",

   zap.String(

       "invoice",

       invoiceID,

   ),

)

السجلات مهمة جداً عند التحقيق في المشكلات.


راقب معدل الأخطاء

أنشئ Dashboard تتابع:

  • عدد الفواتير
  • المدفوعات الناجحة
  • المدفوعات الفاشلة
  • الأخطاء
  • زمن معالجة الـ Webhooks


مخطط دورة العمل الكاملة

sequenceDiagram


User->>Telegram Bot: /buy


Telegram Bot->>Go Backend:

Create Order


Go Backend->>Payment Provider:

Create Invoice


Payment Provider-->>User:

Checkout URL


User->>Blockchain:

Send USDT


Blockchain->>Payment Provider:

Transaction Confirmed


Payment Provider->>Webhook Endpoint:

invoice.paid


Webhook Endpoint->>Database:

Mark Paid


Webhook Endpoint->>Queue:

Create Job


Worker->>Telegram Bot:

Deliver Product


Telegram Bot-->>User:

Download Link


اختبار النظام

قبل الإطلاق تأكد من اختبار:

إنشاء الفاتورة

✅ يعمل

إعادة إرسال Webhook

✅ لا يكرر التنفيذ

انقطاع قاعدة البيانات

✅ لا يفقد الطلبات

فشل Telegram API

✅ يعيد المحاولة

دفع أقل من المطلوب

✅ ينتقل إلى Review


أخطاء شائعة يجب تجنبها

❌ استخدام is_paid فقط

❌ تسليم المنتج داخل Webhook

❌ عدم التحقق من Signature

❌ عدم استخدام Idempotency

❌ تخزين Secrets داخل Git

❌ الاعتماد على لقطات الشاشة كدليل دفع


الخاتمة

بناء نظام دفع USDT آلي داخل Telegram Bot ليس مجرد ربط مزود دفع مع البوت. النظام الحقيقي يجب أن يكون قادراً على التعامل مع التكرار، والأخطاء، ومحاولات الاحتيال، وانقطاع الخدمات دون فقدان الطلبات أو تسليم المنتجات أكثر من مرة.

باستخدام Go وPostgreSQL وWebhooks الموقعة وFulfillment Queue يمكنك بناء بنية دفع احترافية قابلة للتوسع من عشرات الطلبات يومياً إلى آلاف المعاملات دون الحاجة إلى التحقق اليدوي من كل عملية دفع.

الاستثمار في هذه البنية منذ البداية يوفر ساعات طويلة من أعمال الدعم ويمنح المستخدم تجربة شراء سلسة وآمنة، وهو ما يجعل مشروعك أكثر قابلية للنمو على المدى الطويل.






أعجبك المقال؟ شاركه

النشرة البريدية

كن أول من يعرف بمستقبل التقنية

أهم الأخبار والتحليلات التقنية مباشرة في بريدك.