clientFactory = $clientFactory; $this->client = $clientFactory->make(); } public function createOrder(Request $request) { $request->validate([ 'package_id' => 'required|exists:packages,id', 'tenant_id' => 'nullable|exists:tenants,id', ]); $tenant = $request->tenant_id ? Tenant::findOrFail($request->tenant_id) : optional(Auth::user())->tenant; if (! $tenant) { return response()->json(['error' => 'Tenant context required for checkout.'], 422); } $package = Package::findOrFail($request->package_id); $ordersController = $this->client->getOrdersController(); $body = OrderRequestBuilder::init( CheckoutPaymentIntent::CAPTURE, [ PurchaseUnitRequestBuilder::init( AmountWithBreakdownBuilder::init('EUR', number_format($package->price, 2, '.', '')) ->build() ) ->description('Package: ' . $package->name) ->customId(json_encode([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'type' => 'endcustomer_event' ])) ->build() ] ) ->applicationContext( OrderApplicationContextBuilder::init() ->brandName('Fotospiel') ->landingPage('BILLING') ->build() ) ->build(); $collect = [ 'body' => $body, 'prefer' => 'return=representation' ]; try { $response = $ordersController->createOrder($collect); if ($response->getStatusCode() === 201) { $result = $response->getResult(); $approveLink = collect($result->links)->first(fn($link) => $link->rel === 'approve')?->href; return response()->json([ 'id' => $result->id, 'approve_url' => $approveLink, ]); } Log::error('PayPal order creation failed', ['response' => $response]); return response()->json(['error' => 'Order creation failed'], 400); } catch (\Exception $e) { Log::error('PayPal order creation exception', ['error' => $e->getMessage()]); return response()->json(['error' => 'Order creation failed'], 500); } } public function captureOrder(Request $request) { $request->validate(['order_id' => 'required']); $ordersController = $this->client->getOrdersController(); $collect = [ 'id' => $request->order_id, 'prefer' => 'return=representation' ]; try { $response = $ordersController->captureOrder($collect); if ($response->getStatusCode() === 201) { $result = $response->getResult(); $customId = $result->purchaseUnits[0]->customId ?? null; if ($customId) { $metadata = json_decode($customId, true); $tenantId = $metadata['tenant_id'] ?? null; $packageId = $metadata['package_id'] ?? null; $type = $metadata['type'] ?? 'endcustomer_event'; if ($tenantId && $packageId) { $tenant = Tenant::findOrFail($tenantId); $package = Package::findOrFail($packageId); PackagePurchase::create([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'provider_id' => $result->id, 'price' => $result->purchaseUnits[0]->amount->value, 'type' => $type, 'purchased_at' => now(), 'refunded' => false, ]); TenantPackage::create([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'price' => $package->price, 'purchased_at' => now(), 'active' => true, ]); $tenant->update(['subscription_status' => 'active']); } else { Log::error('Invalid metadata in PayPal custom_id', ['custom_id' => $customId]); } Log::info('PayPal order captured and purchase created: ' . $result->id); } return response()->json(['status' => 'captured', 'order' => $result]); } Log::error('PayPal order capture failed', ['response' => $response]); return response()->json(['error' => 'Capture failed'], 400); } catch (\Exception $e) { Log::error('PayPal order capture exception', ['error' => $e->getMessage()]); return response()->json(['error' => 'Capture failed'], 500); } } public function createSubscription(Request $request) { $request->validate([ 'package_id' => 'required|exists:packages,id', 'plan_id' => 'required|string', 'tenant_id' => 'nullable|exists:tenants,id', ]); $tenant = $request->tenant_id ? Tenant::findOrFail($request->tenant_id) : optional(Auth::user())->tenant; if (! $tenant) { return response()->json(['error' => 'Tenant context required for subscription checkout.'], 422); } $package = Package::findOrFail($request->package_id); $ordersController = $this->client->getOrdersController(); $storedPaymentSource = new \PaypalServerSdkLib\Models\StoredPaymentSource( 'CUSTOMER', 'RECURRING' ); $storedPaymentSource->setUsage('FIRST'); $paymentSource = new \PaypalServerSdkLib\Models\PaymentSource(); $paymentSource->storedPaymentSource = $storedPaymentSource; $body = OrderRequestBuilder::init( CheckoutPaymentIntent::CAPTURE, [ PurchaseUnitRequestBuilder::init( AmountWithBreakdownBuilder::init('EUR', number_format($package->price, 2, '.', '')) ->build() ) ->description('Subscription Package: ' . $package->name) ->customId(json_encode([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'type' => 'reseller_subscription', 'plan_id' => $request->plan_id ])) ->build() ] ) ->paymentSource($paymentSource) ->applicationContext( OrderApplicationContextBuilder::init() ->brandName('Fotospiel') ->landingPage('BILLING') ->build() ) ->build(); $collect = [ 'body' => $body, 'prefer' => 'return=representation' ]; try { $response = $ordersController->createOrder($collect); if ($response->getStatusCode() === 201) { $result = $response->getResult(); $orderId = $result->id; // Initial purchase record for subscription setup TenantPackage::create([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'price' => $package->price, 'purchased_at' => now(), 'expires_at' => now()->addYear(), // Assuming annual subscription 'active' => true, ]); PackagePurchase::create([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'provider_id' => $orderId . '_sub_' . $request->plan_id, // Combine for uniqueness 'price' => $package->price, 'type' => 'reseller_subscription', 'purchased_at' => now(), ]); $approveLink = collect($result->links)->first(fn($link) => $link->rel === 'approve')?->href; return response()->json([ 'order_id' => $orderId, 'approve_url' => $approveLink, ]); } Log::error('PayPal subscription order creation failed', ['response' => $response]); return response()->json(['error' => 'Subscription order creation failed'], 400); } catch (\Exception $e) { Log::error('PayPal subscription order creation exception', ['error' => $e->getMessage()]); return response()->json(['error' => 'Subscription order creation failed'], 500); } } }