UPGRADE.md
[!NOTE] We strive to document every potential breaking change. However, as some of these alterations occur in lesser-known sections of Bagisto, only a fraction of them may impact your application.
Impact Probability: High
Bagisto v2.4.x now requires PHP 8.3 or greater.
Impact Probability: High
Bagisto v2.4 has been upgraded to Laravel 12, which introduces stricter type checking and modernized date/time handling.
Laravel 12 enforces stricter type checking for Carbon date/time operations. If your custom code uses Carbon methods, ensure you're passing the correct parameter types:
Integer/Float Parameters Required
Carbon methods like addDays(), subDays(), etc., now require integer or float values, not strings:
- Carbon::now()->addDays('1')
+ Carbon::now()->addDays(1)
- Carbon::now()->subDays('7')
+ Carbon::now()->subDays(7)
Non-Null Timezones
Methods that accept timezone parameters no longer accept null values. Use a fallback:
- $date->setTimezone($channel->timezone)
+ $date->setTimezone($channel->timezone ?: config('app.timezone'))
If you're using legacy PHP date functions in your custom code, consider migrating to Carbon for better Laravel 12 compatibility:
- strtotime($date)
+ \Carbon\Carbon::parse($date)->timestamp
- date('Y-m-d H:i:s')
+ \Carbon\Carbon::now()->format('Y-m-d H:i:s')
- date('Y-m-d')
+ \Carbon\Carbon::now()->format('Y-m-d')
- date_default_timezone_set($timezone)
+ \Carbon\Carbon::now($timezone) // Isolated to instance
Laravel 12 has updated the format for PDF response headers. If you're generating PDFs in your custom code, update the Content-Disposition header:
- 'Content-Disposition' => 'attachment; filename="'.$fileName.'"',
+ 'Content-Disposition' => 'attachment; filename='.$fileName,
If you have custom test cases, ensure all test data includes required fields that may have been added in migrations. For example, if new foreign keys have been introduced, make sure your test factories and test data include these fields.
Impact Probability: High
Bagisto v2.4 has migrated from Google reCAPTCHA v2 to Google reCAPTCHA Enterprise, which introduces significant changes to the implementation and configuration.
The following configuration keys have changed:
v2.3 Configuration:
customer.captcha.credentials.site_keycustomer.captcha.credentials.secret_keyv2.4 Configuration:
customer.captcha.credentials.site_keycustomer.captcha.credentials.project_id (new)customer.captcha.credentials.api_key (replaces secret_key)customer.captcha.credentials.score_threshold (new)v2.3:
v2.4:
https://recaptchaenterprise.googleapis.com/v1/projects/{project_id}/assessments.The captcha token field name has changed:
v2.3:
'g-recaptcha-response' => 'required|captcha'
v2.4:
'recaptcha_token' => 'required|captcha'
Update Configuration:
Here are the details for updating the configuration to support Google reCAPTCHA Enterprise in Bagisto v2.4. You will need to update the configuration with the new keys and values as described below.
Obtain Google Cloud Project ID:
Generate API Key:
Create reCAPTCHA Site Key:
example.com).Configure in Bagisto Admin Panel:
Update Form Submissions:
g-recaptcha-response field name with recaptcha_token in all forms using captcha.Update Frontend Implementation:
https://www.google.com/recaptcha/enterprise.js.Review Validation Messages:
customer::app.validations.captcha.required and customer::app.validations.captcha.captcha).v2.3:
v2.4:
If you have custom implementations using the Captcha class:
v2.3:
// Old implementation
$captcha->getSecretKey();
$rules = ['g-recaptcha-response' => 'required|captcha'];
v2.4:
// New implementation
$captcha->getProjectId();
$captcha->getApiKey();
$captcha->getScoreThreshold();
$rules = ['recaptcha_token' => 'required|captcha'];
The new implementation includes comprehensive logging. Check your logs for:
reCAPTCHA: Validation failed. - Configuration or token issues.reCAPTCHA: Assessment response received. - Successful API communication.reCAPTCHA: Validation result. - Score and threshold comparison.Ensure your Google Cloud Project has:
Impact Probability: High
Bagisto v2.4 has upgraded from the abandoned paypal/paypal-checkout-sdk v1.0.1 to the modern paypal/paypal-server-sdk v2.0, which introduces significant changes to the implementation and improves reliability and security.
The PayPal SDK dependency has changed:
v2.3 Dependency:
"paypal/paypal-checkout-sdk": "1.0.1"
v2.4 Dependency:
"paypal/paypal-server-sdk": "^2.0"
If you have custom PayPal implementations, the following namespace imports need to be updated:
v2.3 Imports:
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
use PayPalCheckoutSdk\Orders\OrdersGetRequest;
use PayPalCheckoutSdk\Payments\CapturesRefundRequest;
v2.4 Imports:
use PaypalServerSdkLib\PaypalServerSdkClientBuilder;
use PaypalServerSdkLib\Authentication\ClientCredentialsAuthCredentialsBuilder;
use PaypalServerSdkLib\Environment;
The client initialization method has changed significantly:
v2.3:
$environment = $isSandbox
? new SandboxEnvironment($clientId, $clientSecret)
: new ProductionEnvironment($clientId, $clientSecret);
$client = new PayPalHttpClient($environment);
v2.4:
$environment = $isSandbox
? Environment::SANDBOX
: Environment::PRODUCTION;
$client = PaypalServerSdkClientBuilder::init()
->clientCredentialsAuthCredentials(
ClientCredentialsAuthCredentialsBuilder::init(
$clientId,
$clientSecret
)
)
->environment($environment)
->build();
The way API requests are made has changed:
v2.3:
// Create order
$request = new OrdersCreateRequest();
$request->headers["prefer"] = "return=representation";
$request->body = $orderData;
$response = $client->execute($request);
// Capture order
$request = new OrdersCaptureRequest($orderId);
$response = $client->execute($request);
v2.4:
// Create order
$ordersController = $client->getOrdersController();
$response = $ordersController->createOrder([
'body' => $orderData,
'prefer' => 'return=representation'
]);
// Capture order
$response = $ordersController->captureOrder($orderId, [
'prefer' => 'return=representation'
]);
Response object access has changed:
v2.3:
// Direct property access
$orderId = $response->result->id;
$status = $response->result->status;
$captureId = $response->result->purchase_units[0]->payments->captures[0]->id;
v2.4:
// Getter methods
$result = $response->getResult();
$orderId = $result->getId();
$status = $result->getStatus();
$captureId = $result->getPurchaseUnits()[0]->getPayments()->getCaptures()[0]->getId();
v2.3:
sales.invoice.save.after event.Transaction.php listener class.v2.4:
Update Dependencies:
composer remove paypal/paypal-checkout-sdk
composer require paypal/paypal-server-sdk:^2.0
Update Custom Implementations:
If you have custom PayPal integrations:
PaypalServerSdkLib\*.Test PayPal Functionality:
Review Configuration:
No changes required to PayPal configuration in admin panel. All existing settings (Client ID, Client Secret, sandbox mode) work as before.
v2.3:
v2.4:
Impact Probability: High
Bagisto v2.4 has completely removed the shetabit/visitor package and all visitor tracking functionality. This is a breaking change if your custom code relies on visitor data.
shetabit/visitor Composer dependencyvisitor() helper functionvisits database table (no longer created or used)Visitable trait from Product and Category modelsVisitor trait from the Customer modelconfig/visitor.php configuration fileWebkul\Core\Visitor, Webkul\Core\Models\Visit, Webkul\Core\Repositories\VisitRepositoryWebkul\Core\Jobs\UpdateCreateVisitIndex, Webkul\Core\Jobs\UpdateCreateVisitableIndexWebkul\Core\Listeners\ResponseCacheHitWebkul\Admin\Helpers\Reporting\VisitorRemove custom visitor code:
If your custom code calls visitor()->visit() or uses the Visitable/Visitor traits, remove those references:
- use Shetabit\Visitor\Traits\Visitable;
- class MyModel extends Model {
- use Visitable;
- }
- visitor()->visit($model);
Remove the Composer dependency (if separately required):
composer remove shetabit/visitor
Drop the visits table (optional):
If you want to clean up the database, create a migration:
Schema::dropIfExists('visits');
Update custom dashboards/reports:
If you built custom dashboards or reports using visitor data, replace them with an external analytics solution (e.g., Google Analytics, Plausible, Matomo).
Remove the visitor config check:
If your code references core()->getConfigData('general.general.visitor_options.enabled'), remove it. The system configuration field no longer exists.
Impact Probability: Medium
Bagisto v2.4 has migrated the Magic AI feature from direct OpenAI integration to the Laravel AI SDK (laravel/ai), introducing a unified multi-provider architecture.
v2.3:
config/openai.phpv2.4:
laravel/ai SDK supporting 8 providers: Anthropic, DeepSeek, Gemini, Groq, Mistral, Ollama, OpenAI, xAIWebkul\MagicAI\Enums\Models\Webkul\MagicAI\AiProviderconfig/ai.phpRun Composer update to install the laravel/ai dependency (handled automatically).
If you have custom Magic AI code:
Update any direct references to OpenAI-specific classes to use the new AiProvider unified interface:
- use OpenAI\Client;
+ use Webkul\MagicAI\AiProvider;
Update AI configuration in Admin > Configuration > Magic AI to select your preferred provider and model.