مزودو المدفوعات
يوفر FORGE تجريداً موحداً للمدفوعات لمعالجة المدفوعات عبر الإنترنت وإدارة جلسات الدفع ومعالجة الاستردادات وتتبع تاريخ المعاملات. يدعم نظام المدفوعات مزودي الدفع الإقليميين والعالميين من خلال واجهة سمة مشتركة.
سمة PaymentProvider
جميع drivers المدفوعات تُنفّذ سمة PaymentProvider:
#[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>;
}أنواع الطلبات والاستجابات
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
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_EMAIL | boolean | true | false | طلب البريد الإلكتروني للعميل |
PAYMENT_REQUIRE_NAME | boolean | false | false | طلب اسم العميل |
PAYMENT_REQUIRE_PHONE | boolean | false | true | طلب رقم هاتف العميل |
افتراضي منطقة MENA
عند تشغيل forge provider:add payment hyperpay أو forge provider:add payment checkout، يتم تعيين الإعدادات الافتراضية تلقائياً لتطبيقات MENA (الهاتف مطلوب، البريد الإلكتروني اختياري). Stripe يستخدم الأنماط الغربية افتراضياً (البريد الإلكتروني مطلوب).
أمثلة على الإعدادات:
# 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_ENABLED | boolean | false | تمكين زر Apple Pay |
{PROVIDER}_GOOGLE_PAY_ENABLED | boolean | false | تمكين زر Google Pay |
{PROVIDER}_CARD_PAYMENTS_ENABLED | boolean | true | تمكين نموذج الدفع بالبطاقة |
HYPERPAY_MADA_ENABLED | boolean | false | تمكين بطاقات مدى (HyperPay) |
CHECKOUT_MADA_ENABLED | boolean | false | تمكين بطاقات مدى (Checkout.com) |
HYPERPAY_SAMSUNG_PAY_ENABLED | boolean | false | تمكين Samsung Pay (HyperPay فقط) |
استبدل {PROVIDER} بـ STRIPE أو CHECKOUT أو HYPERPAY.
مصفوفة دعم المزودين
| طريقة الدفع | Stripe | Checkout.com | HyperPay |
|---|---|---|---|
| الدفع بالبطاقة | ✅ | ✅ | ✅ |
| Apple Pay | ✅ | ✅ | ✅ |
| Google Pay | ✅ | ✅ | ✅ (Fast Checkout) |
| Samsung Pay | ⚠️ KRW فقط | ❌ | ✅ (Mobile SDK) |
| مدى | ❌ | ✅ | ✅ |
الـ Drivers المتاحة
Stripe
منصة دفع عالمية مع Apple Pay و Google Pay ودعم شامل للبطاقات. معالج الدفع الأكثر استخداماً لتطبيقات SaaS والتجارة الإلكترونية.
التثبيت:
forge provider:add payment stripeالإعدادات:
| الإعداد | النوع | الوصف |
|---|---|---|
STRIPE_PUBLIC_KEY | string | Stripe publishable key (pk_test_...) |
STRIPE_SECRET_KEY | encrypted | Stripe secret key (sk_test_...) |
STRIPE_WEBHOOK_SECRET | encrypted | Webhook signing secret (whsec_...) |
STRIPE_MODE | string | "test" أو "live" |
STRIPE_APPLE_PAY_ENABLED | boolean | تمكين Apple Pay |
STRIPE_APPLE_PAY_MERCHANT_ID | string | Apple Pay Merchant ID |
STRIPE_GOOGLE_PAY_ENABLED | boolean | تمكين Google Pay |
STRIPE_CARD_PAYMENTS_ENABLED | boolean | تمكين الدفع بالبطاقات |
الميزات:
- Apple Pay و Google Pay -- دعم محفظة الدفع الأصلي للموبايل والويب
- الدفع العالمي بالبطاقات -- Visa و MasterCard و Amex و135+ عملة
- 3D Secure -- توافق SCA/PSD2 مدمج مع مصادقة تلقائية
- Payment Intents -- مسار دفع حديث مع معالجة تأكيد تلقائية
- الاستردادات -- استردادات كاملة وجزئية مع تعديلات رصيد تلقائية
- Webhooks -- إشعارات أحداث في الوقت الفعلي مع التحقق من التوقيع
HyperPay
بوابة دفع مستخدمة على نطاق واسع في منطقة الشرق الأوسط وشمال أفريقيا. تدعم طرق الدفع المحلية بما في ذلك بطاقات مدى و STC Pay و Apple Pay إلى جانب شبكات البطاقات الدولية.
التثبيت:
forge provider:add payment hyperpayالإعدادات:
| الإعداد | النوع | الوصف |
|---|---|---|
HYPERPAY_ENTITY_ID | string | HyperPay Entity ID لجلسة الدفع |
HYPERPAY_ACCESS_TOKEN | encrypted | HyperPay Access Token |
HYPERPAY_MODE | string | "test" أو "live" |
HYPERPAY_APPLE_PAY_ENABLED | boolean | تمكين Apple Pay |
HYPERPAY_GOOGLE_PAY_ENABLED | boolean | تمكين Google Pay عبر Fast Checkout |
HYPERPAY_MADA_ENABLED | boolean | تمكين بطاقات مدى |
HYPERPAY_MADA_ENTITY_ID | string | Entity ID منفصل لمعاملات مدى |
HYPERPAY_CARD_PAYMENTS_ENABLED | boolean | تمكين الدفع بالبطاقات |
HYPERPAY_SAMSUNG_PAY_ENABLED | boolean | تمكين 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.
التثبيت:
forge provider:add payment checkoutالإعدادات:
| الإعداد | النوع | الوصف |
|---|---|---|
CHECKOUT_PUBLIC_KEY | string | Checkout.com Public Key (للواجهة الأمامية) |
CHECKOUT_SECRET_KEY | encrypted | Checkout.com Secret Key |
CHECKOUT_MODE | string | "sandbox" أو "production" |
CHECKOUT_APPLE_PAY_ENABLED | boolean | تمكين Apple Pay |
CHECKOUT_APPLE_PAY_MERCHANT_ID | string | Apple Pay Merchant ID |
CHECKOUT_GOOGLE_PAY_ENABLED | boolean | تمكين Google Pay |
CHECKOUT_MADA_ENABLED | boolean | تمكين بطاقات مدى |
CHECKOUT_CARD_PAYMENTS_ENABLED | boolean | تمكين الدفع بالبطاقات |
الميزات:
- Apple Pay و Google Pay -- دعم محفظة الدفع الأصلي عبر Payment Request API
- الدفع العالمي بالبطاقات -- Visa و MasterCard و Amex و Discover وشبكات البطاقات الإقليمية
- دعم مدى -- شبكة بطاقات الخصم المحلية في المملكة العربية السعودية
- 3D Secure -- مصادقة 3DS2 مدمجة للتوافق التنظيمي (PSD2، SCA)
- الترميز -- تخزين آمن للبطاقات للدفعات بنقرة واحدة والاشتراكات
- الدفعات المتكررة -- إعداد فوترة الاشتراكات مع الخصم التلقائي من البطاقات
- الاستردادات -- استردادات كاملة وجزئية مع تحديثات حالة webhook
- Webhooks -- إشعارات شاملة لجميع أحداث دورة حياة الدفع
تحذير
تحقق دائماً من حالة الدفع من جانب الخادم باستخدام get_status() بعد عودة العميل إلى success_url. لا تثق أبداً بإعادة التوجيه من جانب العميل وحدها كتأكيد للدفع.
الاستخدام
الحصول على إعدادات الدفع
قبل عرض نموذج الدفع، اجلب الإعدادات لمعرفة حقول العميل المطلوبة:
// الواجهة الأمامية: جلب إعدادات الدفع
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 الصحيح بناءً على إعداداتك:
use crate::services::payments::PaymentFactory;
let payments = PaymentFactory::create(&settings).await?;إنشاء جلسة دفع
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:
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);
}
}معالجة استرداد
// 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 تلقائياً عند تثبيت مزود دفع:
// 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 ] │
└──────────────────────────────────────────────────┘معالجة الأخطاء
جميع عمليات المدفوعات تُرجع أنواع أخطاء منظمة:
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 أكثر من مرة أو إذا قام العميل بتحديث صفحة النجاح.