attributes->get('service_caller') !== 'sms') { return response()->json(['message' => 'Forbidden.'], 403); } $maxKb = max(256, (int) config('transfer.sms_voice_max_upload_kb', 5120)); $data = $request->validate([ 'user' => ['required', 'string', 'max:64'], 'title' => ['nullable', 'string', 'max:120'], 'voice' => ['required', 'file', 'mimes:mp3,wav', 'max:'.$maxKb], ]); try { $user = $this->users->resolve((string) $data['user']); } catch (RuntimeException $e) { return response()->json(['message' => $e->getMessage()], 422); } $voice = $request->file('voice'); $filename = $voice?->getClientOriginalName() ?: 'voice.mp3'; $title = trim((string) ($data['title'] ?? '')); if ($title === '') { $title = Str::limit('SMS voice: '.$filename, 120, ''); } try { $transfer = $this->transfers->create($user, [ 'title' => $title, ], [$voice]); } catch (RuntimeException $e) { $payload = ['message' => $e->getMessage()]; if (TransferWalletErrors::isInsufficientBalance($e)) { $payload['error_code'] = TransferWalletErrors::CODE_INSUFFICIENT_WALLET; } return response()->json($payload, 422); } $transfer->load(['files', 'qrCode']); $file = $transfer->files->first(); if ($file === null) { return response()->json(['message' => 'Voice upload failed.'], 422); } return response()->json([ 'data' => [ 'direct_file_url' => DirectTransferFileUrl::for($transfer, $file), 'public_url' => $transfer->qrCode?->publicUrl(), 'transfer_id' => $transfer->id, 'file_id' => $file->id, 'paid_until' => $transfer->paid_until?->toIso8601String(), 'storage_gb' => $transfer->storageGb(), 'monthly_cost_ghs' => $transfer->monthlyCostGhs(), ], ], 201); } }