Skip to content

مزودو المدفوعات

يوفر FORGE تجريداً موحداً للمدفوعات لمعالجة المدفوعات عبر الإنترنت وإدارة جلسات الدفع ومعالجة الاستردادات وتتبع تاريخ المعاملات. يدعم نظام المدفوعات مزودي الدفع الإقليميين والعالميين من خلال واجهة سمة مشتركة.

سمة PaymentProvider

جميع drivers المدفوعات تُنفّذ سمة PaymentProvider:

rust
#[async_trait]
pub trait PaymentProvider: Send + Sync {
    /// Create a new checkout session and return payment URL or form data
    async fn create_checkout(
        &self,
        request: CreateCheckoutRequest,
    ) -> Result<CheckoutResponse, PaymentError>;

    /// Get current status of a payment by its provider reference
    async fn get_status(
        &self,
        payment_id: &str,
    ) -> Result<PaymentStatus, PaymentError>;

    /// Issue a full or partial refund for a completed payment
    async fn refund(
        &self,
        payment_id: &str,
        amount: Option<f64>,
    ) -> Result<RefundResult, PaymentError>;

    /// List transactions with optional filters
    async fn list_transactions(
        &self,
        filters: TransactionFilters,
    ) -> Result<Vec<Transaction>, PaymentError>;
}

أنواع الطلبات والاستجابات

rust
pub struct CreateCheckoutRequest {
    /// Amount in smallest currency unit (e.g., cents, halalas)
    pub amount: u64,
    /// ISO 4217 currency code (e.g., "SAR", "USD", "AED")
    pub currency: String,
    /// Human-readable description shown on checkout page
    pub description: String,
    /// Your internal reference for this order
    pub reference_id: String,
    /// Redirect URL for customer after successful payment
    pub success_url: String,
    /// Redirect URL for customer after cancellation
    pub cancel_url: String,
    /// Optional customer metadata
    pub customer: Option<CustomerInfo>,
}

pub struct CustomerInfo {
    pub email: Option<String>,
    pub name: Option<String>,
    pub phone: Option<String>,
}

pub struct CheckoutResponse {
    /// Provider's unique ID for this checkout session
    pub checkout_id: String,
    /// Redirect URL for customer to complete payment
    pub payment_url: String,
    /// Current status of the checkout session
    pub status: PaymentStatus,
}

تعداد PaymentStatus

rust
pub enum PaymentStatus {
    /// Checkout session created, awaiting customer action
    Pending,
    /// Payment is being processed by provider
    Processing,
    /// Payment completed successfully
    Succeeded,
    /// Payment failed (card declined, insufficient funds, etc.)
    Failed(String),
    /// Payment was cancelled by customer
    Cancelled,
    /// Payment was refunded (fully or partially)
    Refunded,
    /// Payment expired before completion
    Expired,
}

إعدادات حقول العميل

يتيح لك FORGE إعداد حقول العميل المطلوبة أثناء الدفع. هذا مفيد بشكل خاص لتطبيقات منطقة الشرق الأوسط وشمال أفريقيا حيث أرقام الهاتف هي المعرّف الأساسي للعميل بدلاً من البريد الإلكتروني.

الإعدادالنوعافتراضي Stripeافتراضي MENAالوصف
PAYMENT_REQUIRE_EMAILbooleantruefalseطلب البريد الإلكتروني للعميل
PAYMENT_REQUIRE_NAMEbooleanfalsefalseطلب اسم العميل
PAYMENT_REQUIRE_PHONEbooleanfalsetrueطلب رقم هاتف العميل

افتراضي منطقة MENA

عند تشغيل forge provider:add payment hyperpay أو forge provider:add payment checkout، يتم تعيين الإعدادات الافتراضية تلقائياً لتطبيقات MENA (الهاتف مطلوب، البريد الإلكتروني اختياري). Stripe يستخدم الأنماط الغربية افتراضياً (البريد الإلكتروني مطلوب).

أمثلة على الإعدادات:

bash
# Phone only (MENA apps) - default for HyperPay and Checkout.com
PAYMENT_REQUIRE_EMAIL=false
PAYMENT_REQUIRE_NAME=false
PAYMENT_REQUIRE_PHONE=true

# Email only (Western apps) - default for Stripe
PAYMENT_REQUIRE_EMAIL=true
PAYMENT_REQUIRE_NAME=false
PAYMENT_REQUIRE_PHONE=false

# Full customer info
PAYMENT_REQUIRE_EMAIL=true
PAYMENT_REQUIRE_NAME=true
PAYMENT_REQUIRE_PHONE=true

إعدادات طرق الدفع

كل مزود يدعم تمكين/تعطيل طرق دفع محددة:

الإعدادالنوعالافتراضيالوصف
{PROVIDER}_APPLE_PAY_ENABLEDbooleanfalseتمكين زر Apple Pay
{PROVIDER}_GOOGLE_PAY_ENABLEDbooleanfalseتمكين زر Google Pay
{PROVIDER}_CARD_PAYMENTS_ENABLEDbooleantrueتمكين نموذج الدفع بالبطاقة
HYPERPAY_MADA_ENABLEDbooleanfalseتمكين بطاقات مدى (HyperPay)
CHECKOUT_MADA_ENABLEDbooleanfalseتمكين بطاقات مدى (Checkout.com)
HYPERPAY_SAMSUNG_PAY_ENABLEDbooleanfalseتمكين Samsung Pay (HyperPay فقط)

استبدل {PROVIDER} بـ STRIPE أو CHECKOUT أو HYPERPAY.

مصفوفة دعم المزودين

طريقة الدفعStripeCheckout.comHyperPay
الدفع بالبطاقة
Apple Pay
Google Pay✅ (Fast Checkout)
Samsung Pay⚠️ KRW فقط✅ (Mobile SDK)
مدى

الـ Drivers المتاحة

Stripe

منصة دفع عالمية مع Apple Pay و Google Pay ودعم شامل للبطاقات. معالج الدفع الأكثر استخداماً لتطبيقات SaaS والتجارة الإلكترونية.

التثبيت:

bash
forge provider:add payment stripe

الإعدادات:

الإعدادالنوعالوصف
STRIPE_PUBLIC_KEYstringStripe publishable key (pk_test_...)
STRIPE_SECRET_KEYencryptedStripe secret key (sk_test_...)
STRIPE_WEBHOOK_SECRETencryptedWebhook signing secret (whsec_...)
STRIPE_MODEstring"test" أو "live"
STRIPE_APPLE_PAY_ENABLEDbooleanتمكين Apple Pay
STRIPE_APPLE_PAY_MERCHANT_IDstringApple Pay Merchant ID
STRIPE_GOOGLE_PAY_ENABLEDbooleanتمكين Google Pay
STRIPE_CARD_PAYMENTS_ENABLEDbooleanتمكين الدفع بالبطاقات

الميزات:

  • Apple Pay و Google Pay -- دعم محفظة الدفع الأصلي للموبايل والويب
  • الدفع العالمي بالبطاقات -- Visa و MasterCard و Amex و135+ عملة
  • 3D Secure -- توافق SCA/PSD2 مدمج مع مصادقة تلقائية
  • Payment Intents -- مسار دفع حديث مع معالجة تأكيد تلقائية
  • الاستردادات -- استردادات كاملة وجزئية مع تعديلات رصيد تلقائية
  • Webhooks -- إشعارات أحداث في الوقت الفعلي مع التحقق من التوقيع

HyperPay

بوابة دفع مستخدمة على نطاق واسع في منطقة الشرق الأوسط وشمال أفريقيا. تدعم طرق الدفع المحلية بما في ذلك بطاقات مدى و STC Pay و Apple Pay إلى جانب شبكات البطاقات الدولية.

التثبيت:

bash
forge provider:add payment hyperpay

الإعدادات:

الإعدادالنوعالوصف
HYPERPAY_ENTITY_IDstringHyperPay Entity ID لجلسة الدفع
HYPERPAY_ACCESS_TOKENencryptedHyperPay Access Token
HYPERPAY_MODEstring"test" أو "live"
HYPERPAY_APPLE_PAY_ENABLEDbooleanتمكين Apple Pay
HYPERPAY_GOOGLE_PAY_ENABLEDbooleanتمكين Google Pay عبر Fast Checkout
HYPERPAY_MADA_ENABLEDbooleanتمكين بطاقات مدى
HYPERPAY_MADA_ENTITY_IDstringEntity ID منفصل لمعاملات مدى
HYPERPAY_CARD_PAYMENTS_ENABLEDbooleanتمكين الدفع بالبطاقات
HYPERPAY_SAMSUNG_PAY_ENABLEDbooleanتمكين Samsung Pay

الميزات:

  • الدفع بالبطاقات -- Visa و MasterCard ومدى (شبكة البطاقات السعودية)
  • Apple Pay -- تكامل Apple Pay الأصلي لمستخدمي iOS و Safari
  • Google Pay -- Google Pay عبر أداة Fast Checkout الخاصة بـ HyperPay
  • Samsung Pay -- Samsung Pay عبر OPPWA mobile SDK (Android فقط)
  • دعم مدى -- شبكة بطاقات الخصم المحلية في المملكة العربية السعودية
  • الترميز -- تخزين رموز البطاقات للعملاء العائدين بدون التعامل مع بيانات البطاقة الخام
  • الاستردادات -- استردادات كاملة وجزئية عبر API
  • Webhooks -- استقبال إشعارات حالة الدفع غير المتزامنة

افتراضي MENA

HyperPay يستخدم تعريف العميل القائم على الهاتف افتراضياً (PAYMENT_REQUIRE_PHONE=true). هذا يُطابق النمط الشائع في المملكة العربية السعودية ودول الخليج حيث أرقام الهاتف المحمول هي المعرّف الأساسي.

Checkout.com

منصة دفع عالمية تدعم 150+ عملة ومجموعة واسعة من طرق الدفع. مناسبة للشركات الدولية والمعالجة عالية الحجم، مع حضور قوي في منطقة MENA.

التثبيت:

bash
forge provider:add payment checkout

الإعدادات:

الإعدادالنوعالوصف
CHECKOUT_PUBLIC_KEYstringCheckout.com Public Key (للواجهة الأمامية)
CHECKOUT_SECRET_KEYencryptedCheckout.com Secret Key
CHECKOUT_MODEstring"sandbox" أو "production"
CHECKOUT_APPLE_PAY_ENABLEDbooleanتمكين Apple Pay
CHECKOUT_APPLE_PAY_MERCHANT_IDstringApple Pay Merchant ID
CHECKOUT_GOOGLE_PAY_ENABLEDbooleanتمكين Google Pay
CHECKOUT_MADA_ENABLEDbooleanتمكين بطاقات مدى
CHECKOUT_CARD_PAYMENTS_ENABLEDbooleanتمكين الدفع بالبطاقات

الميزات:

  • Apple Pay و Google Pay -- دعم محفظة الدفع الأصلي عبر Payment Request API
  • الدفع العالمي بالبطاقات -- Visa و MasterCard و Amex و Discover وشبكات البطاقات الإقليمية
  • دعم مدى -- شبكة بطاقات الخصم المحلية في المملكة العربية السعودية
  • 3D Secure -- مصادقة 3DS2 مدمجة للتوافق التنظيمي (PSD2، SCA)
  • الترميز -- تخزين آمن للبطاقات للدفعات بنقرة واحدة والاشتراكات
  • الدفعات المتكررة -- إعداد فوترة الاشتراكات مع الخصم التلقائي من البطاقات
  • الاستردادات -- استردادات كاملة وجزئية مع تحديثات حالة webhook
  • Webhooks -- إشعارات شاملة لجميع أحداث دورة حياة الدفع

تحذير

تحقق دائماً من حالة الدفع من جانب الخادم باستخدام get_status() بعد عودة العميل إلى success_url. لا تثق أبداً بإعادة التوجيه من جانب العميل وحدها كتأكيد للدفع.

الاستخدام

الحصول على إعدادات الدفع

قبل عرض نموذج الدفع، اجلب الإعدادات لمعرفة حقول العميل المطلوبة:

typescript
// الواجهة الأمامية: جلب إعدادات الدفع
const config = await api.get('/payments/config');

// يُرجع:
{
  "provider": "checkout",
  "payment_methods": {
    "apple_pay_enabled": true,
    "google_pay_enabled": true,
    "card_payments_enabled": true
  },
  "customer_fields": {
    "require_email": false,
    "require_name": false,
    "require_phone": true
  }
}

إنشاء المزود

يُنشئ المصنع الـ driver الصحيح بناءً على إعداداتك:

rust
use crate::services::payments::PaymentFactory;

let payments = PaymentFactory::create(&settings).await?;

إنشاء جلسة دفع

rust
let checkout = payments.create_checkout(CreateCheckoutRequest {
    amount: 9999,         // 99.99 in smallest currency unit (cents/halalas)
    currency: "SAR".to_string(),
    description: "الخطة المميزة - اشتراك سنوي".to_string(),
    reference_id: "order-abc-123".to_string(),
    success_url: "https://myapp.com/payment/success".to_string(),
    cancel_url: "https://myapp.com/payment/cancel".to_string(),
    customer: Some(CustomerInfo {
        email: Some("customer@example.com".to_string()),
        name: Some("أحمد علي".to_string()),
        phone: Some("+966501234567".to_string()),
    }),
}).await?;

// Redirect customer to payment page
println!("Redirecting to: {}", checkout.payment_url);
println!("Checkout session ID: {}", checkout.checkout_id);

التحقق من حالة الدفع

بعد إكمال العميل للدفع وعودته إلى success_url:

rust
let status = payments.get_status(&checkout_id).await?;

match status {
    PaymentStatus::Succeeded => {
        // Activate subscription, fulfill order, etc.
        println!("Payment confirmed!");
    }
    PaymentStatus::Pending | PaymentStatus::Processing => {
        // Payment still in progress -- wait for webhook
        println!("Payment processing");
    }
    PaymentStatus::Failed(reason) => {
        println!("Payment failed: {}", reason);
    }
    _ => {
        println!("Unexpected status: {:?}", status);
    }
}

معالجة استرداد

rust
// Full refund
let refund = payments.refund("payment-id-123", None).await?;
println!("Refund {}: {:?}", refund.refund_id, refund.status);

// Partial refund (refund 25.00 from 99.99)
let partial = payments.refund("payment-id-123", Some(2500.0)).await?;
println!("Partial refund: {} units", partial.amount);

معالجة Webhook

كلا المزودين يرسلان إشعارات webhook لأحداث الدفع غير المتزامنة. يُولّد FORGE نقطة نهاية webhook تلقائياً عند تثبيت مزود دفع:

rust
// Auto-generated at POST /api/webhooks/payments
pub async fn payment_webhook(
    req: HttpRequest,
    body: web::Json<serde_json::Value>,
    payments: web::Data<Box<dyn PaymentProvider>>,
) -> Result<HttpResponse, AppError> {
    // 1. Verify webhook signature (provider-specific)
    // 2. Extract payment ID and event type
    // 3. Update order status in database
    // 4. Return 200 OK to acknowledge receipt

    Ok(HttpResponse::Ok().finish())
}

تحذير

تحقق دائماً من توقيعات webhook لمنع تأكيدات الدفع المُزيّفة. كل مزود يستخدم آلية توقيع مختلفة -- راجع توثيق المزود للتفاصيل.

الإعدادات عبر الإدارة

تُدار إعدادات المدفوعات من خلال صفحة الإعدادات في الإدارة تحت مجموعة المدفوعات:

الإعدادات > المدفوعات
┌──────────────────────────────────────────────────┐
│  المزود:          [HyperPay          v]         │
│  Entity ID:       [abc123def456        ]        │
│  Access Token:    [................    ]        │
│  الوضع:           [Test             v]          │
│  العملة:          [SAR                ]         │
└──────────────────────────────────────────────────┘

معالجة الأخطاء

جميع عمليات المدفوعات تُرجع أنواع أخطاء منظمة:

rust
match payments.create_checkout(request).await {
    Ok(checkout) => redirect_to(checkout.payment_url),
    Err(PaymentError::InvalidAmount) => println!("المبلغ يجب أن يكون موجباً"),
    Err(PaymentError::UnsupportedCurrency(c)) => println!("العملة {} غير مدعومة", c),
    Err(PaymentError::ProviderError(msg)) => println!("خطأ المزود: {}", msg),
    Err(PaymentError::NetworkError(msg)) => println!("خطأ الشبكة: {}", msg),
    Err(e) => println!("خطأ غير متوقع: {}", e),
}

نصيحة

لنشر الإنتاج، نفّذ معالجة طلبات متطابقة. استخدم حقل reference_id لمنع الرسوم المكررة إذا تم تسليم webhook أكثر من مرة أو إذا قام العميل بتحديث صفحة النجاح.

Released under the MIT License.