Your IP : 216.73.217.77


Current Path : /home/users/unlimited/www/whatsapp-crm/app/Services/
Upload File :
Current File : /home/users/unlimited/www/whatsapp-crm/app/Services/StripeService.php

<?php

namespace App\Services;

use Carbon\Carbon;
use Helper;
use App\Models\BillingPayment;
use App\Models\BillingTransaction;
use App\Models\Organization;
use App\Models\PaymentGateway;
use App\Models\Setting;
use App\Models\Subscription;
use App\Models\SubscriptionPlan;
use App\Models\User;
use App\Services\SubscriptionService;
use App\Traits\ConsumesExternalServices;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class StripeService
{
    protected $config;
    protected $subscriptionService;
    protected $stripe;

    public function __construct()
    {
        $this->subscriptionService = new SubscriptionService();

        $stripeInfo = PaymentGateway::where('name', 'Stripe')->first();
        $this->config = json_decode($stripeInfo->metadata);
        $this->stripe = new \Stripe\StripeClient($this->config->secret_key);
    }

    public function handlePayment($amount, $planId = NULL)
    {
        try {
            if($planId == NULL){
                $stripeSession = $this->stripe->checkout->sessions->create([
                    'line_items' => [[
                        'price_data' => [
                            'currency' => strtolower(Setting::where('key', 'currency')->first()->value), 
                            'product_data' => [ 
                                'name' => 'Account Credits' 
                            ], 
                            'unit_amount' => $amount * 100
                        ],
                        'quantity' => 1,
                    ]],
                    'mode' => 'payment',
                    //'customer' => session()->get('current_organization'),
                    'customer_email' => auth()->user()->email,
                    'metadata' => [
                        'organization_id' => session()->get('current_organization'),
                        'user_id' => auth()->user()->id,
                        'amount' => $amount,
                        'plan_id' => $planId
                    ],
                    'success_url' => url('billing'),
                    'cancel_url' => url('billing'),
                ]);
            } else {
                $plan = SubscriptionPlan::where('id', $planId)->first();
                $metadata = json_decode($plan->metadata, true);
                $productId = $metadata['stripe']['product']['id'] ?? null;
                $priceId = $metadata['stripe']['price']['id'] ?? null;

                $stripeSession = $this->stripe->checkout->sessions->create([
                    'line_items' => [[
                        'price' => $priceId,
                        'quantity' => 1,
                    ]],
                    'mode' => 'subscription',
                    //'customer' => session()->get('current_organization'),
                    'customer_email' => auth()->user()->email,
                    'metadata' => [
                        'organization_id' => session()->get('current_organization'),
                        'user_id' => auth()->user()->id,
                        'amount' => $amount,
                        'plan_id' => $planId
                    ],
                    'success_url' => url('billing'),
                    'cancel_url' => url('billing'),
                ]);
            }

            return (object) array('success' => true, 'data' => $stripeSession->url);
        } catch (\Exception $e) {
            return (object) array('success' => false, 'error' => $e->getMessage());
        }
    }

    public function createProduct($productData){
        try {
            $plan = SubscriptionPlan::where('id', $productData->id)->first();
            $metadata = json_decode($plan->metadata, true);

            $product = $this->stripe->products->create([
                'name' => $plan->name,
            ]);

            $currency = Setting::where('key', 'currency')->value('value') ?? 'usd';
            $interval = $plan->period == 'monthly' ? 'month' : 'year';

            $price = $this->stripe->prices->create([
                'currency' => $currency,
                'unit_amount' => $plan->price * 100,
                'recurring' => ['interval' => $interval],
                'product' => $product->id,
            ]);
            
            $metadata['stripe'] = [
                'product' => $product,
                'price' => $price,
            ];
            $plan->metadata = json_encode($metadata);
            $plan->save();

            return (object) array('success' => true);
        } catch (\Exception $e) {
            return (object) array('success' => false, 'error' => $e->getMessage());
        }
    }

    public function updateProduct($productData){
        try {
            // Retrieve the subscription plan and its metadata
            $plan = SubscriptionPlan::where('id', $productData->id)->first();
            $metadata = json_decode($plan->metadata, true);

            // Get currency and interval settings
            $currency = Setting::where('key', 'currency')->value('value') ?? 'usd';
            $interval = $plan->period == 'monthly' ? 'month' : 'year';

            // Retrieve product and price IDs from metadata
            $productId = $metadata['stripe']['product']['id'] ?? null;
            $priceId = $metadata['stripe']['price']['id'] ?? null;

            if($productId){
                // Update the product in Stripe
                $product = $this->stripe->products->update(
                    $productId,
                    [
                        'name' => $plan->name ?? null,
                    ]
                );
            } else {
                // Create a new product if it does not exist
                $product = $this->stripe->products->create([
                    'name' => $plan->name,
                ]);

                $productId = $product->id;
            }

            // Check if we need to update the existing price or create a new one
            if ($priceId) {
                // Update the existing price for the product
                $price = $this->stripe->prices->update(
                    $priceId,
                    [
                        'active' => false, //Deactivate price
                    ]
                );
            }

            // Create a new price if the price ID does not exist
            $price = $this->stripe->prices->create([
                'currency' => $currency,
                'unit_amount' => $plan->price * 100,  // Stripe expects amount in the smallest currency unit
                'recurring' => isset($interval) ? ['interval' => $interval] : null,
                'product' => $productId,
            ]);
            
            // Update the metadata with the new product and price information
            $metadata['stripe'] = [
                'product' => $product,
                'price' => $price ?? null, // Use null if price is not created or updated
            ];
            $plan->metadata = json_encode($metadata);
            $plan->save();

            return (object) array('success' => true);
        } catch (\Exception $e) {
            return (object) array('success' => false, 'error' => $e->getMessage());
        }
    }

    public function deleteProduct($productData){
        try {
            $plan = SubscriptionPlan::where('id', $productData->id)->first();
            $metadata = json_decode($plan->metadata, true);

            //Retrieve the product id
            $productId = $metadata['stripe']['product']['id'] ?? null;

            //Archive the product
            $product = $this->stripe->products->update(
                $productId,
                [
                    'active' => false,
                ]
            );

            return (object) array('success' => true);
        } catch (\Exception $e) {
            return (object) array('success' => false, 'error' => $e->getMessage());
        }
    }

    public function handleSubscription($amount, $planId = NULL)
    {
        try{
            /*$organization = Organization::where('id', session()->get('current_organization'))->first();
            if ($organization && $metadata = json_decode($organization->metadata, true)) {
                $stripeCustomerId = $metadata['stripe']['id'] ?? null;
            } else {
                $stripeCustomerId = $this->createCustomer(auth()->user()->id);
            }*/

            // Create the subscription.
            $stripeSession = $this->stripe->subscriptions->create([
                'line_items' => [[
                    'price_data' => [
                        'currency' => strtolower(Setting::where('key', 'currency')->first()->value), 
                        'product_data' => [ 
                            'name' => 'Subscription Payment' 
                        ], 
                        'unit_amount' => $amount * 100
                    ],
                    'quantity' => 1,
                ]],
                'mode' => 'subscription',
                'customer_email' => auth()->user()->email,
                'metadata' => [
                    'organization_id' => session()->get('current_organization'),
                    'user_id' => auth()->user()->id,
                    'amount' => $amount,
                    'plan_id' => $planId
                ],
                'success_url' => url('billing'),
                'cancel_url' => url('billing'),
            ]);

            return (object) array('success' => true, 'data' => $stripeSession->url);
        } catch (\Exception $e) {
            return (object) array('success' => false, 'error' => $e->getMessage());
        }
    }

    public function handleWebhook(Request $request)
    {
        // Attempt to validate the Webhook
        try {
            $stripeEvent = \Stripe\Webhook::constructEvent($request->getContent(), $request->server('HTTP_STRIPE_SIGNATURE'), $this->config->webhook_secret);
        } catch(\UnexpectedValueException $e) {
            // Invalid payload
            //Log::info($e->getMessage());

            return response()->json([
                'status' => 400,
                'message' => $e->getMessage()
            ], 400);
        } catch(\Stripe\Exception\SignatureVerificationException $e) {
            // Invalid signature
            //Log::info($e->getMessage());

            return response()->json([
                'status' => 400,
                'message' => $e->getMessage()
            ], 400);
        }
        
        /*if ($stripeEvent->type == 'checkout.session.completed') {
            // Provide enough time for the event to be handled
            sleep(3);
        }

        if ($stripeEvent->type == 'invoice.paid') {
            // Provide enough time for the event to be handled
            sleep(3);
        }*/

        if($stripeEvent->type == 'checkout.session.completed'){
            // Get the metadata
            $metadata = $stripeEvent->data->object->lines->data[0]->metadata ?? ($stripeEvent->data->object->metadata ?? null);

            if (isset($metadata->organization_id)) {
                $transaction = DB::transaction(function () use ($stripeEvent, $metadata) {
                    //Save stripe id to database
                    $organization = Organization::where('id', $metadata->organization_id)->first();
                    $orgMetadata = json_decode($organization->metadata, true);
                    $orgMetadata['stripe_id'] = $stripeEvent->data->object->customer;
                    $organization->metadata = json_encode($orgMetadata);
                    $organization->save();

                    if($stripeEvent->data->object->mode == 'subscription'){
                        //Save plan id to database
                        if($metadata->plan_id != NULL){
                            $subscription = Subscription::where('organization_id', $metadata->organization_id)->update([
                                'plan_id' => $metadata->plan_id
                            ]);
                        }
                    } else {
                        $payment = BillingPayment::create([
                            'organization_id' => $metadata->organization_id,
                            'processor' => 'stripe',
                            'details' => $stripeEvent->data->object->payment_intent,
                            'amount' => $metadata->amount
                        ]);

                        //Log::info($payment);
                        $transaction = BillingTransaction::create([
                            'organization_id' => $metadata->organization_id,
                            'entity_type' => 'payment',
                            'entity_id' => $payment->id,
                            'description' => 'Stripe Payment',
                            'amount' => $metadata->amount,
                            'created_by' => $metadata->user_id,
                        ]);

                        if($metadata->plan_id == null){
                            $this->subscriptionService->activateSubscriptionIfInactiveAndExpiredWithCredits($metadata->organization_id, $metadata->user_id);
                        } else {
                            $this->subscriptionService->updateSubscriptionPlan($metadata->organization_id, $metadata->plan_id, $metadata->user_id);
                        }

                        return $transaction;
                    }

                    return response()->json([
                        'status' => 200
                    ], 200);
                });
            }
        }

        if($stripeEvent->type == 'invoice.paid'){
            Log::info($stripeEvent->type);
            $transaction = DB::transaction(function () use ($stripeEvent) {
                $customerId = $stripeEvent->data->object->customer;

                Log::info($customerId);

                // Query the Organization using the stripe_id (customer ID)
                $organization = Organization::whereJsonContains('metadata->stripe_id', $customerId)->first();

                Log::info($organization);

                if ($organization) {
                    $subscription = Subscription::where('organization_id', $organization->id)->first();
                    $organizationId = $organization->id;
                    $planId = $subscription->plan_id;
                    $amount = $stripeEvent->data->object->total/100;
                    $userId = 0;

                    $payment = BillingPayment::create([
                        'organization_id' => $organizationId,
                        'processor' => 'stripe',
                        'details' => $stripeEvent->data->object->id,
                        'amount' => $amount,
                    ]);

                    //Log::info($payment);
                    $transaction = BillingTransaction::create([
                        'organization_id' => $organizationId,
                        'entity_type' => 'payment',
                        'entity_id' => $payment->id,
                        'description' => 'Stripe Payment',
                        'amount' => $amount,
                        'created_by' => $userId,
                    ]);

                    Log::info($transaction);

                    if($planId == null){
                        $this->subscriptionService->activateSubscriptionIfInactiveAndExpiredWithCredits($organizationId, $userId);
                    } else {
                        $this->subscriptionService->updateSubscriptionPlan($organizationId, $planId, $userId);
                    }

                    return response()->json([
                        'status' => 200
                    ], 200);
                } 

                return response()->json([
                    'status' => 404
                ], 404);
            });
        }

        return response()->json([
            'status' => 200
        ], 200);
    }
}