diff --git a/backend/.phpstorm.meta.php b/backend/.phpstorm.meta.php index 1ccc6df..4e01625 100644 --- a/backend/.phpstorm.meta.php +++ b/backend/.phpstorm.meta.php @@ -1157,6 +1157,8 @@ 'services.ses.region' => 'string', 'services.slack.notifications.bot_user_oauth_token' => 'NULL', 'services.slack.notifications.channel' => 'NULL', + 'services.stripe.secret' => 'string', + 'services.stripe.webhook' => 'string', 'session.driver' => 'string', 'session.lifetime' => 'integer', 'session.expire_on_close' => 'boolean', @@ -1597,6 +1599,8 @@ 'services.ses.region' => 'string', 'services.slack.notifications.bot_user_oauth_token' => 'NULL', 'services.slack.notifications.channel' => 'NULL', + 'services.stripe.secret' => 'string', + 'services.stripe.webhook' => 'string', 'session.driver' => 'string', 'session.lifetime' => 'integer', 'session.expire_on_close' => 'boolean', @@ -2037,6 +2041,8 @@ 'services.ses.region' => 'string', 'services.slack.notifications.bot_user_oauth_token' => 'NULL', 'services.slack.notifications.channel' => 'NULL', + 'services.stripe.secret' => 'string', + 'services.stripe.webhook' => 'string', 'session.driver' => 'string', 'session.lifetime' => 'integer', 'session.expire_on_close' => 'boolean', @@ -2204,17 +2210,17 @@ 'queue.batching.table','queue.failed.driver','queue.failed.database','queue.failed.table','sanctum.stateful', 'sanctum.guard','sanctum.expiration','sanctum.token_prefix','sanctum.middleware.authenticate_session','sanctum.middleware.encrypt_cookies', 'sanctum.middleware.validate_csrf_token','services.postmark.key','services.resend.key','services.ses.key','services.ses.secret', -'services.ses.region','services.slack.notifications.bot_user_oauth_token','services.slack.notifications.channel','session.driver','session.lifetime', -'session.expire_on_close','session.encrypt','session.files','session.connection','session.table', -'session.store','session.lottery','session.cookie','session.path','session.domain', -'session.secure','session.http_only','session.same_site','session.partitioned','ide-helper.filename', -'ide-helper.models_filename','ide-helper.meta_filename','ide-helper.include_fluent','ide-helper.include_factory_builders','ide-helper.write_model_magic_where', -'ide-helper.write_model_external_builder_methods','ide-helper.write_model_relation_count_properties','ide-helper.write_model_relation_exists_properties','ide-helper.write_eloquent_model_mixins','ide-helper.include_helpers', -'ide-helper.helper_files','ide-helper.model_locations','ide-helper.ignored_models','ide-helper.model_hooks','ide-helper.extra.Eloquent', -'ide-helper.extra.Session','ide-helper.magic','ide-helper.interfaces','ide-helper.model_camel_case_properties','ide-helper.type_overrides.integer', -'ide-helper.type_overrides.boolean','ide-helper.include_class_docblocks','ide-helper.force_fqn','ide-helper.use_generics_annotations','ide-helper.macro_default_return_types.Illuminate\\Http\\Client\\Factory', -'ide-helper.additional_relation_types','ide-helper.additional_relation_return_types','ide-helper.enforce_nullable_relationships','ide-helper.post_migrate','tinker.commands', -'tinker.alias','tinker.dont_alias','tinker.trust_project',); +'services.ses.region','services.slack.notifications.bot_user_oauth_token','services.slack.notifications.channel','services.stripe.secret','services.stripe.webhook', +'session.driver','session.lifetime','session.expire_on_close','session.encrypt','session.files', +'session.connection','session.table','session.store','session.lottery','session.cookie', +'session.path','session.domain','session.secure','session.http_only','session.same_site', +'session.partitioned','ide-helper.filename','ide-helper.models_filename','ide-helper.meta_filename','ide-helper.include_fluent', +'ide-helper.include_factory_builders','ide-helper.write_model_magic_where','ide-helper.write_model_external_builder_methods','ide-helper.write_model_relation_count_properties','ide-helper.write_model_relation_exists_properties', +'ide-helper.write_eloquent_model_mixins','ide-helper.include_helpers','ide-helper.helper_files','ide-helper.model_locations','ide-helper.ignored_models', +'ide-helper.model_hooks','ide-helper.extra.Eloquent','ide-helper.extra.Session','ide-helper.magic','ide-helper.interfaces', +'ide-helper.model_camel_case_properties','ide-helper.type_overrides.integer','ide-helper.type_overrides.boolean','ide-helper.include_class_docblocks','ide-helper.force_fqn', +'ide-helper.use_generics_annotations','ide-helper.macro_default_return_types.Illuminate\\Http\\Client\\Factory','ide-helper.additional_relation_types','ide-helper.additional_relation_return_types','ide-helper.enforce_nullable_relationships', +'ide-helper.post_migrate','tinker.commands','tinker.alias','tinker.dont_alias','tinker.trust_project',); registerArgumentsSet('middleware', 'web','api','auth','auth.basic','auth.session', 'cache.headers','can','guest','password.confirm','precognitive', @@ -2222,9 +2228,8 @@ registerArgumentsSet('routes', 'sanctum.csrf-cookie','cart.show','cart.update','cart.store','cart.destroy', 'user.addresses.index','user.addresses.store','addresses.show','addresses.update','addresses.destroy', -'users.orders.index','users.orders.store','orders.show','orders.update','orders.destroy','products.index','products.store','products.show','products.update', -'products.destroy', -'storage.local','storage.local.upload',); +'users.orders.index','users.orders.store','orders.show','orders.update','orders.destroy','products.index','products.store','products.show', +'products.update','products.destroy','storage.local','storage.local.upload',); registerArgumentsSet('views', 'laravel-exceptions-renderer::components.badge','laravel-exceptions-renderer::components.empty-state','laravel-exceptions-renderer::components.file-with-line','laravel-exceptions-renderer::components.formatted-source','laravel-exceptions-renderer::components.frame', 'laravel-exceptions-renderer::components.frame-code','laravel-exceptions-renderer::components.header','laravel-exceptions-renderer::components.http-method','laravel-exceptions-renderer::components.icons.alert','laravel-exceptions-renderer::components.icons.check', @@ -2279,7 +2284,8 @@ 'REDIS_CLIENT','REDIS_HOST','REDIS_PASSWORD','REDIS_PORT','MAIL_MAILER', 'MAIL_SCHEME','MAIL_HOST','MAIL_PORT','MAIL_USERNAME','MAIL_PASSWORD', 'MAIL_FROM_ADDRESS','MAIL_FROM_NAME','AWS_ACCESS_KEY_ID','AWS_SECRET_ACCESS_KEY','AWS_DEFAULT_REGION', -'AWS_BUCKET','AWS_USE_PATH_STYLE_ENDPOINT','VITE_APP_NAME','FRONTEND_URL','SANCTUM_STATEFUL_DOMAINS',); +'AWS_BUCKET','AWS_USE_PATH_STYLE_ENDPOINT','VITE_APP_NAME','FRONTEND_URL','SANCTUM_STATEFUL_DOMAINS', +'STRIPE_SECRET_KEY','STRIPE_WEBHOOK_KEY',); expectedArguments(\Illuminate\Support\Facades\Gate::has(), 0, argumentsSet('auth')); expectedArguments(\Illuminate\Support\Facades\Gate::allows(), 0, argumentsSet('auth')); diff --git a/backend/_ide_helper_models.php b/backend/_ide_helper_models.php index 0d51db2..acc319e 100644 --- a/backend/_ide_helper_models.php +++ b/backend/_ide_helper_models.php @@ -84,6 +84,7 @@ class IdeHelperCart {} * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \App\Models\Cart|null $cart + * @property-read \App\Models\StripeSession|null $stripeSession * @property-read \App\Models\User $user * @method static \Illuminate\Database\Eloquent\Builder|Order newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Order newQuery() @@ -106,6 +107,55 @@ class IdeHelperCart {} class IdeHelperOrder {} } +namespace App\Models{ +/** + * @property int $id + * @property int $order_id + * @property string $transaction_id + * @property int $amount + * @property string $currency + * @property string $payment_method + * @property int $payment_status_id + * @property string|null $error_message + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\Order|null $order + * @property-read \App\Models\PaymentStatus|null $paymentStatus + * @property-read mixed $status + * @method static \Illuminate\Database\Eloquent\Builder|Payment newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Payment newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Payment query() + * @method static \Illuminate\Database\Eloquent\Builder|Payment whereAmount($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCurrency($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment whereErrorMessage($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment whereOrderId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment wherePaymentMethod($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment wherePaymentStatusId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment whereTransactionId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment whereUpdatedAt($value) + * @mixin \Eloquent + */ + #[\AllowDynamicProperties] + class IdeHelperPayment {} +} + +namespace App\Models{ +/** + * @property int $id + * @property string $name + * @method static \Illuminate\Database\Eloquent\Builder|PaymentStatus newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|PaymentStatus newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|PaymentStatus query() + * @method static \Illuminate\Database\Eloquent\Builder|PaymentStatus whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|PaymentStatus whereName($value) + * @mixin \Eloquent + */ + #[\AllowDynamicProperties] + class IdeHelperPaymentStatus {} +} + namespace App\Models{ /** * @property int $id @@ -188,6 +238,28 @@ class IdeHelperProductCategory {} class IdeHelperProductImage {} } +namespace App\Models{ +/** + * @property int $id + * @property string $session_id + * @property int $order_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\Order|null $order + * @method static \Illuminate\Database\Eloquent\Builder|StripeSession newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|StripeSession newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|StripeSession query() + * @method static \Illuminate\Database\Eloquent\Builder|StripeSession whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|StripeSession whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|StripeSession whereOrderId($value) + * @method static \Illuminate\Database\Eloquent\Builder|StripeSession whereSessionId($value) + * @method static \Illuminate\Database\Eloquent\Builder|StripeSession whereUpdatedAt($value) + * @mixin \Eloquent + */ + #[\AllowDynamicProperties] + class IdeHelperStripeSession {} +} + namespace App\Models{ /** * @property int $id diff --git a/backend/app/Models/Order.php b/backend/app/Models/Order.php index 5c61f1e..4cfdc57 100644 --- a/backend/app/Models/Order.php +++ b/backend/app/Models/Order.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; /** @@ -21,13 +22,26 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } - public function cart(): HasOne + public function cart(): BelongsTo { - return $this->hasOne(Cart::class); + return $this->belongsTo(Cart::class); } + /** + * Stripe session id helps to update status later from webhook + * + * @return HasOne + */ public function stripeSession(): HasOne { return $this->hasOne(StripeSession::class); } + + /** + * @return HasMany + */ + public function payments(): HasMany + { + return $this->hasMany(Payment::class); + } } diff --git a/backend/app/Models/StripeSession.php b/backend/app/Models/StripeSession.php index bc54e88..d8b17c7 100644 --- a/backend/app/Models/StripeSession.php +++ b/backend/app/Models/StripeSession.php @@ -5,6 +5,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasOne; +/** + * @mixin IdeHelperStripeSession + */ class StripeSession extends Model { public $fillable = ['session_id', 'order_id']; diff --git a/backend/composer.json b/backend/composer.json index 352f37e..74cc658 100644 --- a/backend/composer.json +++ b/backend/composer.json @@ -12,7 +12,8 @@ "php": "^8.2", "laravel/framework": "^12.0", "laravel/sanctum": "^4.0", - "laravel/tinker": "^2.10.1" + "laravel/tinker": "^2.10.1", + "stripe/stripe-php": "^19.4" }, "require-dev": { "barryvdh/laravel-ide-helper": "^3.6", diff --git a/backend/composer.lock b/backend/composer.lock index 08f82f3..7e54d11 100644 --- a/backend/composer.lock +++ b/backend/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "eb492a5715ccca89342113f679f1a050", + "content-hash": "68eb84c37a2e900c5295571a84d50566", "packages": [ { "name": "brick/math", @@ -3358,6 +3358,65 @@ }, "time": "2025-12-14T04:43:48+00:00" }, + { + "name": "stripe/stripe-php", + "version": "v19.4.1", + "source": { + "type": "git", + "url": "https://github.com/stripe/stripe-php.git", + "reference": "095384404587d07de2ad1154c389c4051c5ed92f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stripe/stripe-php/zipball/095384404587d07de2ad1154c389c4051c5ed92f", + "reference": "095384404587d07de2ad1154c389c4051c5ed92f", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=5.6.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.94.0", + "phpstan/phpstan": "^1.2", + "phpunit/phpunit": "^5.7 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Stripe\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stripe and contributors", + "homepage": "https://github.com/stripe/stripe-php/contributors" + } + ], + "description": "Stripe PHP Library", + "homepage": "https://stripe.com/", + "keywords": [ + "api", + "payment processing", + "stripe" + ], + "support": { + "issues": "https://github.com/stripe/stripe-php/issues", + "source": "https://github.com/stripe/stripe-php/tree/v19.4.1" + }, + "time": "2026-03-06T22:53:13+00:00" + }, { "name": "symfony/clock", "version": "v8.0.0",