| Install | |
|---|---|
composer require officeguy/laravel-sumit-gateway |
|
| Latest Version: | v3.0.3 |
| PHP: | ^8.2 |
Clone 1:1 של התוסף WooCommerce woo-payment-gateway-officeguy עבור Laravel.
תמיכה רשמית:
Laravel 12
Laravel 13
PHP 8.2+ עבור Laravel 12
PHP 8.3+ עבור Laravel 13
תשלומים בכרטיס אשראי (PCI modes: no/redirect/yes)
תשלומי Bit
תמיכה ב‑Tokens (J2/J5), Authorize Only, תשלומים (עד 36), recurring
מסמכים (חשבונית/קבלה/תרומה), שילוב PayPal/BlueSnap receipts
Multivendor & CartFlows מקבילים (לפי מפרט המקור)
סנכרון מלאי (12/24 שעות/Checkout), ווידג'ט דשבורד (למימוש עתידי)
ממשק ניהול Filament v4
דפי לקוח Filament להצגת טרנזקציות/מסמכים/אמצעי תשלום
httpdocs/vendor/officeguy/laravel-sumit-gateway).resources/css/checkout-mobile.css (מותאם ברנד) לחבילה; במקור לא היה CSS צ'קאאוט.success-card.blade.php הוחלף לגרסה שמבוססת על שדות token גולמיים (card_type, last_four, exp_month/exp_year).composer require officeguy/laravel-sumit-gateway
php artisan migrate # יריץ את כל מיגרציות החבילה
Laravel 13 דורש PHP 8.3 ומעלה. אם האפליקציה שלכם עדיין על PHP 8.2, הישארו עם Laravel 12.
אם תרצה להעתיק גם קונפיג/מיגרציות/תצוגות:
--tag=officeguy-config,--tag=officeguy-migrations,--tag=officeguy-views. ראה קבצים לפרסום לפרטים נוספים.
כל ההגדרות נשמרות במסד הנתונים (טבלת officeguy_settings) עם fallback לקובץ config. ניתן לערוך דרך Filament (עמוד Gateway Settings) או בקוד באמצעות SettingsService.
נווטו ל-SUMIT Gateway > Gateway Settings ב-Admin Panel.
no (PaymentsJS), redirect, yes (PCI server)ניתן להתאים את כל נתיבי החבילה ישירות מה-Admin Panel:
ב-Admin Panel: נווטו ל-SUMIT Gateway > Gateway Settings > Route Configuration
נתיבים ניתנים להתאמה:
| הגדרה | ברירת מחדל | תיאור |
|---|---|---|
| Route Prefix | officeguy |
קידומת לכל הנתיבים |
| Card Callback | callback/card |
חזרה מתשלום בכרטיס |
| Bit Webhook | webhook/bit |
קבלת IPN מ-Bit |
| SUMIT Webhook | webhook/sumit |
קבלת webhooks מ-SUMIT |
| Document Download | documents/{document} |
הורדת מסמכים |
| Checkout Charge | checkout/charge |
חיוב ישיר |
| Public Checkout | checkout/{id} |
עמוד תשלום ציבורי |
| Success Route | checkout.success |
נתיב הצלחה |
| Failed Route | checkout.failed |
נתיב כישלון |
דוגמה - שינוי נתיבים:
paymentsreturn/cardphp artisan route:clearתוצאה:
POST /payments/return/card במקום POST /officeguy/callback/cardPOST /payments/webhook/bit במקום POST /officeguy/webhook/bitאו ב-.env:
OFFICEGUY_ROUTE_PREFIX=payments
OFFICEGUY_CARD_CALLBACK_PATH=return/card
OFFICEGUY_BIT_WEBHOOK_PATH=ipn/bit
OFFICEGUY_SUMIT_WEBHOOK_PATH=triggers/sumit
שימוש בקוד:
use OfficeGuy\LaravelSumitGateway\Support\RouteConfig;
// קבלת כל הנתיבים המוגדרים
$paths = RouteConfig::getAllPaths();
// [
// 'prefix' => 'officeguy',
// 'card_callback' => 'officeguy/callback/card',
// 'bit_webhook' => 'officeguy/webhook/bit',
// 'sumit_webhook' => 'officeguy/webhook/sumit',
// ...
// ]
// קבלת נתיב ספציפי
$cardCallbackPath = RouteConfig::getPrefix() . '/' . RouteConfig::getCardCallbackPath();
עמוד התשלום מספק ממשק מלא ומותאם לגביית תשלומים מלקוחות. ניתן להתאים את התוכן, העיצוב והשדות.
הפעלה:
// ב-Admin Panel
// נווטו ל-SUMIT Gateway > Gateway Settings > Public Checkout Page
// הפעילו את "Enable Public Checkout"
או ב-.env:
OFFICEGUY_ENABLE_PUBLIC_CHECKOUT=true
גישה לעמוד:
GET /officeguy/checkout/{id}
יצירת קישור תשלום:
$checkoutUrl = route('officeguy.public.checkout', ['id' => $order->id]);
// שליחה ללקוח
Mail::to($customer->email)->send(new PaymentLinkEmail($checkoutUrl));
php artisan vendor:publish --tag=officeguy-views
לאחר מכן ערכו את הקובץ:
resources/views/vendor/officeguy/pages/checkout.blade.php
תכונות עמוד התשלום:
ניתן להגדיר אם שדות ת.ז ו-CVV יהיו חובה, אופציונליים, או מוסתרים.
ב-Admin Panel: נווטו ל-SUMIT Gateway > Gateway Settings > Payment Settings
אפשרויות לכל שדה:
required - חובה (ברירת מחדל)yes - אופציונלי (מוצג אך לא חובה)no - מוסתרב-.env:
OFFICEGUY_CITIZEN_ID=required # required/yes/no
OFFICEGUY_CVV=required # required/yes/no
בקוד:
// קריאה להגדרות
$settings = app(SettingsService::class);
$citizenIdMode = $settings->get('citizen_id', 'required');
$cvvMode = $settings->get('cvv', 'required');
⚠️ חשוב: חברות האשראי מחייבות הזנת נתונים אלה. כדי להסתיר את השדות, יש לקבל מהן פטור מהזנת מס' ת.ז ו-CVV.
בברירת המחדל יופקו המסמכים בעברית. הפעלת "בחירת שפה אוטומטית" תאפשר להפיק את המסמכים בהתאם לשפת הלקוח/ה.
ב-Admin Panel:
ב-.env:
OFFICEGUY_AUTOMATIC_LANGUAGES=true
הפקת מסמך הזמנה נוסף ושליחתו ללקוח לאחר חיוב מוצלח, בנוסף למסמך חשבונית/קבלה.
ב-Admin Panel:
ב-.env:
OFFICEGUY_CREATE_ORDER_DOCUMENT=true
# שליחת מסמך במייל ללקוח
OFFICEGUY_EMAIL_DOCUMENT=true
# יצירת מסמך כטיוטא (לא סופי)
OFFICEGUY_DRAFT_DOCUMENT=false
// במודל Payable שלכם
public function getVatRate(): ?float
{
return 17.0; // 17% מע"מ
}
public function isTaxEnabled(): bool
{
return true;
}
הפקת מסמך (חשבונית/קבלה) אוטומטית בתשלום ב-PayPal, BlueSnap, או שערי תשלום אחרים.
ב-Admin Panel: נווטו ל-Gateway Settings > Additional Features
ב-.env:
# PayPal - אפשרויות: no, yes, async
OFFICEGUY_PAYPAL_RECEIPTS=yes
# BlueSnap
OFFICEGUY_BLUESNAP_RECEIPTS=true
# שערים אחרים
OFFICEGUY_OTHER_RECEIPTS=stripe,paddle
בקוד:
// הפקת קבלה ידנית לתשלום PayPal
DocumentService::createReceiptForExternalPayment($order, 'paypal', $transactionId);
הגדרת מספר תשלומים (עד 36) אפשרי לעסקה.
ב-Admin Panel: נווטו ל-Gateway Settings > Payment Settings
הגדרות:
ב-.env:
OFFICEGUY_MAX_PAYMENTS=12
OFFICEGUY_MIN_AMOUNT_FOR_PAYMENTS=100
OFFICEGUY_MIN_AMOUNT_PER_PAYMENT=50
בקוד:
// קבלת מספר תשלומים מקסימלי לסכום מסוים
$maxPayments = PaymentService::getMaximumPayments($amount);
// חיוב עם תשלומים
$result = PaymentService::processCharge($order, $paymentsCount = 6);
תפיסת מסגרת מאפשרת לבצע את חיוב האשראי בשלב מאוחר יותר - מתאימה לעסקאות עם סכום חיוב משתנה.
ב-Admin Panel: נווטו ל-Gateway Settings > Payment Settings
הגדרות:
ב-.env:
OFFICEGUY_AUTHORIZE_ONLY=true
OFFICEGUY_AUTHORIZE_ADDED_PERCENT=20
OFFICEGUY_AUTHORIZE_MINIMUM_ADDITION=50
בקוד:
// תפיסת מסגרת
$result = PaymentService::authorizePayment($order, $amount);
// חיוב מאוחר יותר
$result = PaymentService::capturePayment($transactionId, $finalAmount);
💡 שימוש נפוץ: בתי מלון, השכרת רכב, או כל עסקה שבה הסכום הסופי עשוי להשתנות.
מצב טסט מאפשר לבצע בדיקות כדי לוודא שהכל עובד בלי לסלוק ולבצע חיובים אמיתיים. מסמכים יופקו כטיוטות.
ב-Admin Panel: נווטו ל-Gateway Settings > Environment Settings > סמנו "Testing Mode"
ב-.env:
OFFICEGUY_TESTING=true
מספרי כרטיסים לבדיקות:
| כרטיס | מספר | תוקף | CVV |
|---|---|---|---|
| ויזה (הצלחה) | 4580 0000 0000 0000 | כל תאריך עתידי | 123 |
| ויזה (כישלון) | 4580 0000 0000 0001 | כל תאריך עתידי | 123 |
| מאסטרקארד | 5326 1000 0000 0000 | כל תאריך עתידי | 123 |
בקוד:
// בדיקה אם במצב טסט
$isTest = app(SettingsService::class)->get('testing', false);
⚠️ חשוב: לפני שהאתר עולה לאוויר, ודאו שביטלתם את מצב הטסט כדי לא לפספס מכירות אמיתיות!
מאפשר ללקוחות לשמור את פרטי כרטיס האשראי לרכישות עתידיות מהירות יותר.
ב-Admin Panel: נווטו ל-Gateway Settings > Tokenization > סמנו "Support Tokens"
ב-.env:
OFFICEGUY_SUPPORT_TOKENS=true
OFFICEGUY_TOKEN_PARAM=5 # 5=J5 (מומלץ), 2=J2
בקוד:
// שמירת טוקן לאחר חיוב
$token = OfficeGuyToken::createFromApiResponse($customer, $response);
// חיוב עם טוקן שמור
$result = PaymentService::processCharge($order, $payments, false, false, $token);
// קבלת טוקנים של לקוח
$tokens = OfficeGuyToken::where('owner_type', get_class($user))
->where('owner_id', $user->id)
->get();
תכונות:
לגביית תשלומים קבועים מלקוחות או תורמים, החבילה מספקת פתרון יעיל ואוטומטי לניהול מנויים.
ב-Admin Panel: נווטו ל-Gateway Settings > Subscriptions
הגדרות:
ב-.env:
OFFICEGUY_SUBSCRIPTIONS_ENABLED=true
OFFICEGUY_SUBSCRIPTIONS_DEFAULT_INTERVAL=1
OFFICEGUY_SUBSCRIPTIONS_DEFAULT_CYCLES=12
OFFICEGUY_SUBSCRIPTIONS_ALLOW_PAUSE=true
OFFICEGUY_SUBSCRIPTIONS_RETRY_FAILED=true
OFFICEGUY_SUBSCRIPTIONS_MAX_RETRIES=3
יצירת מנוי:
use OfficeGuy\LaravelSumitGateway\Services\SubscriptionService;
// יצירת מנוי חדש
$subscription = SubscriptionService::create(
$user, // הלקוח
'תוכנית חודשית', // שם המנוי
99.00, // סכום
'ILS', // מטבע
1, // אינטרוול בחודשים
12, // מספר חיובים (null = ללא הגבלה)
$tokenId // טוקן לתשלום
);
// חיוב ראשוני
$result = SubscriptionService::processInitialCharge($subscription);
// חיוב ידני
$result = SubscriptionService::processRecurringCharge($subscription);
// השהיית מנוי
SubscriptionService::pause($subscription);
// חידוש מנוי
SubscriptionService::resume($subscription);
// ביטול מנוי
SubscriptionService::cancel($subscription);
תזמון חיובים חוזרים אוטומטיים:
הוסיפו ל-routes/console.php:
use Illuminate\Support\Facades\Schedule;
// חיוב יומי בשעה 8:00
Schedule::command('sumit:process-recurring-payments')->dailyAt('08:00');
// או חיוב כל שעה
Schedule::command('sumit:process-recurring-payments')->hourly();
// עם דיווח על כשלונות
Schedule::command('sumit:process-recurring-payments')
->daily()
->emailOutputOnFailure('admin@example.com');
הרצה ידנית:
# הרצה אסינכרונית (כ-job)
php artisan sumit:process-recurring-payments
# הרצה סינכרונית
php artisan sumit:process-recurring-payments --sync
# עיבוד מנוי ספציפי
php artisan sumit:process-recurring-payments --subscription=123
📦 לניהול המלאי, יש להתקין את מודול מלאי בחשבון SUMIT.
ב-Admin Panel: נווטו ל-Gateway Settings > Additional Features
הגדרות:
none, 12 (שעות), 24 (שעות)ב-.env:
OFFICEGUY_STOCK_SYNC_FREQ=12 # none/12/24
OFFICEGUY_CHECKOUT_STOCK_SYNC=true
Callback לעדכון מלאי:
// config/officeguy.php
'stock' => [
'update_callback' => function(array $stockItem) {
// עדכון מלאי במוצר
$product = Product::where('sku', $stockItem['sku'])->first();
if ($product) {
$product->update(['stock_quantity' => $stockItem['quantity']]);
}
},
],
הרצת סנכרון ידנית:
php artisan sumit:stock-sync
סנכרון בקוד:
use OfficeGuy\LaravelSumitGateway\Services\Stock\StockSyncService;
// סנכרון כל המלאי
StockSyncService::syncAll();
// סנכרון מוצר ספציפי
StockSyncService::syncProduct($sku);
תזמון סנכרון אוטומטי:
// routes/console.php
Schedule::command('sumit:stock-sync')->everyTwelveHours();
גביה באמצעות Bit, Google Pay, Apple Pay, 3DS אפשרית באמצעות הגדרת דף סליקה בשיטת Redirect.
ב-Admin Panel: נווטו ל-Gateway Settings > Environment Settings > PCI Mode > בחרו "Redirect"
ב-.env:
OFFICEGUY_PCI_MODE=redirect
OFFICEGUY_BIT_ENABLED=true
בקוד:
// חיוב עם Bit
$result = BitPaymentService::processOrder(
$order,
route('checkout.success'),
route('checkout.failed'),
route('officeguy.webhook.bit')
);
if ($result['success']) {
return redirect($result['redirect_url']);
}
Webhook ל-Bit:
POST /officeguy/webhook/bit
החבילה מטפלת אוטומטית ב-webhook ומעדכנת את סטטוס ההזמנה.
⚠️ שימו לב: מצב Redirect לא תומך בהוראות קבע, שמירת פרטי תשלום, ותפיסת מסגרת.
מיזוג כרטיס לקוח קיים במערכת SUMIT בסיום הרכישה באתר כדי למנוע כפילות. המיזוג מתבצע בהתאם למזהה הלקוח או המייל.
ב-Admin Panel: נווטו ל-Gateway Settings > Customer Merging > סמנו "Enable Customer Merging"
ב-.env:
OFFICEGUY_MERGE_CUSTOMERS=true
איך זה עובד:
ניתן לסנכרן לקוחות מ-SUMIT עם מודל הלקוחות המקומי שלכם ללא לגעת בקוד המודל.
ב-Admin Panel: נווטו ל-Gateway Settings > Customer Merging
הגדרות:
| הגדרה | תיאור | דוגמה |
|---|---|---|
| Enable Customer Merging | הפעלת מיזוג ב-SUMIT | true |
| Enable Local Customer Sync | הפעלת סנכרון מקומי | true |
| Customer Model Class | שם מלא של מודל הלקוח | App\Models\User |
מיפוי שדות לקוח:
| שדה | ברירת מחדל | תיאור |
|---|---|---|
| Email Field | email |
שדה אימייל (מזהה ייחודי) |
| Name Field | name |
שדה שם מלא |
| Phone Field | phone |
שדה טלפון |
| First Name Field | - | שדה שם פרטי (אם נפרד) |
| Last Name Field | - | שדה שם משפחה (אם נפרד) |
| Company Field | - | שדה שם חברה |
| Address Field | - | שדה כתובת |
| City Field | - | שדה עיר |
| SUMIT ID Field | sumit_customer_id |
שדה לשמירת מזהה SUMIT |
דוגמה - חיבור למודל User:
php artisan make:migration add_sumit_customer_id_to_users_table
Schema::table('users', function (Blueprint $table) {
$table->string('sumit_customer_id')->nullable()->index();
});
ב-Admin Panel הגדירו:
App\Models\Useremailnamesumit_customer_idהפעילו סנכרון אוטומטי ב-Listener:
// app/Providers/EventServiceProvider.php
use OfficeGuy\LaravelSumitGateway\Events\SumitWebhookReceived;
use OfficeGuy\LaravelSumitGateway\Listeners\CustomerSyncListener;
protected $listen = [
SumitWebhookReceived::class => [
CustomerSyncListener::class,
],
];
שימוש ב-CustomerMergeService:
use OfficeGuy\LaravelSumitGateway\Services\CustomerMergeService;
// סנכרון ידני של לקוח מ-SUMIT
$mergeService = app(CustomerMergeService::class);
// מציאת לקוח לפי SUMIT ID
$customer = $mergeService->findBySumitId('12345');
// מציאת לקוח לפי אימייל
$customer = $mergeService->findByEmail('customer@example.com');
// סנכרון לקוח מנתוני SUMIT
$sumitData = [
'ID' => '12345',
'Email' => 'customer@example.com',
'FirstName' => 'John',
'LastName' => 'Doe',
'Phone' => '0501234567',
];
$localCustomer = $mergeService->syncFromSumit($sumitData);
יתרונות:
החבילה דורשת שמודל ההזמנה יממש OfficeGuy\LaravelSumitGateway\Contracts\Payable.
ניתן לחבר כל מודל קיים מבלי לשנות את הקוד שלו. ראו סעיף עמוד תשלום ציבורי.
class Order extends Model implements Payable {
use \OfficeGuy\LaravelSumitGateway\Support\Traits\PayableAdapter;
}
כדאי להעמיס (eager load) יחסי items/fees.
'order' => [
'model' => App\Models\Order::class,
// או
'resolver' => fn($id) => App\Models\Order::with('items','fees')->find($id),
],
תחת prefix (ברירת מחדל officeguy):
| מסלול | סוג | תיאור |
|---|---|---|
callback/card |
GET | חזרת Redirect מכרטיס |
webhook/bit |
POST | IPN ל-Bit |
checkout/charge |
POST | מסלול סליקה מובנה (אופציונלי) |
checkout/{id} |
GET/POST | עמוד תשלום ציבורי (אופציונלי) |
מסלולי הצלחה/כישלון: מוגדרים ב-config routes.success / routes.failed.
Admin Panel > SUMIT Gateway > Gateway Settings
ה-HTTP client משתמש ב-ssl_verify (ברירת מחדל true). לשימוש dev בלבד ניתן לכבות:
OFFICEGUY_SSL_VERIFY=false
הפעלת לוגים לניטור ודיבוג:
OFFICEGUY_LOGGING=true
OFFICEGUY_LOG_CHANNEL=stack
🔒 נתונים רגישים (מספר כרטיס/CVV) מנוקים אוטומטית מהלוגים.
תמיכה בשוק (marketplace) עם credentials נפרדים לכל ספק.
ב-Admin Panel: נווטו ל-Gateway Settings > Multi-Vendor
הגדרות:
ב-.env:
OFFICEGUY_MULTIVENDOR_ENABLED=true
OFFICEGUY_MULTIVENDOR_VALIDATE_CREDENTIALS=true
OFFICEGUY_MULTIVENDOR_ALLOW_AUTHORIZE=false
בקוד:
use OfficeGuy\LaravelSumitGateway\Models\VendorCredential;
use OfficeGuy\LaravelSumitGateway\Services\MultiVendorPaymentService;
// שמירת credentials לספק
VendorCredential::create([
'vendor_type' => get_class($vendor),
'vendor_id' => $vendor->id,
'company_id' => '12345',
'api_key' => 'your-api-key',
]);
// חיוב הזמנה מרובת ספקים
$result = MultiVendorPaymentService::processMultiVendorCharge($order, $paymentsCount);
Resolver לזיהוי ספק:
// config/officeguy.php
'multivendor' => [
'vendor_resolver' => fn(array $item) => \App\Models\Vendor::find($item['vendor_id']),
],
הפקת קבלת תרומה אוטומטית במקום חשבונית רגילה.
ב-Admin Panel: נווטו ל-Gateway Settings > Donations
הגדרות:
ב-.env:
OFFICEGUY_DONATIONS_ENABLED=true
OFFICEGUY_DONATIONS_ALLOW_MIXED=false
OFFICEGUY_DONATIONS_DOCUMENT_TYPE=320 # 320=קבלת תרומה
בקוד:
use OfficeGuy\LaravelSumitGateway\Services\DonationService;
// בדיקה אם עגלה מכילה תרומות ומוצרים רגילים
$validation = DonationService::validateCart($order);
if (!$validation['valid']) {
return redirect()->back()->with('error', $validation['message']);
}
// קבלת סוג המסמך המתאים
$docType = DonationService::getDocumentType($order);
חיוב מוצרים נוספים באמצעות טוקן מהחיוב הראשי - ללא צורך להזין שוב פרטי כרטיס.
ב-Admin Panel: נווטו ל-Gateway Settings > Upsell / CartFlows
הגדרות:
ב-.env:
OFFICEGUY_UPSELL_ENABLED=true
OFFICEGUY_UPSELL_REQUIRE_TOKEN=true
OFFICEGUY_UPSELL_MAX_PER_ORDER=5
בקוד:
use OfficeGuy\LaravelSumitGateway\Services\UpsellService;
// חיוב עם טוקן ידוע
$result = UpsellService::processUpsellCharge($upsellOrder, $token, $parentOrderId);
// חיוב עם זיהוי אוטומטי של הטוקן
$result = UpsellService::processUpsellWithAutoToken($upsellOrder, $parentOrderId, $customer);
החבילה תוכל ליצור באופן אוטומטי חשבון משתמש עבור קונים אורחים (לא מחוברים) לאחר השלמת תשלום מוצלח.
ב-Admin Panel: נווטו ל-SUMIT Gateway > Gateway Settings > User Management
הגדרות זמינות:
ב-.env:
OFFICEGUY_AUTO_CREATE_GUEST_USER=true
OFFICEGUY_GUEST_PASSWORD_EXPIRY_DAYS=7
PaymentCompletedהמייל שנשלח ללקוח כולל:
User:
[
'name' => 'שם מלא מההזמנה',
'first_name' => 'שם פרטי',
'last_name' => 'שם משפחה',
'email' => 'client@example.com',
'phone' => 'טלפון מההזמנה',
'company' => 'שם החברה (אם קיים)',
'address' => 'כתובת',
'city' => 'עיר',
'country' => 'IL',
'password' => 'סיסמה מוצפנת (Hash)',
'role' => 'client',
'email_verified_at' => now(),
'has_temporary_password' => true,
'temporary_password_expires_at' => now()->addDays(7),
'temporary_password_created_by' => null, // נוצרה אוטומטית
]
Client:
Client::createFromUser($user);
// יוצר Client עם כל הפרטים מהמשתמש
Order:
$order->update([
'user_id' => $user->id,
'client_id' => $client->id,
]);
אם ברצונך להשבית את יצירת המשתמש האוטומטית:
ב-Admin Panel: נווטו ל-Gateway Settings > User Management ושנו את Auto Create Guest User ל-OFF
או ב-.env:
OFFICEGUY_AUTO_CREATE_GUEST_USER=false
src/Listeners/AutoCreateUserListener.php - Listener המטפל ביצירת המשתמשapp/Mail/GuestWelcomeWithPasswordMail.php - Mailable לשליחת המיילresources/views/emails/guest-welcome-with-password.blade.php - תבנית המיילconfig/officeguy.php:108-123 - הגדרותהחבילה כוללת מערכת אבטחה 7-שכבות לעמודי הצלחה שלאחר תשלום, המונעת גישה לא מורשית ל-URLs של הצלחה וחושפת נתוני תשלום רק לבעלים הלגיטימיים.
הבעיה:
עמודי הצלחה מסורתיים משתמשים ב-URLs פשוטים כמו /order/success/264 שכל אחד יכול לנחש ולגשת אליהם, מה שחושף:
הפתרון: מערכת אבטחה 7-שכבות המייצרת URLs חד-פעמיים עם טוקנים קריפטוגרפיים.
1. תשלום מוצלח
↓
2. CardCallbackController מייצר טוקן (128 תווים אקראיים)
↓
3. Nonce קריפטוגרפי (64 תווים) נוצר
↓
4. SHA256 hash נשמר ב-DB (לא הטוקן הגולמי!)
↓
5. Laravel signed URL נוצר עם הטוקן והנונס
↓
6. הלקוח מופנה ל-URL המאובטח
↓
7. SecureSuccessController מאמת את 7 השכבות
↓
8. הטוקן נצרך (consumed = true)
↓
9. עמוד ההצלחה מוצג (פעם אחת בלבד!)
https://nm-digitalhub.com/officeguy/success?
token=a1b2c3d4e5f6... ← 128 תווים אקראיים
&nonce=9f8e7d6c5b4a... ← 64 תווים nonce
&signature=7c1a2b3d4e5f... ← חתימת Laravel
&expires=1703462400 ← תוקף Signed URL
ב-Admin Panel: נווטו ל-SUMIT Gateway > Gateway Settings > Secure Success Page (v1.2.0+)
הגדרות זמינות:
| הגדרה | ברירת מחדל | תיאור |
|---|---|---|
| Enable Secure Success URLs | true |
הפעלת/השבתת מערכת האבטחה |
| Token Validity (Hours) | 24 |
כמה זמן הטוקן תקף (1-168 שעות) |
| Rate Limit - Max Attempts | 10 |
מספר ניסיונות גישה מקסימלי לכל IP |
| Rate Limit - Decay Time | 1 |
חלון זמן להגבלת קצב (דקות) |
ב-.env:
OFFICEGUY_SUCCESS_SECURE_ENABLED=true
OFFICEGUY_SUCCESS_TOKEN_TTL=24
OFFICEGUY_SUCCESS_RATE_LIMIT_MAX=10
OFFICEGUY_SUCCESS_RATE_LIMIT_DECAY=1
use OfficeGuy\LaravelSumitGateway\Services\SecureSuccessUrlGenerator;
$generator = app(SecureSuccessUrlGenerator::class);
// יצירת URL מאובטח עבור הזמנה
$secureUrl = $generator->generate($order);
// הפניה ללקוח
return redirect()->away($secureUrl);
use OfficeGuy\LaravelSumitGateway\Services\SuccessAccessValidator;
$validator = app(SuccessAccessValidator::class);
// אימות כל 7 השכבות
$result = $validator->validate($request);
if ($result->isValid) {
// הצג עמוד הצלחה
return view('success', ['order' => $result->order]);
}
// גישה נדחתה
return view('errors.access-denied', [
'error' => $result->error,
]);
✅ שימוש חד-פעמי: הטוקן נצרך אחרי הגישה הראשונה. אם הלקוח ינסה לרענן את העמוד, הוא יקבל שגיאה.
✅ אבטחה לאורחים: גם לקוחות לא מחוברים מוגנים באמצעות הוכחת בעלות קריפטוגרפית (nonce matching).
✅ תאימות לאחור:
אם המערכת מושבתת או ההזמנה לא מיישמת את ממשק Payable, המערכת חוזרת ל-redirect המסורתי.
✅ מניעת Replay Attacks: השימוש ב-nonce מונע שימוש חוזר בטוקן גם אם מישהו מצליח לגנוב אותו.
אם ברצונך לחזור ל-redirect המסורתי (לא מומלץ):
ב-Admin Panel: נווטו ל-Gateway Settings > Secure Success Page ושנו את Enable Secure Success URLs ל-OFF
או ב-.env:
OFFICEGUY_SUCCESS_SECURE_ENABLED=false
src/Services/SecureSuccessUrlGenerator.php - יצירת URLs מאובטחיםsrc/Services/SuccessAccessValidator.php - אימות 7 השכבותsrc/Http/Controllers/SecureSuccessController.php - Controller עמוד הצלחהsrc/Http/Controllers/CardCallbackController.php - שימוש ב-SecureSuccessUrlGeneratorsrc/Models/OrderSuccessToken.php - מודל הטוקניםsrc/Models/OrderSuccessAccessLog.php - לוג ניסיונות גישהdatabase/migrations/2025_12_18_000001_create_order_success_tokens_table.phpdatabase/migrations/2025_12_18_000002_create_order_success_access_log_table.phpconfig/officeguy.php:192-222 - הגדרות| אירוע | תיאור | Payload |
|---|---|---|
SuccessPageAccessed |
גישה מוצלחת לעמוד הצלחה | $order, $token, $user |
דוגמת Listener:
use OfficeGuy\LaravelSumitGateway\Events\SuccessPageAccessed;
Event::listen(SuccessPageAccessed::class, function($event) {
Log::info('Success page accessed', [
'order_id' => $event->order->id,
'user_id' => $event->user?->id ?? 'guest',
'token_id' => $event->token->id,
]);
});
החבילה משדרת את האירועים הבאים:
| אירוע | תיאור |
|---|---|
PaymentCompleted |
תשלום הצליח |
PaymentFailed |
תשלום נכשל |
DocumentCreated |
מסמך נוצר |
StockSynced |
מלאי סונכרן |
BitPaymentCompleted |
תשלום Bit הושלם |
SubscriptionCreated |
מנוי נוצר |
SubscriptionCharged |
מנוי חויב |
SubscriptionChargesFailed |
חיוב מנוי נכשל |
SubscriptionCancelled |
מנוי בוטל |
MultiVendorPaymentCompleted |
תשלום מרובה-ספקים הצליח |
MultiVendorPaymentFailed |
תשלום מרובה-ספקים נכשל |
UpsellPaymentCompleted |
תשלום upsell הצליח |
UpsellPaymentFailed |
תשלום upsell נכשל |
האזנה לאירועים:
// app/Providers/EventServiceProvider.php
use OfficeGuy\LaravelSumitGateway\Events\PaymentCompleted;
protected $listen = [
PaymentCompleted::class => [
\App\Listeners\SendPaymentConfirmation::class,
\App\Listeners\UpdateOrderStatus::class,
],
];
דוגמת Listener:
namespace App\Listeners;
use OfficeGuy\LaravelSumitGateway\Events\PaymentCompleted;
class SendPaymentConfirmation
{
public function handle(PaymentCompleted $event): void
{
$orderId = $event->orderId;
$transactionId = $event->transactionId;
// שליחת אימייל אישור
Mail::to($event->customerEmail)->send(new PaymentConfirmed($orderId));
}
}
במקום ליצור Listeners בקוד, ניתן להגדיר Webhooks מותאמים אישית ישירות מה-Admin Panel. המערכת תשלח התראות HTTP לכל URL שתגדירו כאשר מתרחשים אירועים.
ב-Admin Panel: נווטו ל-SUMIT Gateway > Gateway Settings > Custom Event Webhooks
אירועים נתמכים:
| אירוע | שדה בהגדרות | תיאור |
|---|---|---|
| Payment Completed | webhook_payment_completed |
תשלום הושלם בהצלחה |
| Payment Failed | webhook_payment_failed |
תשלום נכשל |
| Document Created | webhook_document_created |
מסמך (חשבונית/קבלה) נוצר |
| Subscription Created | webhook_subscription_created |
מנוי חדש נוצר |
| Subscription Charged | webhook_subscription_charged |
מנוי חויב |
| Bit Payment Completed | webhook_bit_payment_completed |
תשלום Bit הושלם |
| Stock Synced | webhook_stock_synced |
מלאי סונכרן |
הגדרת סוד לאימות:
הגדירו Webhook Secret ב-Admin Panel. המערכת תשלח חתימה בכותרת X-Webhook-Signature לאימות מקור הבקשה.
דוגמת Payload:
{
"event": "payment_completed",
"timestamp": "2024-01-15T10:30:00+02:00",
"order_id": 123,
"transaction_id": "TXN_12345",
"amount": 99.00,
"currency": "ILS",
"customer_email": "customer@example.com"
}
כותרות HTTP:
Content-Type: application/json
X-Webhook-Event: payment_completed
X-Webhook-Signature: sha256=abc123...
X-Webhook-Timestamp: 2024-01-15T10:30:00+02:00
אימות חתימה בשרת שלכם:
function verifyWebhook(Request $request): bool
{
$signature = $request->header('X-Webhook-Signature');
$payload = $request->getContent();
$secret = config('your-webhook-secret');
$expectedSignature = hash_hmac('sha256', $payload, $secret);
return hash_equals($expectedSignature, $signature);
}
שימוש ב-WebhookService ישירות (אופציונלי):
use OfficeGuy\LaravelSumitGateway\Services\WebhookService;
// שליחת webhook ידנית
$webhookService = app(WebhookService::class);
$webhookService->send('payment_completed', [
'order_id' => 123,
'amount' => 99.00,
]);
משאב מלא לצפייה וניהול כל אירועי ה-Webhook, כולל חיבור למשאבים קיימים לבניית אוטומציות.
ב-Admin Panel: נווטו ל-SUMIT Gateway > Webhook Events
רשימת אירועים:
פעולות:
חיבור למשאבים קיימים: כל אירוע מקושר אוטומטית למשאבים הרלוונטיים:
סטטיסטיקות (Widget):
כדי לקבל התראות, צרו endpoint בשרת שלכם שמקבל בקשות POST:
// routes/api.php
Route::post('/webhooks/sumit', [WebhookController::class, 'handle']);
// app/Http/Controllers/WebhookController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class WebhookController extends Controller
{
public function handle(Request $request)
{
// 1. אימות החתימה
$signature = $request->header('X-Webhook-Signature');
$payload = $request->getContent();
$secret = config('services.sumit.webhook_secret');
$expectedSignature = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expectedSignature, $signature)) {
Log::warning('Invalid webhook signature');
return response('Invalid signature', 401);
}
// 2. עיבוד האירוע
$event = $request->input('event');
$data = $request->all();
switch ($event) {
case 'payment_completed':
$this->handlePaymentCompleted($data);
break;
case 'payment_failed':
$this->handlePaymentFailed($data);
break;
case 'document_created':
$this->handleDocumentCreated($data);
break;
case 'subscription_charged':
$this->handleSubscriptionCharged($data);
break;
}
return response('OK', 200);
}
protected function handlePaymentCompleted(array $data): void
{
$orderId = $data['order_id'];
$transactionId = $data['transaction_id'];
$amount = $data['amount'];
// עדכון הזמנה
$order = Order::find($orderId);
$order->update([
'status' => 'paid',
'paid_at' => now(),
]);
// שליחת אישור ללקוח
Mail::to($data['customer_email'])->send(new PaymentConfirmation($order));
// עדכון CRM
CrmService::updateCustomer($data['customer_email'], [
'last_purchase' => now(),
'total_spent' => $amount,
]);
}
protected function handlePaymentFailed(array $data): void
{
$orderId = $data['order_id'];
$error = $data['error'] ?? 'Unknown error';
// עדכון הזמנה
Order::find($orderId)?->update(['status' => 'payment_failed']);
// התראה לצוות
Notification::route('slack', config('services.slack.webhook'))
->notify(new PaymentFailedNotification($orderId, $error));
}
protected function handleDocumentCreated(array $data): void
{
// שמירת קישור למסמך
$orderId = $data['order_id'];
$documentUrl = $data['document_url'] ?? null;
Order::find($orderId)?->update(['invoice_url' => $documentUrl]);
}
protected function handleSubscriptionCharged(array $data): void
{
$subscriptionId = $data['subscription_id'];
$amount = $data['amount'];
// רישום חיוב
SubscriptionCharge::create([
'subscription_id' => $subscriptionId,
'amount' => $amount,
'charged_at' => now(),
]);
}
}
use OfficeGuy\LaravelSumitGateway\Models\WebhookEvent;
// קבלת כל האירועים שנכשלו
$failedEvents = WebhookEvent::failed()->get();
// קבלת אירועים של לקוח ספציפי
$customerEvents = WebhookEvent::forCustomer('customer@example.com')->get();
// קבלת אירועים מסוג מסוים
$paymentEvents = WebhookEvent::ofType('payment_completed')
->with(['transaction', 'document'])
->get();
// אירועים מוכנים לשליחה חוזרת
$pendingRetries = WebhookEvent::readyForRetry()->get();
// אירועים ממוינים לפי תאריך
$recentEvents = WebhookEvent::orderBy('created_at', 'desc')
->limit(100)
->get();
// לכל אירוע יש גישה למשאבים הקשורים אליו
foreach ($paymentEvents as $event) {
// גישה לטרנזקציה
$transaction = $event->transaction;
if ($transaction) {
echo "Transaction ID: {$transaction->payment_id}";
echo "Amount: {$transaction->amount}";
}
// גישה למסמך
$document = $event->document;
if ($document) {
echo "Document Number: {$document->document_number}";
echo "Document URL: {$document->url}";
}
// גישה לטוקן
$token = $event->token;
if ($token) {
echo "Card: ****{$token->last_digits}";
}
// גישה למנוי
$subscription = $event->subscription;
if ($subscription) {
echo "Subscription: {$subscription->name}";
echo "Next Charge: {$subscription->next_charge_at}";
}
// גישה להזמנה (polymorphic)
$order = $event->order;
if ($order) {
echo "Order ID: {$order->id}";
}
}
use OfficeGuy\LaravelSumitGateway\Services\WebhookService;
// שליחה חוזרת של אירוע בודד
$event = WebhookEvent::find(123);
if ($event->canRetry()) {
$webhookService = app(WebhookService::class);
$success = $webhookService->send($event->event_type, $event->payload);
if ($success) {
$event->markAsSent(200);
} else {
$event->scheduleRetry(5); // retry in 5 minutes
}
}
// שליחה חוזרת לכל האירועים שנכשלו
$failedEvents = WebhookEvent::failed()->get();
foreach ($failedEvents as $event) {
if ($event->canRetry()) {
$event->scheduleRetry();
}
}
use OfficeGuy\LaravelSumitGateway\Models\WebhookEvent;
// יצירת אירוע חדש
$event = WebhookEvent::createEvent('payment_completed', [
'order_id' => 123,
'amount' => 99.00,
'currency' => 'ILS',
'customer_email' => 'customer@example.com',
], [
'transaction_id' => $transaction->id,
'document_id' => $document->id,
'webhook_url' => 'https://your-site.com/webhook',
]);
// סימון כנשלח
$event->markAsSent(200, ['received' => true]);
// סימון ככישלון
$event->markAsFailed('Connection timeout', 504);
// app/Console/Commands/SyncWebhooksToCrm.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use OfficeGuy\LaravelSumitGateway\Models\WebhookEvent;
use App\Services\CrmService;
class SyncWebhooksToCrm extends Command
{
protected $signature = 'crm:sync-webhooks';
protected $description = 'Sync payment webhooks to CRM';
public function handle(CrmService $crm)
{
// קבלת כל האירועים שטרם סונכרנו
$events = WebhookEvent::ofType('payment_completed')
->sent()
->where('synced_to_crm', false)
->with(['transaction', 'document'])
->get();
foreach ($events as $event) {
$crm->recordPurchase([
'email' => $event->customer_email,
'amount' => $event->amount,
'currency' => $event->currency,
'transaction_id' => $event->transaction?->payment_id,
'invoice_url' => $event->document?->url,
]);
$event->update(['synced_to_crm' => true]);
}
$this->info("Synced {$events->count()} events to CRM");
}
}
// app/Console/Commands/WebhookDailyReport.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use OfficeGuy\LaravelSumitGateway\Models\WebhookEvent;
use Illuminate\Support\Facades\Mail;
class WebhookDailyReport extends Command
{
protected $signature = 'webhooks:daily-report';
protected $description = 'Send daily webhook statistics report';
public function handle()
{
$today = now()->startOfDay();
$stats = [
'total' => WebhookEvent::whereDate('created_at', $today)->count(),
'sent' => WebhookEvent::sent()->whereDate('created_at', $today)->count(),
'failed' => WebhookEvent::failed()->whereDate('created_at', $today)->count(),
'by_type' => WebhookEvent::whereDate('created_at', $today)
->selectRaw('event_type, count(*) as count')
->groupBy('event_type')
->pluck('count', 'event_type'),
'total_amount' => WebhookEvent::ofType('payment_completed')
->whereDate('created_at', $today)
->sum('amount'),
];
// שליחת דוח במייל
Mail::to('admin@example.com')->send(new WebhookStatsReport($stats));
$this->info("Report sent. Total events: {$stats['total']}");
}
}
// app/Console/Commands/MonitorWebhookFailures.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use OfficeGuy\LaravelSumitGateway\Models\WebhookEvent;
use Illuminate\Support\Facades\Notification;
use App\Notifications\WebhookFailureAlert;
class MonitorWebhookFailures extends Command
{
protected $signature = 'webhooks:monitor';
protected $description = 'Monitor webhook failures and alert';
public function handle()
{
$failedCount = WebhookEvent::failed()
->where('created_at', '>=', now()->subHour())
->count();
if ($failedCount > 10) {
// שליחת התראה
Notification::route('slack', config('services.slack.webhook'))
->notify(new WebhookFailureAlert($failedCount));
$this->error("Alert sent: {$failedCount} failures in the last hour");
} else {
$this->info("All good: {$failedCount} failures in the last hour");
}
}
}
הוסיפו ל-routes/console.php:
use Illuminate\Support\Facades\Schedule;
// עיבוד webhooks שממתינים לשליחה חוזרת
Schedule::command('sumit:process-webhook-retries')->everyFiveMinutes();
// דוח יומי
Schedule::command('webhooks:daily-report')->dailyAt('09:00');
// ניטור כשלונות
Schedule::command('webhooks:monitor')->everyThirtyMinutes();
// סנכרון עם CRM
Schedule::command('crm:sync-webhooks')->hourly();
| סוג אירוע | קבוע | תיאור | שדות עיקריים |
|---|---|---|---|
| Payment Completed | payment_completed |
תשלום הושלם בהצלחה | order_id, transaction_id, amount, customer_email |
| Payment Failed | payment_failed |
תשלום נכשל | order_id, error, customer_email |
| Document Created | document_created |
מסמך נוצר | order_id, document_id, document_number, document_url |
| Subscription Created | subscription_created |
מנוי חדש נוצר | subscription_id, customer_email, amount, interval |
| Subscription Charged | subscription_charged |
מנוי חויב | subscription_id, transaction_id, amount |
| Bit Payment | bit_payment_completed |
תשלום Bit הושלם | order_id, transaction_id, amount |
| Stock Synced | stock_synced |
מלאי סונכרן | items_count, sync_time |
| סטטוס | קבוע | תיאור |
|---|---|---|
| Pending | pending |
ממתין לשליחה |
| Sent | sent |
נשלח בהצלחה |
| Failed | failed |
השליחה נכשלה |
| Retrying | retrying |
מתוזמן לשליחה חוזרת |
SUMIT יכולה לשלוח התראות (Webhooks) לאפליקציה שלכם כאשר מתרחשות פעולות במערכת SUMIT. זה מאפשר לכם לקבל עדכונים בזמן אמת על פעולות שבוצעו במערכת ניהול החשבונות.
מידע נוסף:
| פעולה | תיאור |
|---|---|
card_created |
יצירת כרטיס (לקוח, מסמך, פריט וכו') |
card_updated |
עדכון כרטיס |
card_deleted |
מחיקת כרטיס |
card_archived |
העברת כרטיס לארכיון |
| סוג כרטיס | תיאור |
|---|---|
customer |
כרטיס לקוח |
document |
מסמך (חשבונית, קבלה) |
transaction |
עסקה |
item |
פריט מלאי |
payment |
תשלום |
החבילה חושפת מספר endpoints לקבלת webhooks מ-SUMIT:
| כתובת | תיאור |
|---|---|
POST /officeguy/webhook/sumit |
Endpoint כללי (זיהוי אוטומטי) |
POST /officeguy/webhook/sumit/card-created |
יצירת כרטיס |
POST /officeguy/webhook/sumit/card-updated |
עדכון כרטיס |
POST /officeguy/webhook/sumit/card-deleted |
מחיקת כרטיס |
POST /officeguy/webhook/sumit/card-archived |
העברת לארכיון |
התקנת מודולים נדרשים ב-SUMIT:
יצירת תצוגה:
יצירת טריגר:
הגדרת הכתובת:
https://your-domain.com/officeguy/webhook/sumit
או לאירוע ספציפי:
https://your-domain.com/officeguy/webhook/sumit/card-created
צפייה בכל ה-webhooks שהתקבלו מ-SUMIT ב-Admin Panel:
ב-Admin Panel: נווטו ל-SUMIT Gateway > SUMIT Webhooks
תכונות:
סטטיסטיקות:
// app/Providers/EventServiceProvider.php
use OfficeGuy\LaravelSumitGateway\Events\SumitWebhookReceived;
protected $listen = [
SumitWebhookReceived::class => [
\App\Listeners\HandleSumitWebhook::class,
],
];
// app/Listeners/HandleSumitWebhook.php
namespace App\Listeners;
use OfficeGuy\LaravelSumitGateway\Events\SumitWebhookReceived;
use OfficeGuy\LaravelSumitGateway\Models\SumitWebhook;
class HandleSumitWebhook
{
public function handle(SumitWebhookReceived $event): void
{
$webhook = $event->webhook;
switch ($webhook->event_type) {
case SumitWebhook::TYPE_CARD_CREATED:
$this->handleCardCreated($webhook);
break;
case SumitWebhook::TYPE_CARD_UPDATED:
$this->handleCardUpdated($webhook);
break;
case SumitWebhook::TYPE_CARD_DELETED:
$this->handleCardDeleted($webhook);
break;
case SumitWebhook::TYPE_CARD_ARCHIVED:
$this->handleCardArchived($webhook);
break;
}
}
protected function handleCardCreated(SumitWebhook $webhook): void
{
// טיפול ביצירת כרטיס
$cardType = $webhook->card_type;
$cardId = $webhook->card_id;
$payload = $webhook->payload;
if ($cardType === 'customer') {
// סנכרון לקוח חדש למערכת
Customer::create([
'sumit_id' => $cardId,
'name' => $payload['Name'] ?? '',
'email' => $payload['Email'] ?? '',
'phone' => $payload['Phone'] ?? '',
]);
} elseif ($cardType === 'document') {
// שמירת מסמך חדש
Document::create([
'sumit_id' => $cardId,
'number' => $payload['Number'] ?? '',
'amount' => $payload['Amount'] ?? 0,
]);
}
// סימון כמעובד
$webhook->markAsProcessed('Successfully synced');
}
protected function handleCardUpdated(SumitWebhook $webhook): void
{
// עדכון כרטיס קיים
$cardType = $webhook->card_type;
$cardId = $webhook->card_id;
if ($cardType === 'customer') {
Customer::where('sumit_id', $cardId)->update([
'name' => $webhook->payload['Name'] ?? '',
'email' => $webhook->payload['Email'] ?? '',
]);
}
$webhook->markAsProcessed('Successfully updated');
}
protected function handleCardDeleted(SumitWebhook $webhook): void
{
// מחיקת כרטיס
$cardType = $webhook->card_type;
$cardId = $webhook->card_id;
if ($cardType === 'customer') {
Customer::where('sumit_id', $cardId)->delete();
}
$webhook->markAsProcessed('Successfully deleted');
}
protected function handleCardArchived(SumitWebhook $webhook): void
{
// סימון כרטיס כמאורכב
$cardType = $webhook->card_type;
$cardId = $webhook->card_id;
if ($cardType === 'customer') {
Customer::where('sumit_id', $cardId)
->update(['archived' => true]);
}
$webhook->markAsProcessed('Successfully archived');
}
}
use OfficeGuy\LaravelSumitGateway\Models\SumitWebhook;
// קבלת webhooks שטרם טופלו
$pending = SumitWebhook::received()->get();
// קבלת webhooks לפי סוג אירוע
$createdCards = SumitWebhook::ofType('card_created')->get();
// קבלת webhooks לפי סוג כרטיס
$customerWebhooks = SumitWebhook::ofCardType('customer')->get();
// קבלת webhooks שנכשלו
$failed = SumitWebhook::failed()->get();
// קבלת webhooks של לקוח ספציפי
$customerWebhooks = SumitWebhook::forCustomer('CUST123')->get();
// סימון webhook כמעובד
$webhook->markAsProcessed('Synced to CRM', [
'transaction_id' => $transaction->id,
]);
// סימון webhook כנכשל
$webhook->markAsFailed('API error: 500');
// סימון webhook כמתעלם
$webhook->markAsIgnored('Duplicate webhook');
SUMIT מבצעת ניסיונות חוזרים אוטומטיים:
המלצות:
// מומלץ: עיבוד אסינכרוני
public function handle(Request $request): JsonResponse
{
// שמירה מהירה של ה-webhook
$webhook = SumitWebhook::createFromRequest(...);
// דחיית העיבוד ל-queue
ProcessSumitWebhookJob::dispatch($webhook);
// החזרת תשובה מיידית (תוך 10 שניות!)
return response()->json(['success' => true], 200);
}
// app/Jobs/SyncCustomerFromSumit.php
public function handle(): void
{
$webhook = $this->webhook;
if ($webhook->card_type !== 'customer') {
$webhook->markAsIgnored('Not a customer card');
return;
}
$payload = $webhook->payload;
Customer::updateOrCreate(
['sumit_id' => $webhook->card_id],
[
'name' => $payload['Name'] ?? '',
'email' => $payload['Email'] ?? '',
'phone' => $payload['Phone'] ?? '',
'address' => $payload['Address'] ?? '',
]
);
$webhook->markAsProcessed('Customer synced');
}
// app/Jobs/SyncInventoryFromSumit.php
public function handle(): void
{
$webhook = $this->webhook;
if ($webhook->card_type !== 'item') {
$webhook->markAsIgnored('Not an item card');
return;
}
$payload = $webhook->payload;
Product::updateOrCreate(
['sumit_sku' => $payload['SKU'] ?? $webhook->card_id],
[
'name' => $payload['Name'] ?? '',
'price' => $payload['Price'] ?? 0,
'stock' => $payload['Stock'] ?? 0,
]
);
$webhook->markAsProcessed('Inventory synced');
}
// app/Jobs/NotifyNewDocument.php
public function handle(): void
{
$webhook = $this->webhook;
if ($webhook->card_type !== 'document') {
$webhook->markAsIgnored('Not a document');
return;
}
$payload = $webhook->payload;
// שליחת התראה לצוות
Notification::route('slack', config('services.slack.webhook'))
->notify(new NewDocumentFromSumit([
'document_number' => $payload['Number'] ?? '',
'amount' => $payload['Amount'] ?? 0,
'customer' => $payload['CustomerName'] ?? '',
]));
$webhook->markAsProcessed('Notification sent');
}
| טבלה | תיאור |
|---|---|
officeguy_transactions |
טרנזקציות תשלום |
officeguy_tokens |
כרטיסי אשראי שמורים |
officeguy_documents |
חשבוניות וקבלות |
officeguy_settings |
הגדרות מערכת |
vendor_credentials |
credentials לספקים |
subscriptions |
מנויים |
officeguy_webhook_events |
אירועי Webhook (יוצאים) |
officeguy_sumit_webhooks |
Webhooks מ-SUMIT (נכנסים) |
המיגרציות נטענות אוטומטית מהחבילה. להעתקה מקומית:
php artisan vendor:publish --tag=officeguy-migrations
הרצת בדיקות:
composer test
החבילה מציעה מספר קבצים לפרסום (publish) להתאמה אישית. להלן פירוט כל קובץ, מה הוא מכיל, ומתי כדאי להשתמש בו.
# פרסום כל הקבצים בבת אחת
php artisan vendor:publish --provider="OfficeGuy\LaravelSumitGateway\OfficeGuyServiceProvider"
# או פרסום קבצים ספציפיים לפי תגית (tag)
php artisan vendor:publish --tag=<tag-name>
--tag=officeguy-config)php artisan vendor:publish --tag=officeguy-config
מיקום: config/officeguy.php
מה מכיל:
מתי להשתמש:
order.resolver, stock.update_callback)דוגמה להתאמה אישית:
// config/officeguy.php
return [
'order' => [
'resolver' => fn($id) => \App\Models\Order::with(['items', 'fees', 'customer'])->find($id),
],
'stock' => [
'update_callback' => fn(array $stockItem) => \App\Services\InventoryService::updateStock($stockItem),
],
'multivendor' => [
'enabled' => true,
'vendor_resolver' => fn(array $item) => \App\Models\Vendor::find($item['vendor_id']),
],
];
--tag=officeguy-migrations)php artisan vendor:publish --tag=officeguy-migrations
מיקום: database/migrations/
מה מכיל:
create_officeguy_transactions_table - טבלת טרנזקציותcreate_officeguy_tokens_table - טבלת טוקנים (כרטיסי אשראי שמורים)create_officeguy_documents_table - טבלת מסמכים (חשבוניות/קבלות)create_officeguy_settings_table - טבלת הגדרותcreate_vendor_credentials_table - טבלת credentials לספקים (Multi-Vendor)create_subscriptions_table - טבלת מנוייםadd_donation_and_vendor_fields - שדות נוספים לתרומות וספקיםמתי להשתמש:
הערה חשובה: לאחר פרסום המיגרציות, החבילה תמשיך לטעון את המיגרציות שלה מ-vendor/. כדי למנוע כפילויות, ודאו שאתם לא מריצים את אותן מיגרציות פעמיים.
--tag=officeguy-views)php artisan vendor:publish --tag=officeguy-views
מיקום: resources/views/vendor/officeguy/
מה מכיל:
components/payment-form.blade.php - טופס תשלום עם:
pages/checkout.blade.php - עמוד תשלום ציבורי מלא עם:
filament/pages/officeguy-settings.blade.php - עמוד הגדרות ב-Filament Adminfilament/client/payment-methods/hosted-token-form.blade.php - טופס ניהול אמצעי תשלום ללקוחמתי להשתמש:
דוגמה להתאמת טופס תשלום:
{{-- resources/views/vendor/officeguy/components/payment-form.blade.php --}}
<div class="my-custom-payment-form">
{{-- הוספת לוגו חברה --}}
<div class="company-logo mb-4">
<img src="https://raw.githubusercontent.com/nm-digitalhub/SUMIT-Payment-Gateway-for-laravel/HEAD/{{ asset('images/logo.svg') }}" alt="Logo">
</div>
{{-- שאר הטופס... --}}
</div>
| תגית | מיקום יעד | שימוש עיקרי |
|---|---|---|
officeguy-config |
config/officeguy.php |
הגדרות API, תשלומים, resolvers |
officeguy-migrations |
database/migrations/ |
התאמת מבנה מסד נתונים |
officeguy-views |
resources/views/vendor/officeguy/ |
התאמת עיצוב וממשק משתמש |
ניתן לפרסם מספר תגיות בבת אחת:
# פרסום קונפיג ותצוגות בלבד
php artisan vendor:publish --tag=officeguy-config --tag=officeguy-views
החבילה מספקת עמוד תשלום ציבורי שניתן לשייך לכל מודל המממש את הממשק Payable. זה מאפשר ליצור קישורי תשלום לכל סוג של מוצר, שירות או הזמנה במערכת.
ניתן להפעיל את עמוד התשלום הציבורי בשתי דרכים:
1. דרך Admin Panel (מומלץ):
גשו לעמוד ההגדרות ב-Filament Admin Panel:
App\Models\Order)2. דרך קובץ .env:
OFFICEGUY_ENABLE_PUBLIC_CHECKOUT=true
OFFICEGUY_ORDER_MODEL=App\Models\Order
לאחר ההפעלה, ניתן לגשת לעמוד התשלום בכתובת:
GET /officeguy/checkout/{id}
כאשר {id} הוא המזהה של המודל ה-Payable (למשל מזהה הזמנה).
// יצירת קישור תשלום להזמנה
$order = Order::find(123);
$checkoutUrl = route('officeguy.public.checkout', ['id' => $order->id]);
// שליחת הקישור ללקוח
Mail::to($order->customer_email)->send(new PaymentLinkEmail($checkoutUrl));
יש שתי דרכים לחבר את המודל שלכם לעמוד התשלום:
אפשרות 1: מיפוי שדות מ-Admin Panel (ללא שינוי קוד)
ניתן לחבר כל מודל קיים מבלי לשנות את הקוד שלו. פשוט הגדירו את מיפוי השדות ב-Admin Panel:
total, price, amount)currency) או השאירו ריק עבור ILSהמערכת תעטוף אוטומטית את המודל שלכם ותמפה את השדות.
אפשרות 2: מימוש ממשק Payable (למודלים מורכבים)
use OfficeGuy\LaravelSumitGateway\Contracts\Payable;
use OfficeGuy\LaravelSumitGateway\Support\Traits\PayableAdapter;
class Order extends Model implements Payable
{
use PayableAdapter;
// או מימוש ידני של המתודות
}
פרסמו את התצוגות והתאימו את pages/checkout.blade.php:
php artisan vendor:publish --tag=officeguy-views
לאחר מכן ערכו את הקובץ resources/views/vendor/officeguy/pages/checkout.blade.php להתאמה לעיצוב האתר שלכם.
| משתנה | תיאור |
|---|---|
$payable |
אובייקט ה-Payable (הזמנה/מוצר) |
$settings |
הגדרות שער התשלום |
$maxPayments |
מספר תשלומים מקסימלי |
$bitEnabled |
האם Bit מופעל |
$supportTokens |
האם שמירת כרטיסים מופעלת |
$savedTokens |
אוסף כרטיסים שמורים (למשתמש מחובר) |
$currency |
קוד מטבע (ILS, USD וכו') |
$currencySymbol |
סימן מטבע (₪, $ וכו') |
$checkoutUrl |
כתובת לשליחת הטופס |
ניתן להגדיר resolver מותאם אישית בקונפיגורציה:
// config/officeguy.php
'order' => [
'resolver' => fn($id) => \App\Models\Product::with('prices')->find($id),
],
MIT