Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
56.96% |
45 / 79 |
|
75.00% |
6 / 8 |
CRAP | |
0.00% |
0 / 1 |
RolePlaySubscriptionController | |
56.96% |
45 / 79 |
|
75.00% |
6 / 8 |
20.65 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
createUserFreemiumPlan | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
1 | |||
getUserPlan | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
info | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
1 | |||
getAvailableTime | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
getAvailableSessions | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
create | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
6 | |||
destroy | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace App\Http\Controllers\v2\RolePlay; |
4 | |
5 | use Illuminate\Http\Request; |
6 | use App\Traits\SubscriptionTrait; |
7 | use Illuminate\Http\JsonResponse; |
8 | use App\Http\Controllers\Controller; |
9 | use App\Http\Models\AddOns; |
10 | use App\Http\Models\Auth\User; |
11 | use App\Http\Models\RolePlayConversations; |
12 | use App\Http\Models\UserAddOns; |
13 | use App\Http\Requests\v2\RolePlay\DestroyUserAddOnRequest; |
14 | use Stripe\Stripe; |
15 | use Stripe\Subscription; |
16 | use Stripe\Checkout\Session; |
17 | use Stripe\Exception\ApiErrorException; |
18 | |
19 | class 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 | } |