input('coupons'); $definitions = collect(is_array($payload) ? $payload : []) ->map(fn ($definition) => $this->normalizeDefinition($definition)) ->filter() ->values(); if ($definitions->isEmpty()) { $definitions = collect($this->defaultDefinitions()); } $created = $definitions->map(function (array $definition) { $coupon = Coupon::updateOrCreate( ['code' => strtoupper($definition['code'])], [ 'name' => $definition['name'] ?? $definition['code'], 'type' => CouponType::from($definition['type']), 'status' => CouponStatus::from($definition['status'] ?? CouponStatus::ACTIVE->value), 'amount' => $definition['amount'], 'currency' => $definition['currency'], 'description' => $definition['description'] ?? null, 'enabled_for_checkout' => $definition['enabled_for_checkout'], 'auto_apply' => $definition['auto_apply'], 'is_stackable' => $definition['is_stackable'], 'usage_limit' => $definition['usage_limit'], 'per_customer_limit' => $definition['per_customer_limit'], 'starts_at' => $definition['starts_at'], 'ends_at' => $definition['ends_at'], ] ); if (array_key_exists('packages', $definition)) { $coupon->packages()->sync($definition['packages']); } return $coupon->fresh(['packages']); }); return response()->json([ 'data' => $created->map(fn (Coupon $coupon) => [ 'id' => $coupon->id, 'code' => $coupon->code, 'type' => $coupon->type->value, 'status' => $coupon->status->value, ]), ]); } private function normalizeDefinition(array $definition): array { $code = strtoupper((string) ($definition['code'] ?? '')); $type = $definition['type'] ?? CouponType::PERCENTAGE->value; if ($code === '') { throw ValidationException::withMessages(['code' => 'Coupon code is required.']); } $amount = (float) ($definition['amount'] ?? 0); if ($amount <= 0) { throw ValidationException::withMessages(['amount' => 'Coupon amount must be greater than zero.']); } $currency = $definition['currency'] ?? null; if ($type !== CouponType::PERCENTAGE->value && ! $currency) { $currency = 'EUR'; } return [ 'code' => $code, 'name' => $definition['name'] ?? null, 'type' => $type, 'status' => $definition['status'] ?? CouponStatus::ACTIVE->value, 'amount' => $amount, 'currency' => $currency ? strtoupper((string) $currency) : null, 'description' => $definition['description'] ?? null, 'enabled_for_checkout' => Arr::get($definition, 'enabled_for_checkout', true), 'auto_apply' => Arr::get($definition, 'auto_apply', false), 'is_stackable' => Arr::get($definition, 'is_stackable', false), 'usage_limit' => Arr::get($definition, 'usage_limit'), 'per_customer_limit' => Arr::get($definition, 'per_customer_limit'), 'starts_at' => $this->parseDate($definition['starts_at'] ?? null), 'ends_at' => $this->parseDate($definition['ends_at'] ?? null), 'packages' => Arr::get($definition, 'packages'), ]; } private function parseDate(mixed $value): ?Carbon { if (! $value) { return null; } if ($value instanceof Carbon) { return $value; } return Carbon::parse($value); } private function defaultDefinitions(): array { return [ [ 'code' => 'PERCENT10', 'name' => '10% off', 'type' => CouponType::PERCENTAGE->value, 'amount' => 10, 'description' => '10% discount for package flows', 'usage_limit' => 500, ], [ 'code' => 'FLAT50', 'name' => '50 EUR off', 'type' => CouponType::FLAT->value, 'amount' => 50, 'currency' => 'EUR', 'description' => '50€ discount on qualifying packages', 'usage_limit' => 200, ], [ 'code' => 'EXPIRED25', 'name' => 'Expired 25%', 'type' => CouponType::PERCENTAGE->value, 'amount' => 25, 'description' => 'Expired coupon for error handling tests', 'starts_at' => now()->subWeeks(2), 'ends_at' => now()->subDays(2), ], ]; } }