active() ->whereNotNull('stripe_price_id') ->orderBy('sort_order') ->orderBy('amount') ->get(); return CreditProductResource::collection($products); } public function checkout(Request $request, CreditProduct $creditProduct): JsonResponse { abort_unless($creditProduct->is_active, 404); abort_unless(filled($creditProduct->stripe_price_id), 404); $user = $request->user(); $metadata = [ 'credit_product_id' => $creditProduct->getKey(), 'credit_product_type' => $creditProduct->type->value, 'credits' => (string) $creditProduct->credits, ]; $sessionOptions = [ 'success_url' => $this->redirectUrl('success'), 'cancel_url' => $this->redirectUrl('cancel'), 'metadata' => $metadata, 'client_reference_id' => $creditProduct->getKey(), ]; if ($creditProduct->type === CreditProductType::MONTHLY) { $checkout = $user ->newSubscription('default', $creditProduct->stripe_price_id) ->withMetadata($metadata) ->checkout($sessionOptions); } else { $checkout = $user->checkout([ $creditProduct->stripe_price_id => 1, ], $sessionOptions); } $session = $checkout->asStripeCheckoutSession(); return response()->json([ 'id' => $session->id, 'url' => $session->url, ]); } public function portal(Request $request): JsonResponse { return response()->json([ 'url' => $request->user()->billingPortalUrl($this->redirectUrl('portal')), ]); } private function redirectUrl(string $status): string { $scheme = trim((string) config('app.mobile_scheme', env('APP_MOBILE_SCHEME', 'bowly')), ':/'); return "{$scheme}:///billing/{$status}"; } }