diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 5164a0b..3e76696 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -140,6 +140,85 @@ public function subscribeUser(Request $request, $identifier) ]); } + /** + * List users who reached the referral invite target (کد معرف) and have + * not yet been granted their free-month subscription. + */ + public function pendingReferralRewards(Request $request) + { + $target = User::REFERRAL_SUBSCRIPTION_TARGET; + + $users = User::withCount('referrals') + ->whereNull('referral_subscription_granted_at') + ->having('referrals_count', '>=', $target) + ->orderByDesc('referrals_count') + ->get(['id', 'uuid', 'first_name', 'last_name', 'email', 'mobile', 'referral_code']); + + return response()->json([ + 'subscription_target' => $target, + 'data' => $users->map(fn ($u) => [ + 'id' => $u->id, + 'uuid' => $u->uuid, + 'name' => trim($u->first_name . ' ' . $u->last_name), + 'email' => $u->email, + 'mobile' => $u->mobile, + 'referral_code' => $u->referral_code, + 'successful_invites' => $u->referrals_count, + ]), + ]); + } + + /** + * Grant the free-month subscription to a user who reached the referral + * invite target. Defaults to the meditation package's monthly product, + * overridable via product_id. Marks the grant so it happens only once. + */ + public function fulfillReferralReward(Request $request, $userId) + { + $data = $request->validate([ + 'product_id' => 'integer|nullable', + ]); + + if (!$user = User::find($userId)) { + return response()->json(['message' => 'user not found'], 404); + } + + if ($user->referral_subscription_granted_at) { + return response()->json(['message' => 'referral subscription already granted'], 400); + } + + if ($user->referrals()->count() < User::REFERRAL_SUBSCRIPTION_TARGET) { + return response()->json(['message' => 'user has not reached the referral target yet'], 400); + } + + // Resolve which monthly product to grant + if (!empty($data['product_id'])) { + $product = Product::where('id', $data['product_id'])->first(); + } else { + $package = PackageName::where('name', User::REFERRAL_PACKAGE_NAME)->first(); + $product = $package + ? Product::where('package_name_id', $package->id) + ->where('type', Product::TYPES['monthly']) + ->first() + : null; + } + + if (!$product) { + return response()->json(['message' => 'monthly product not found'], 404); + } + + $product->buy($user); + + $user->referral_subscription_granted_at = now(); + $user->save(); + + return response()->json([ + 'message' => 'referral subscription granted', + 'user_id' => $user->id, + 'product_id' => $product->id, + ]); + } + public function unsubscribeUser(Request $request, $identifier) { $data = $request->validate([ diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 8827472..5325cbe 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -638,6 +638,10 @@ public function status(Request $request) ->first(); } + // Expose the referrer's uuid so downstream apps can resolve the + // referral relationship (کد معرف) against their own local user. + $user['referrer_uuid'] = $user->referred_by ? optional($user->referrer)->uuid : null; + return $user; } diff --git a/app/Models/User.php b/app/Models/User.php index 60aa670..42b54bc 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -20,9 +20,16 @@ class User extends Authenticatable 'male' => 1, 'female' => 2, ]; + + // Referral program (کد معرف): successful invites needed for a free month, + // and the app whose monthly product is granted as the reward. + const REFERRAL_SUBSCRIPTION_TARGET = 10; + const REFERRAL_PACKAGE_NAME = 'com.approagency.meditation'; + protected $guarded = []; protected $casts = [ 'mobile_verified_at' => 'datetime', + 'referral_subscription_granted_at' => 'datetime', ]; protected $hidden = [ 'password', diff --git a/database/migrations/2026_06_06_000100_add_referral_subscription_granted_at_to_users_table.php b/database/migrations/2026_06_06_000100_add_referral_subscription_granted_at_to_users_table.php new file mode 100644 index 0000000..05d232e --- /dev/null +++ b/database/migrations/2026_06_06_000100_add_referral_subscription_granted_at_to_users_table.php @@ -0,0 +1,27 @@ +timestamp('referral_subscription_granted_at')->nullable()->after('referred_by'); + } + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + if (Schema::hasColumn('users', 'referral_subscription_granted_at')) { + $table->dropColumn('referral_subscription_granted_at'); + } + }); + } +}; diff --git a/routes/api.php b/routes/api.php index 417ca7e..060175a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -95,6 +95,9 @@ Route::get('/users/{mobile}/status-v1', 'getUserStatusV1'); Route::put('/users/{mobile}/status', 'subscribeUser'); Route::delete('/users/{mobile}/status', 'unsubscribeUser'); + // Referral free-month rewards (کد معرف) + Route::get('/referral-rewards', 'pendingReferralRewards'); + Route::post('/referral-rewards/{user}/fulfill', 'fulfillReferralReward'); }); }); ///promotions