Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
56.96% covered (warning)
56.96%
45 / 79
75.00% covered (warning)
75.00%
6 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
RolePlaySubscriptionController
56.96% covered (warning)
56.96%
45 / 79
75.00% covered (warning)
75.00%
6 / 8
20.65
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createUserFreemiumPlan
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 getUserPlan
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 info
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 getAvailableTime
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getAvailableSessions
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 create
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
6
 destroy
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Http\Controllers\v2\RolePlay;
4
5use Illuminate\Http\Request;
6use App\Traits\SubscriptionTrait;
7use Illuminate\Http\JsonResponse;
8use App\Http\Controllers\Controller;
9use App\Http\Models\AddOns;
10use App\Http\Models\Auth\User;
11use App\Http\Models\RolePlayConversations;
12use App\Http\Models\UserAddOns;
13use App\Http\Requests\v2\RolePlay\DestroyUserAddOnRequest;
14use Stripe\Stripe;
15use Stripe\Subscription;
16use Stripe\Checkout\Session;
17use Stripe\Exception\ApiErrorException;
18
19class RolePlaySubscriptionController extends Controller
20{
21    use SubscriptionTrait;
22
23    public function __construct()
24    {
25        Stripe::setApiKey(config('app.stripe.secret'));
26    }
27
28    private function createUserFreemiumPlan(User $user): UserAddOns
29    {
30        $freemiumAddOn = AddOns::where('identifier', 'roleplay-beginner')->first();
31
32        return UserAddOns::create([
33            'user_id' => $user->id,
34            'add_on_id' => $freemiumAddOn->id,
35            'product' => 'RolePlay',
36            'status' => 'active',
37            'starts_at' => now(),
38            'ends_at' => null,
39            'quantity' => 1,
40            'stripe_id' => null,
41        ]);
42    }
43
44    private function getUserPlan(User $user): UserAddOns
45    {
46        $rolePlayAddOn = UserAddOns::where('user_id', $user->id)
47            ->where('product', 'RolePlay')
48            ->where('status', 'active')
49            ->orderBy('created_at', 'desc')
50            ->first();
51
52        if (!$rolePlayAddOn) {
53            $rolePlayAddOn = $this->createUserFreemiumPlan($user);
54        }
55
56        return $rolePlayAddOn;
57    }
58
59    public function info(Request $request): JsonResponse
60    {
61        $user = $request->user();
62        $plan = $this->getUserPlan($user);
63        $addOn = AddOns::find($plan->add_on_id);
64        $availableTime = $this->getAvailableTime($user, $addOn);
65        $availableSessions = $this->getAvailableSessions($user, $addOn);
66
67        return response()->json([
68            'status' => 'success',
69            'data' => [
70                'user' => $user,
71                'plan' => $plan,
72                'add_on' => $addOn,
73                'available_time_seconds' => $availableTime,
74                'available_sessions' => $availableSessions,
75            ],
76        ]);
77    }
78
79    private function getAvailableTime(User $user, AddOns $product): int
80    {
81        $total = $product->features['monthly_total_time_credits'] ?? 0;
82        $used = RolePlayConversations::where('user_id', $user->id)
83            ->whereBetween('created_at', [now()->startOfMonth(), now()->endOfMonth()])
84            ->sum('duration');
85
86        return max(0, $total - $used);
87    }
88
89    private function getAvailableSessions(User $user, AddOns $product): int
90    {
91        $total = $product->features['monthly_roleplay_credits'] ?? 0;
92        $used = RolePlayConversations::where('user_id', $user->id)
93            ->whereBetween('created_at', [now()->startOfMonth(), now()->endOfMonth()])
94            ->count();
95
96        return max(0, $total - $used);
97    }
98
99    public function create(Request $request, AddOns $addOn): JsonResponse
100    {
101        $user = $request->user();
102        try {
103            $session = Session::create([
104                'payment_method_types' => ['card'],
105                'customer' => $user->stripe_id,
106                'line_items' => [[
107                    'price' => $addOn->stripe_price_id,
108                    'quantity' => 1,
109                ]],
110                'mode' => 'subscription',
111                'success_url' => config('app.role_play_url') . 'settings/subscription?session_id={CHECKOUT_SESSION_ID}',
112                'cancel_url' => config('app.role_play_url') . 'settings/subscription',
113            ]);
114
115            return response()->json([
116                'status' => 'success',
117                'data' => [
118                    'sessionId' => $session->id,
119                ]
120            ], 200);
121        } catch (ApiErrorException $e) {
122            return response()->json([
123                'status' => 'error',
124                'message' => 'Failed to create subscription: ' . $e->getMessage()
125            ], 400);
126        }
127    }
128
129    public function destroy(DestroyUserAddOnRequest $request, UserAddOns $userAddOn): JsonResponse
130    {
131        try {
132            $subscription = Subscription::retrieve($userAddOn->stripe_id);
133            $subscription->cancel();
134
135            return response()->json([
136                'status' => 'success',
137                'message' => 'Subscription canceled successfully.'
138            ], 200);
139        } catch (ApiErrorException $e) {
140            return response()->json([
141                'status' => 'error',
142                'message' => 'Failed to cancel subscription: ' . $e->getMessage()
143            ], 500);
144        }
145    }
146}