@fleetbase/registry-bridge-engine 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/addon/controllers/application.js +6 -1
  2. package/addon/controllers/developers/payments/settings.js +58 -0
  3. package/addon/routes/developers/payments/settings.js +23 -0
  4. package/addon/routes.js +1 -0
  5. package/addon/templates/application.hbs +28 -26
  6. package/addon/templates/developers/payments/index.hbs +9 -3
  7. package/addon/templates/developers/payments/onboard.hbs +1 -1
  8. package/addon/templates/developers/payments/settings.hbs +30 -0
  9. package/composer.json +1 -1
  10. package/config/environment.js +23 -0
  11. package/extension.json +1 -1
  12. package/package.json +3 -3
  13. package/server/migrations/2026_02_15_000001_create_registry_developer_accounts_table.php +44 -0
  14. package/server/migrations/2026_02_15_000002_add_account_type_to_registry_users_table.php +48 -0
  15. package/server/migrations/2026_02_15_000003_add_publisher_fields_to_registry_extensions_table.php +34 -0
  16. package/server/migrations/2026_02_15_100001_convert_purchases_to_polymorphic.php +45 -0
  17. package/server/migrations/2026_02_15_100002_add_purchaser_indexes.php +35 -0
  18. package/server/src/Http/Controllers/Internal/v1/RegistryAuthController.php +79 -29
  19. package/server/src/Http/Controllers/Internal/v1/RegistryDeveloperAccountController.php +355 -0
  20. package/server/src/Http/Controllers/Internal/v1/RegistryExtensionController.php +28 -0
  21. package/server/src/Http/Controllers/Internal/v1/RegistryPaymentsController.php +74 -3
  22. package/server/src/Models/RegistryDeveloperAccount.php +147 -0
  23. package/server/src/Models/RegistryExtensionPurchase.php +17 -0
  24. package/server/src/Models/RegistryUser.php +85 -14
  25. package/server/src/routes.php +19 -5
  26. package/translations/en-us.yaml +7 -0
@@ -8,6 +8,7 @@ use Fleetbase\RegistryBridge\Http\Requests\AddRegistryUserRequest;
8
8
  use Fleetbase\RegistryBridge\Http\Requests\AuthenticateRegistryUserRequest;
9
9
  use Fleetbase\RegistryBridge\Http\Requests\RegistryAuthRequest;
10
10
  use Fleetbase\RegistryBridge\Http\Resources\RegistryUser as RegistryUserResource;
11
+ use Fleetbase\RegistryBridge\Models\RegistryDeveloperAccount;
11
12
  use Fleetbase\RegistryBridge\Models\RegistryExtension;
12
13
  use Fleetbase\RegistryBridge\Models\RegistryUser;
13
14
  use Fleetbase\RegistryBridge\Support\Bridge;
@@ -88,34 +89,59 @@ class RegistryAuthController extends Controller
88
89
  $identity = $request->input('identity');
89
90
  $password = $request->input('password');
90
91
 
91
- // Find user by email or username
92
+ // First, try to find a cloud user
92
93
  $user = User::where(function ($query) use ($identity) {
93
94
  $query->where('email', $identity)->orWhere('phone', $identity)->orWhere('username', $identity);
94
95
  })->first();
95
96
 
96
- // Authenticate user with password
97
- if (Auth::isInvalidPassword($password, $user->password)) {
97
+ if ($user && Auth::isValidPassword($password, $user->password)) {
98
+ // Cloud user authentication
99
+ $registryUser = RegistryUser::firstOrCreate(
100
+ [
101
+ 'company_uuid' => $user->company_uuid,
102
+ 'user_uuid' => $user->uuid,
103
+ ],
104
+ [
105
+ 'account_type' => 'cloud',
106
+ 'scope' => '*',
107
+ 'expires_at' => now()->addYear(),
108
+ 'name' => $user->public_id . ' developer token',
109
+ ]
110
+ );
111
+
112
+ return new RegistryUserResource($registryUser);
113
+ }
114
+
115
+ // If not a cloud user, try Registry Developer Account
116
+ $developerAccount = RegistryDeveloperAccount::where(function ($query) use ($identity) {
117
+ $query->where('email', $identity)->orWhere('username', $identity);
118
+ })->first();
119
+
120
+ if (!$developerAccount) {
98
121
  return response()->error('Invalid credentials.', 401);
99
122
  }
100
123
 
101
- // Get existing token for current user
102
- $registryUser = RegistryUser::where(['company_uuid' => $user->company_uuid, 'user_uuid' => $user->uuid])->first();
103
- if (!$registryUser) {
104
- // Create registry user
105
- $registryUser = RegistryUser::create([
106
- 'company_uuid' => $user->company_uuid,
107
- 'user_uuid' => $user->uuid,
108
- 'scope' => '*',
109
- 'expires_at' => now()->addYear(),
110
- 'name' => $user->public_id . ' developer token',
111
- ]);
124
+ if ($developerAccount->status !== 'active') {
125
+ return response()->error('Account is not active. Please verify your email.', 401);
112
126
  }
113
127
 
114
- // If no token response with error
115
- if (!$registryUser) {
116
- return response()->error('Unable to authenticate.');
128
+ if (Auth::isInvalidPassword($password, $developerAccount->password)) {
129
+ return response()->error('Invalid credentials.', 401);
117
130
  }
118
131
 
132
+ // Developer account authentication
133
+ $registryUser = RegistryUser::firstOrCreate(
134
+ [
135
+ 'developer_account_uuid' => $developerAccount->uuid,
136
+ ],
137
+ [
138
+ 'account_type' => 'developer',
139
+ 'scope' => '*',
140
+ 'expires_at' => now()->addYear(),
141
+ 'name' => $developerAccount->username . ' developer token',
142
+ ]
143
+ );
144
+
119
145
  return new RegistryUserResource($registryUser);
120
146
  }
121
147
 
@@ -266,29 +292,53 @@ class RegistryAuthController extends Controller
266
292
 
267
293
  // Find package
268
294
  $extension = RegistryExtension::findByPackageName($package);
295
+
269
296
  if (!$extension) {
270
- return response()->error('Attempting to publish extension which has no record.', 401);
297
+ // First time publishing - create extension record
298
+ $publisherUuid = $registryUser->isDeveloperAccount()
299
+ ? $registryUser->developer_account_uuid
300
+ : $registryUser->company_uuid;
301
+
302
+ $extension = RegistryExtension::create([
303
+ 'uuid' => (string) Str::uuid(),
304
+ 'package_name' => $package,
305
+ 'publisher_type' => $registryUser->account_type,
306
+ 'publisher_uuid' => $publisherUuid,
307
+ 'company_uuid' => $registryUser->company_uuid, // Keep for backwards compatibility
308
+ 'status' => 'published',
309
+ 'payment_required' => false, // Default to free
310
+ ]);
311
+
312
+ return response()->json(['allowed' => true]);
271
313
  }
272
314
 
273
- // If user is not admin respond with error
274
- // For now only admin is allowed to publish to registry
275
- // This may change in the future with approval/reject flow
276
- if ($registryUser->isNotAdmin()) {
277
- return response()->error('User is not allowed publish to the registry.', 401);
315
+ // Check ownership
316
+ if ($registryUser->isCloudAccount()) {
317
+ if ($extension->publisher_type === 'cloud' && $extension->publisher_uuid !== $registryUser->company_uuid) {
318
+ return response()->error('You do not own this extension.', 403);
319
+ }
320
+ } elseif ($registryUser->isDeveloperAccount()) {
321
+ if ($extension->publisher_type === 'developer' && $extension->publisher_uuid !== $registryUser->developer_account_uuid) {
322
+ return response()->error('You do not own this extension.', 403);
323
+ }
278
324
  }
279
325
 
280
- // Extension should be approved
281
- if (!in_array($extension->status, ['approved', 'published'])) {
282
- return response()->error('Attempting to publish extension which is not approved.', 401);
326
+ // If publisher types don't match, deny access
327
+ if ($extension->publisher_type !== $registryUser->account_type) {
328
+ return response()->error('Account type mismatch for this extension.', 403);
283
329
  }
284
330
 
285
- // Change status to published
331
+ // Update extension status
286
332
  if ($action === 'publish') {
287
333
  $extension->update(['status' => 'published']);
288
- $extension->currentBundle()->update(['status' => 'published']);
334
+ if ($extension->currentBundle) {
335
+ $extension->currentBundle->update(['status' => 'published']);
336
+ }
289
337
  } elseif ($action === 'unpublish') {
290
338
  $extension->update(['status' => 'unpublished']);
291
- $extension->currentBundle()->update(['status' => 'unpublished']);
339
+ if ($extension->currentBundle) {
340
+ $extension->currentBundle->update(['status' => 'unpublished']);
341
+ }
292
342
  }
293
343
 
294
344
  // Passed all checks
@@ -0,0 +1,355 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\RegistryBridge\Http\Controllers\Internal\v1;
4
+
5
+ use Fleetbase\Http\Controllers\Controller;
6
+ use Fleetbase\Models\VerificationCode;
7
+ use Fleetbase\RegistryBridge\Models\RegistryDeveloperAccount;
8
+ use Fleetbase\RegistryBridge\Models\RegistryUser;
9
+ use Illuminate\Http\Request;
10
+ use Illuminate\Support\Facades\Hash;
11
+ use Illuminate\Support\Facades\Validator;
12
+ use Illuminate\Support\Str;
13
+
14
+ class RegistryDeveloperAccountController extends Controller
15
+ {
16
+ /**
17
+ * Register a new Registry Developer Account.
18
+ *
19
+ * @return \Illuminate\Http\JsonResponse
20
+ */
21
+ public function register(Request $request)
22
+ {
23
+ $validator = Validator::make($request->all(), [
24
+ 'username' => 'required|string|min:3|max:255|unique:registry_developer_accounts,username|regex:/^[a-zA-Z0-9_-]+$/',
25
+ 'email' => 'required|email|unique:registry_developer_accounts,email',
26
+ 'password' => 'required|string|min:8',
27
+ 'name' => 'nullable|string|max:255',
28
+ ]);
29
+
30
+ if ($validator->fails()) {
31
+ return response()->json([
32
+ 'errors' => $validator->errors(),
33
+ ], 422);
34
+ }
35
+
36
+ $validated = $validator->validated();
37
+
38
+ $account = RegistryDeveloperAccount::create([
39
+ 'uuid' => (string) Str::uuid(),
40
+ 'username' => $validated['username'],
41
+ 'email' => $validated['email'],
42
+ 'password' => Hash::make($validated['password']),
43
+ 'name' => $validated['name'] ?? $validated['username'],
44
+ 'status' => 'pending_verification',
45
+ 'verification_token' => Str::random(64),
46
+ ]);
47
+
48
+ // Send verification email
49
+ $this->sendVerificationEmail($account);
50
+
51
+ return response()->json([
52
+ 'status' => 'success',
53
+ 'message' => 'Account created successfully. Please check your email to verify your account.',
54
+ 'account' => [
55
+ 'uuid' => $account->uuid,
56
+ 'username' => $account->username,
57
+ 'email' => $account->email,
58
+ ],
59
+ ], 201);
60
+ }
61
+
62
+ /**
63
+ * Verify email address using verification code.
64
+ *
65
+ * @return \Illuminate\Http\JsonResponse
66
+ */
67
+ public function verifyEmail(Request $request)
68
+ {
69
+ $code = $request->input('code');
70
+ $email = $request->input('email');
71
+
72
+ if (!$code || !$email) {
73
+ return response()->error('Verification code and email are required.', 400);
74
+ }
75
+
76
+ // Find the account
77
+ $account = RegistryDeveloperAccount::where('email', $email)->first();
78
+
79
+ if (!$account) {
80
+ return response()->error('Account not found.', 404);
81
+ }
82
+
83
+ if ($account->isActive()) {
84
+ return response()->json([
85
+ 'status' => 'success',
86
+ 'message' => 'Email already verified.',
87
+ ]);
88
+ }
89
+
90
+ // Find the verification code
91
+ $verificationCode = VerificationCode::where('subject_uuid', $account->uuid)
92
+ ->where('subject_type', RegistryDeveloperAccount::class)
93
+ ->where('for', 'registry_developer_account_verification')
94
+ ->where('code', $code)
95
+ ->whereIn('status', ['pending', null]) // Support both pending and NULL status
96
+ ->first();
97
+
98
+ if (!$verificationCode) {
99
+ return response()->error('Invalid or expired verification code.', 400);
100
+ }
101
+
102
+ // Check if code is expired
103
+ if ($verificationCode->hasExpired()) {
104
+ return response()->error('Verification code has expired.', 400);
105
+ }
106
+
107
+ // Mark as verified
108
+ $account->markEmailAsVerified();
109
+ $verificationCode->update(['status' => 'used']);
110
+
111
+ // Generate registry token for the developer account
112
+ $token = RegistryUser::generateToken();
113
+
114
+ $registryUser = RegistryUser::firstOrCreate(
115
+ [
116
+ 'developer_account_uuid' => $account->uuid,
117
+ 'account_type' => 'developer',
118
+ ],
119
+ [
120
+ 'token' => $token,
121
+ 'name' => $account->name,
122
+ ]
123
+ );
124
+
125
+ // If registry user already exists, update the token
126
+ if (!$registryUser->wasRecentlyCreated) {
127
+ $registryUser->update(['token' => $token]);
128
+ }
129
+
130
+ return response()->json([
131
+ 'status' => 'success',
132
+ 'message' => 'Email verified successfully. You can now log in.',
133
+ 'token' => $token,
134
+ ]);
135
+ }
136
+
137
+ /**
138
+ * Resend verification email.
139
+ *
140
+ * @return \Illuminate\Http\JsonResponse
141
+ */
142
+ public function resendVerification(Request $request)
143
+ {
144
+ $email = $request->input('email');
145
+
146
+ if (!$email) {
147
+ return response()->error('Email is required.', 400);
148
+ }
149
+
150
+ $account = RegistryDeveloperAccount::where('email', $email)->first();
151
+
152
+ if (!$account) {
153
+ return response()->error('Account not found.', 404);
154
+ }
155
+
156
+ if ($account->isActive()) {
157
+ return response()->json([
158
+ 'status' => 'success',
159
+ 'message' => 'Email already verified.',
160
+ ]);
161
+ }
162
+
163
+ // Generate new token
164
+ $account->generateVerificationToken();
165
+
166
+ // Resend verification email
167
+ $this->sendVerificationEmail($account);
168
+
169
+ return response()->json([
170
+ 'status' => 'success',
171
+ 'message' => 'Verification email sent.',
172
+ ]);
173
+ }
174
+
175
+ /**
176
+ * Get account profile.
177
+ *
178
+ * @return \Illuminate\Http\JsonResponse
179
+ */
180
+ public function profile(Request $request)
181
+ {
182
+ // This would require middleware to authenticate the user
183
+ // For now, we'll accept a token parameter
184
+ $token = $request->bearerToken() ?? $request->input('token');
185
+
186
+ if (!$token) {
187
+ return response()->error('Authentication required.', 401);
188
+ }
189
+
190
+ $registryUser = RegistryUser::findFromToken($token);
191
+
192
+ if (!$registryUser || $registryUser->account_type !== 'developer') {
193
+ return response()->error('Invalid token or not a developer account.', 403);
194
+ }
195
+
196
+ $account = $registryUser->developerAccount;
197
+
198
+ if (!$account) {
199
+ return response()->error('Developer account not found.', 404);
200
+ }
201
+
202
+ return response()->json([
203
+ 'uuid' => $account->uuid,
204
+ 'username' => $account->username,
205
+ 'email' => $account->email,
206
+ 'name' => $account->name,
207
+ 'avatar_url' => $account->avatar_url,
208
+ 'github_username' => $account->github_username,
209
+ 'website' => $account->website,
210
+ 'bio' => $account->bio,
211
+ 'status' => $account->status,
212
+ 'email_verified_at' => $account->email_verified_at,
213
+ 'created_at' => $account->created_at,
214
+ ]);
215
+ }
216
+
217
+ /**
218
+ * Update account profile.
219
+ *
220
+ * @return \Illuminate\Http\JsonResponse
221
+ */
222
+ public function updateProfile(Request $request)
223
+ {
224
+ $token = $request->bearerToken() ?? $request->input('token');
225
+
226
+ if (!$token) {
227
+ return response()->error('Authentication required.', 401);
228
+ }
229
+
230
+ $registryUser = RegistryUser::findFromToken($token);
231
+
232
+ if (!$registryUser || $registryUser->account_type !== 'developer') {
233
+ return response()->error('Invalid token or not a developer account.', 403);
234
+ }
235
+
236
+ $account = $registryUser->developerAccount;
237
+
238
+ if (!$account) {
239
+ return response()->error('Developer account not found.', 404);
240
+ }
241
+
242
+ $validator = Validator::make($request->all(), [
243
+ 'name' => 'nullable|string|max:255',
244
+ 'avatar_url' => 'nullable|url|max:500',
245
+ 'github_username' => 'nullable|string|max:255',
246
+ 'website' => 'nullable|url|max:500',
247
+ 'bio' => 'nullable|string|max:1000',
248
+ ]);
249
+
250
+ if ($validator->fails()) {
251
+ return response()->json([
252
+ 'errors' => $validator->errors(),
253
+ ], 422);
254
+ }
255
+
256
+ $account->update($validator->validated());
257
+
258
+ return response()->json([
259
+ 'status' => 'success',
260
+ 'message' => 'Profile updated successfully.',
261
+ 'account' => $account,
262
+ ]);
263
+ }
264
+
265
+ /**
266
+ * Generate or regenerate registry token for authenticated developer account.
267
+ *
268
+ * @return \Illuminate\Http\JsonResponse
269
+ */
270
+ public function generateToken(Request $request)
271
+ {
272
+ $email = $request->input('email');
273
+ $password = $request->input('password');
274
+
275
+ if (!$email || !$password) {
276
+ return response()->error('Email and password are required.', 400);
277
+ }
278
+
279
+ // Find the account
280
+ $account = RegistryDeveloperAccount::where('email', $email)->first();
281
+
282
+ if (!$account) {
283
+ return response()->error('Account not found.', 404);
284
+ }
285
+
286
+ // Verify password
287
+ if (!Hash::check($password, $account->password)) {
288
+ return response()->error('Invalid credentials.', 401);
289
+ }
290
+
291
+ // Check if account is active
292
+ if (!$account->isActive()) {
293
+ return response()->error('Account is not active. Please verify your email first.', 403);
294
+ }
295
+
296
+ // Generate new token
297
+ $token = RegistryUser::generateToken();
298
+
299
+ // Find or create registry user
300
+ $registryUser = RegistryUser::firstOrCreate(
301
+ [
302
+ 'developer_account_uuid' => $account->uuid,
303
+ 'account_type' => 'developer',
304
+ ],
305
+ [
306
+ 'token' => $token,
307
+ 'name' => $account->name,
308
+ ]
309
+ );
310
+
311
+ // If registry user already exists, regenerate the token
312
+ if (!$registryUser->wasRecentlyCreated) {
313
+ $registryUser->update(['token' => $token]);
314
+ }
315
+
316
+ return response()->json([
317
+ 'status' => 'success',
318
+ 'message' => $registryUser->wasRecentlyCreated ? 'Token generated successfully.' : 'Token regenerated successfully.',
319
+ 'token' => $token,
320
+ ]);
321
+ }
322
+
323
+ /**
324
+ * Send verification email to the account.
325
+ *
326
+ * @return void
327
+ */
328
+ private function sendVerificationEmail(RegistryDeveloperAccount $account)
329
+ {
330
+ try {
331
+ VerificationCode::generateEmailVerificationFor(
332
+ $account,
333
+ 'registry_developer_account_verification',
334
+ [
335
+ 'subject' => 'Verify your Registry Developer Account',
336
+ 'content' => function ($verificationCode) use ($account) {
337
+ return "Hello {$account->name},\n\n" .
338
+ "Thank you for registering a Registry Developer Account!\n\n" .
339
+ "Your verification code is: {$verificationCode->code}\n\n" .
340
+ "To verify your account, copy and paste this command into your terminal:\n\n" .
341
+ "flb verify -e {$account->email} -c {$verificationCode->code}\n\n" .
342
+ "This code will expire in 1 hour.\n\n" .
343
+ 'If you did not create this account, please ignore this email.';
344
+ },
345
+ ]
346
+ );
347
+ } catch (\Exception $e) {
348
+ // Log the error but don't fail registration
349
+ logger()->error('Failed to send verification email', [
350
+ 'email' => $account->email,
351
+ 'error' => $e->getMessage(),
352
+ ]);
353
+ }
354
+ }
355
+ }
@@ -23,6 +23,34 @@ class RegistryExtensionController extends RegistryBridgeController
23
23
  */
24
24
  public $resource = 'registry_extension';
25
25
 
26
+ /**
27
+ * Display a public list of all published extensions.
28
+ *
29
+ * This endpoint is publicly accessible and returns all extensions with a status of 'published'.
30
+ * The results are cached for 15 minutes to improve performance and reduce database load.
31
+ * This endpoint is designed to be called by self-hosted instances to discover available extensions.
32
+ *
33
+ * @param Request $request the incoming HTTP request
34
+ *
35
+ * @return \Illuminate\Http\JsonResponse the collection of published extensions
36
+ */
37
+ public function listPublicExtensions(Request $request)
38
+ {
39
+ $cacheKey = 'public-extensions-list';
40
+ $cacheTtl = now()->addMinutes(15);
41
+
42
+ $extensions = \Illuminate\Support\Facades\Cache::remember($cacheKey, $cacheTtl, function () {
43
+ return RegistryExtension::where('status', 'published')
44
+ ->with(['author', 'category', 'currentBundle'])
45
+ ->orderBy('install_count', 'desc')
46
+ ->get();
47
+ });
48
+
49
+ $this->resource::wrap('registryExtensions');
50
+
51
+ return $this->resource::collection($extensions);
52
+ }
53
+
26
54
  /**
27
55
  * Creates a record with request payload.
28
56
  *
@@ -162,25 +162,56 @@ class RegistryPaymentsController extends Controller
162
162
  $extension->flushCache();
163
163
  }
164
164
 
165
+ // Determine purchaser (Company or RegistryDeveloperAccount)
166
+ $purchaser = null;
167
+ $purchaserType = null;
168
+ $purchaserUuid = null;
169
+
170
+ // Check if authenticated via session (cloud user)
171
+ if (session('company')) {
172
+ $purchaserUuid = session('company');
173
+ $purchaserType = 'Fleetbase\\Models\\Company';
174
+ }
175
+ // Check if authenticated via bearer token (developer account)
176
+ elseif ($request->bearerToken()) {
177
+ $registryUser = \Fleetbase\RegistryBridge\Models\RegistryUser::where('token', $request->bearerToken())->first();
178
+ if ($registryUser && $registryUser->developer_account_uuid) {
179
+ $purchaserUuid = $registryUser->developer_account_uuid;
180
+ $purchaserType = 'Fleetbase\\RegistryBridge\\Models\\RegistryDeveloperAccount';
181
+ }
182
+ }
183
+
184
+ if (!$purchaserUuid || !$purchaserType) {
185
+ return response()->error('Unable to identify purchaser. Please ensure you are logged in.');
186
+ }
187
+
165
188
  // Check if already purchased
166
- $purchaseRecordExists = RegistryExtensionPurchase::where(['company_uuid' => session('company'), 'extension_uuid' => $extension->uuid])->exists();
189
+ $purchaseRecordExists = RegistryExtensionPurchase::where([
190
+ 'purchaser_uuid' => $purchaserUuid,
191
+ 'purchaser_type' => $purchaserType,
192
+ 'extension_uuid' => $extension->uuid,
193
+ ])->exists();
194
+
167
195
  if ($purchaseRecordExists) {
168
196
  return response()->json(['status' => 'purchase_complete', 'extension' => $extension]);
169
197
  }
170
198
 
171
- $stripe = Utils::getStripeClient();
199
+ $stripe = Utils::getStripeClient();
172
200
  try {
173
201
  $session = $stripe->checkout->sessions->retrieve($request->input('checkout_session_id'));
174
202
  if (isset($session->status) && $session->status === 'complete') {
175
203
  RegistryExtensionPurchase::firstOrCreate(
176
204
  [
177
- 'company_uuid' => session('company'),
205
+ 'purchaser_uuid' => $purchaserUuid,
206
+ 'purchaser_type' => $purchaserType,
178
207
  'extension_uuid' => $extension->uuid,
179
208
  ],
180
209
  [
181
210
  'stripe_checkout_session_id' => $session->id,
182
211
  'stripe_payment_intent_id' => $session->payment_intent,
183
212
  'locked_price' => $session->amount_total,
213
+ // Keep company_uuid for backward compatibility if it's a company
214
+ 'company_uuid' => $purchaserType === 'Fleetbase\\Models\\Company' ? $purchaserUuid : null,
184
215
  ]
185
216
  );
186
217
  }
@@ -224,4 +255,44 @@ class RegistryPaymentsController extends Controller
224
255
 
225
256
  return FleetbaseResource::collection($payments)->additional(['total_amount' => $totalPurchaseAmount]);
226
257
  }
258
+
259
+ /**
260
+ * Creates a Stripe account session for account management.
261
+ *
262
+ * This method creates a session that allows connected accounts to manage their account details,
263
+ * including updating bank account information, business details, and other settings.
264
+ *
265
+ * @param Request $request the incoming HTTP request
266
+ *
267
+ * @return \Illuminate\Http\JsonResponse returns a JSON response with the session's client secret or an error message
268
+ */
269
+ public function createAccountManagementSession(Request $request)
270
+ {
271
+ $stripe = Utils::getStripeClient();
272
+ $company = Auth::getCompany();
273
+
274
+ if (!$company || !$company->stripe_connect_id) {
275
+ return response()->error('Stripe Connect account not found for this company.');
276
+ }
277
+
278
+ try {
279
+ $accountSession = $stripe->accountSessions->create([
280
+ 'account' => $company->stripe_connect_id,
281
+ 'components' => [
282
+ 'account_management' => [
283
+ 'enabled' => true,
284
+ 'features' => [
285
+ 'external_account_collection' => true,
286
+ ],
287
+ ],
288
+ ],
289
+ ]);
290
+
291
+ return response()->json([
292
+ 'clientSecret' => $accountSession->client_secret,
293
+ ]);
294
+ } catch (\Exception $e) {
295
+ return response()->error($e->getMessage());
296
+ }
297
+ }
227
298
  }