<?php
/**
 * Copyright (C) Baluart.COM - All Rights Reserved
 *
 * @since 1.0
 * @author Baluart E.I.R.L.
 * @copyright Copyright (c) 2015 - 2021 Baluart E.I.R.L.
 * @license http://codecanyon.net/licenses/faq Envato marketplace licenses
 * @link https://easyforms.dev/ Easy Forms
 */
namespace app\modules\subscription\controllers;

use app\models\User;
use app\modules\subscription\helpers\Config;
use app\modules\subscription\models\search\SubscriptionTransactionSearch;
use app\modules\subscription\models\Subscription;
use app\modules\subscription\models\SubscriptionBillingInformation;
use app\modules\subscription\models\SubscriptionPrice;
use app\modules\subscription\models\SubscriptionProduct;
use app\modules\subscription\models\SubscriptionTransaction;
use app\modules\subscription\services\PaypalClient;
use app\modules\subscription\services\StripeClient;
use Carbon\Carbon;
use kartik\datecontrol\Module as DateControlModule;
use Stripe\Charge;
use Yii;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\helpers\Json;
use yii\helpers\Url;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\web\Response;

class UserController extends Controller
{

    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::class,
                'actions' => [
                    'create' => ['POST'],
                    'cancel' => ['POST'],
                    'cancel-now' => ['POST'],
                    'change-paypal-plan' => ['POST'],
                    'change-stripe-plan' => ['POST'],
                ],
            ],
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'actions' => [
                            'index',
                            'confirm',
                            'change',
                            'change-paypal-plan',
                            'change-stripe-plan',
                            'create',
                            'cancel',
                            'cancel-now',
                            'receipts',
                            'receipt',
                            'billing-info',
                        ],
                        'roles' => ['@'],
                    ],
                ],
            ],
        ];
    }

    /**
     * List subscription plans to select a subscription
     *
     * @return string
     * @throws \Exception
     */
    public function actionIndex()
    {
        $this->layout = '/admin';

        /** @var Subscription $subscription */
        $subscription = Subscription::find()
            ->where(['user_id' => Yii::$app->user->id])
            ->andWhere(['or', ['ends_at' => null], ['>', 'ends_at', time()]])
            ->one();

        if (!empty($subscription) && $subscription->valid()) {

            return $this->render('subscription', [
                'subscription' => $subscription
            ]);

        }

        $products = SubscriptionProduct::find()
            ->where(['status' => SubscriptionProduct::ON])
            ->all();

        $switcher = Config::get('pricing.switcher');
        $intervals = Config::get('pricing.intervals');
        $intervalKeys = array_keys($intervals);

        $monthlyAndYearlyPrices = SubscriptionPrice::find()
            ->where(['type' => SubscriptionPrice::TYPE_RECURRING])
            ->andWhere(['in', 'interval', $intervalKeys])
            ->with('product')
            ->all();;

        $otherRecurringPrices = SubscriptionPrice::find()
            ->where(['type' => SubscriptionPrice::TYPE_RECURRING])
            ->andWhere(['not in', 'interval', $intervalKeys])
            ->with('product')
            ->all();;

        $oneTimePrices = SubscriptionPrice::find()
            ->where(['type' => SubscriptionPrice::TYPE_ONE_TIME])
            ->with('product')
            ->all();;

        return $this->render('index', [
            'products' => $products,
            'switcher' => $switcher,
            'intervals' => $intervals,
            'subscription' => $subscription,
            'monthlyAndYearlyPrices' => $monthlyAndYearlyPrices,
            'otherRecurringPrices' => $otherRecurringPrices,
            'oneTimePrices' => $oneTimePrices,
        ]);

    }


    /**
     * Show a selected subscription plan
     *
     * @param int $id
     * @param int $now
     * @return string|Response
     * @throws \Stripe\Exception\ApiErrorException
     */
    public function actionConfirm($id = 0, $now = 0)
    {

        $this->layout = '/admin';

        /** @var SubscriptionPrice $price */
        $price = SubscriptionPrice::find()
            ->where(['id' => $id])
            ->one();

        /** @var Subscription $subscription */
        $subscription = Subscription::find()
            ->where(['created_by' => Yii::$app->user->id])
            ->andWhere(['or', ['ends_at' => null], ['>', 'ends_at', time()]])
            ->one();

        if (($price !== null) && $price->product->status === SubscriptionProduct::ON) {

            $stripeInvoice = null;

            if ($now && $subscription->isStripe()) {
                $stripeClient = new StripeClient();
                $stripeInvoice = $stripeClient->previewInvoice($subscription->gateway_id, $price->getStripeId());
            }

            return $this->render('confirm', [
                'price' => $price,
                'subscription' => $subscription,
                'now' => $now,
                'stripeInvoice' => $stripeInvoice,
            ]);
        }

        Yii::$app->session->setFlash('warning', Yii::t('app', 'Please select a valid plan.'));

        return $this->redirect(['/subscription/user/index']);
    }

    /**
     * List subscription plans to change a current subscription
     *
     * @return string|Response
     * @throws \Exception
     */
    public function actionChange()
    {
        $this->layout = '/admin';

        /** @var Subscription $subscription */
        $subscription = Subscription::find()
            ->where(['created_by' => Yii::$app->user->id])
            ->andWhere(['or', ['ends_at' => null], ['>', 'ends_at', time()]])
            ->one();

        if ($subscription->cancelled()) {

            Yii::$app->getSession()->setFlash(
                'success',
                Yii::t('app', 'Your current subscription was cancelled. We are unable to change your subscription plan.')
            );

            return $this->redirect(['/subscription/user/index']);

        }

        $products = SubscriptionProduct::find()
            ->where(['status' => SubscriptionProduct::ON])
            ->all();

        $switcher = Config::get('pricing.switcher');
        $intervals = Config::get('pricing.intervals');
        $intervalKeys = array_keys($intervals);

        $monthlyAndYearlyPrices = SubscriptionPrice::find()
            ->where(['type' => SubscriptionPrice::TYPE_RECURRING])
            ->andWhere(['in', 'interval', $intervalKeys])
            ->with('product')
            ->all();;

        $otherRecurringPrices = SubscriptionPrice::find()
            ->where(['type' => SubscriptionPrice::TYPE_RECURRING])
            ->andWhere(['not in', 'interval', $intervalKeys])
            ->with('product')
            ->all();;

        $oneTimePrices = SubscriptionPrice::find()
            ->where(['type' => SubscriptionPrice::TYPE_ONE_TIME])
            ->with('product')
            ->all();;

        return $this->render('index', [
            'products' => $products,
            'switcher' => $switcher,
            'intervals' => $intervals,
            'subscription' => $subscription,
            'monthlyAndYearlyPrices' => $monthlyAndYearlyPrices,
            'otherRecurringPrices' => $otherRecurringPrices,
            'oneTimePrices' => $oneTimePrices,
        ]);
    }

    /**
     * Change Subscription with Paypal
     * @return array
     * @throws NotFoundHttpException
     * @throws \Stripe\Exception\ApiErrorException
     * @throws \Exception
     */
    public function actionChangePaypalPlan()
    {

        Yii::$app->response->format = Response::FORMAT_JSON;

        $request = Yii::$app->request;
        $id = $request->post('id');
        $now = $request->post('now');
        $details = $request->post('details');

        if ($request->isPost && $id && $details) {

            /** @var SubscriptionPrice $price */
            $price = SubscriptionPrice::find()
                ->where(['id' => $id])
                ->one();

            if (($price !== null) && $price->product->status === SubscriptionProduct::ON && $price->isRecurring()) {

                // Order details
                $details = is_string($details) ? json_decode($details) : $details;

                if (isset($details->orderID, $details->subscriptionID)) {

                    $paypalClient = new PaypalClient();
                    $paypalSubscription = $paypalClient->getSubscription($details->subscriptionID);

                    if (isset($paypalSubscription->id, $paypalSubscription->plan_id, $paypalSubscription->status)) {

                        $subscriptionId = $paypalSubscription->id;
                        $subscriptionStatus = $paypalSubscription->status;
                        $nextBillingTime = isset($paypalSubscription->billing_info->next_billing_time)
                            ? strtotime($paypalSubscription->billing_info->next_billing_time)
                            : '';

                        // Important! Validate subscription plan id via REST API
                        if ($price->getPaypalId() !== $paypalSubscription->plan_id) {
                            // This plan id is invalid
                            return [
                                'success' => false,
                                'action' => 'message',
                                'message' => Yii::t('app', 'Invalid Plan. Please contact us!'),
                            ];
                        }

                        // Update subscription plan
                        /** @var Subscription $subscription */
                        $subscription = Subscription::find()
                            ->where(['created_by' => Yii::$app->user->id])
                            ->andWhere(['or', ['ends_at' => null], ['>', 'ends_at', time()]])
                            ->one();

                        if (($subscription !== null) && !$subscription->cancelled()) {

                            /**
                             * Change Plan Now
                             */

                            if ($now) {

                                // Required variables
                                $orderId = isset($details->orderID) ? $details->orderID : null; // By default, we use the order id as transaction id
                                $subscriptionLastPayment = isset($paypalSubscription->billing_info->last_payment) ? $paypalSubscription->billing_info->last_payment : null;

                                // Cancel current subscription
                                $subscription->cancelNow();

                                // Create Subscription
                                $subscription = new Subscription();
                                $subscription->user_id = Yii::$app->user->id;
                                $subscription->product_id = $price->product_id;
                                $subscription->price_id = $price->id;
                                $subscription->name = $price->product->name;
                                $subscription->type = $price->type;
                                $subscription->trial_ends_at = null; // NULL because it's a change (a trial is incompatible)
                                $subscription->ends_at = null; // NULL because it's a recurring payment
                                $subscription->gateway = Subscription::GATEWAY_PAYPAL;
                                $subscription->gateway_id = $subscriptionId;
                                $subscription->gateway_status = $subscriptionStatus;
                                $subscription->form_limit = $price->product->form_limit;
                                $subscription->submission_limit = $price->product->submission_limit;
                                $subscription->info = [
                                    'currency_code' => $price->currency_code,
                                    'amount' => $price->amount,
                                    'interval' => $price->interval,
                                    'interval_count' => $price->interval_count,
                                    'description' => $price->product->description,
                                    'details' => $price->product->details,
                                    'next_billing_time' => $nextBillingTime,
                                ];
                                $subscription->save();

                                // Save Transaction
                                $transaction = new SubscriptionTransaction();
                                $transaction->subscription_id = $subscription->id;
                                $transaction->gateway = Subscription::GATEWAY_PAYPAL;
                                $transaction->gateway_id = $subscriptionId;
                                $transaction->gateway_status = $subscriptionStatus;
                                $transaction->gateway_transaction_id = $orderId;
                                $transaction->gateway_time = isset($paypalSubscription->create_time) ? strtotime($paypalSubscription->create_time) : null;
                                $transaction->total = isset($subscriptionLastPayment->amount->value) ? $subscriptionLastPayment->amount->value : null;
                                $transaction->currency_code = isset($subscriptionLastPayment->amount->currency_code) ? $subscriptionLastPayment->amount->currency_code : null;
                                $transaction->payload = Json::encode($paypalSubscription);
                                $transaction->created_by = $subscription->created_by;
                                $transaction->save();

                                // Update User Roles
                                $subscription->updateUserRoles();

                                return [
                                    'success' => true,
                                    'action' => 'redirect',
                                    'url' => Url::to(['/subscription/user/index']),
                                    'message' => Yii::t('app', 'Thanks! The change will be made effective immediately.'),
                                ];
                            }

                            /**
                             * Change Plan with Next Billing Cycle
                             */

                            // Update current subscription
                            $subscriptionInfo = $subscription->info;
                            $subscriptionInfo['next_subscription'] = [
                                'product_id' => $price->product_id,
                                'price_id' => $price->id,
                                'name' => $price->product->name,
                                'type' => $price->type,
                                'gateway_id' => $subscriptionId,
                                'gateway_status' => $subscriptionStatus,
                                'form_limit' => $price->product->form_limit,
                                'submission_limit' => $price->product->submission_limit,
                                'info' => [
                                    'currency_code' => $price->currency_code,
                                    'amount' => $price->amount,
                                    'interval' => $price->interval,
                                    'interval_count' => $price->interval_count,
                                    'description' => $price->product->description,
                                    'details' => $price->product->details,
                                    'next_billing_time' => $nextBillingTime,
                                ],
                            ];
                            $subscription->info = $subscriptionInfo;
                            $subscription->save();

                            /** @var DateControlModule $dateControlModule */
                            $dateControlModule = Yii::$app->getModule('datecontrol');
                            $dateFormat = $dateControlModule->displaySettings[DateControlModule::FORMAT_DATE];
                            $nextBillingTime = Carbon::createFromTimestamp($nextBillingTime);
                            $nextBillingTime = $nextBillingTime->format($dateFormat);

                            return [
                                'success' => true,
                                'action' => 'redirect',
                                'url' => Url::to(['/subscription/user/index']),
                                'message' => Yii::t('app', 'Thanks! The change will be effective in the next billing cycle {next_billing_time}. Partial funds not be debited from your account.', [
                                    'next_billing_time' => '<strong>'. $nextBillingTime . '</strong>',
                                ]),
                            ];
                        }
                    }
                }
            }
        }

        throw new NotFoundHttpException('Page not found.');

    }

    /**
     * Change Subscription with Stripe
     *
     * @return Response
     * @throws \Stripe\Exception\ApiErrorException
     * @throws \Exception
     */
    public function actionChangeStripePlan()
    {
        $id = Yii::$app->request->post('id');
        $priceID = Yii::$app->request->post('price_id');

        if (($subscription = Subscription::findOne(['id' => $id])) !== null && ($price = SubscriptionPrice::findOne(['id' => $priceID])) !== null) {
            if ($subscription->isStripe()) {
                $stripeClient = new StripeClient();
                $stripeSubscription = $stripeClient->updateSubscription($subscription->gateway_id, $price->getStripeId());
                if ($stripeSubscription) {

                    // Update Subscription
                    $subscription->product_id = $price->product_id;
                    $subscription->price_id = $price->id;
                    $subscription->name = $price->product->name;
                    $subscription->type = $price->type;
                    $subscription->trial_ends_at = $price->calculateTrialEndsAt();
                    $subscription->ends_at = null; // NULL because it's a recurring payment
                    $subscription->gateway_id = $stripeSubscription->id;
                    $subscription->gateway_status = $stripeSubscription->status;
                    $subscription->form_limit = $price->product->form_limit;
                    $subscription->submission_limit = $price->product->submission_limit;
                    $subscription->info = [
                        'currency_code' => $price->currency_code,
                        'amount' => $price->amount,
                        'interval' => $price->interval,
                        'interval_count' => $price->interval_count,
                        'description' => $price->product->description,
                        'details' => $price->product->details,
                        'next_billing_time' => $stripeSubscription->current_period_end,
                    ];
                    $subscription->save();

                    // Update User Roles
                    $subscription->updateUserRoles();

                    Yii::$app->session->setFlash('success', Yii::t('app', 'Your subscription has been updated.'));

                    return $this->redirect(['/subscription/user/index']);
                }
            }
        }

        Yii::$app->session->setFlash('danger', Yii::t('app', 'Your subscription has not been updated.'));

        return $this->redirect(['/subscription/user/index']);
    }

    /**
     * Receipt request to create billing plan
     * Response: Json Format
     *
     * @return array
     * @throws NotFoundHttpException
     * @throws \Exception
     */
    public function actionCreate()
    {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $request = Yii::$app->request;
        $id = $request->post('id');
        $details = $request->post('details');
        $token = $request->post('token');
        $paymentIntent = $request->post('paymentIntent');

        if ($request->isPost && $id) {

            /** @var SubscriptionPrice $price */
            $price = SubscriptionPrice::find()
                ->where(['id' => $id])
                ->one();

            if (($price !== null) && $price->product->status === SubscriptionProduct::ON) {

                if ($details) { // Paypal

                    // Order details
                    $details = is_string($details) ? json_decode($details) : $details;

                    if ($price->isRecurring()) {

                        if (isset($details->orderID, $details->subscriptionID)) {
                            // Validate subscription with paypal
                            $paypalClient = new PaypalClient();
                            $paypalSubscription = $paypalClient->getSubscription($details->subscriptionID);

                            if (isset($paypalSubscription->id, $paypalSubscription->plan_id, $paypalSubscription->status, $paypalSubscription->subscriber, $paypalSubscription->billing_info->last_payment)) {

                                // Validate Subscription Price
                                if ($price->getPaypalId() === $paypalSubscription->plan_id) {
                                    $subscriptionId = $paypalSubscription->id;
                                    $subscriptionStatus = $paypalSubscription->status;
                                    $nextBillingTime = isset($paypalSubscription->billing_info->next_billing_time)
                                        ? strtotime($paypalSubscription->billing_info->next_billing_time)
                                        : '';

                                    // Check if this order was saved in the database before
                                    $count = Subscription::find()->where(['gateway_id' => $subscriptionId])->count();
                                    if ($count > 0) {
                                        // This order was processed before
                                        return [
                                            'success' => false,
                                            'action' => 'message',
                                            'message' => Yii::t('app', 'This subscription was processed before. Please contact us!'),
                                        ];
                                    }

                                    // Create Subscription
                                    $subscription = new Subscription();
                                    $subscription->user_id = Yii::$app->user->id;
                                    $subscription->product_id = $price->product_id;
                                    $subscription->price_id = $price->id;
                                    $subscription->name = $price->product->name;
                                    $subscription->type = $price->type;
                                    $subscription->trial_ends_at = $price->calculateTrialEndsAt();
                                    $subscription->ends_at = null; // NULL because it's a recurring payment
                                    $subscription->gateway = Subscription::GATEWAY_PAYPAL;
                                    $subscription->gateway_id = $subscriptionId;
                                    $subscription->gateway_status = $subscriptionStatus;
                                    $subscription->form_limit = $price->product->form_limit;
                                    $subscription->submission_limit = $price->product->submission_limit;
                                    $subscription->info = [
                                        'currency_code' => $price->currency_code,
                                        'amount' => $price->amount,
                                        'interval' => $price->interval,
                                        'interval_count' => $price->interval_count,
                                        'description' => $price->product->description,
                                        'details' => $price->product->details,
                                        'next_billing_time' => $nextBillingTime,
                                    ];
                                    $subscription->save();

                                    // Update User Roles
                                    $subscription->updateUserRoles();

                                    return [
                                        'success' => true,
                                        'action' => 'redirect',
                                        'url' => Url::to(['/subscription/user/index']),
                                        'message' => Yii::t('app', 'Thanks for your subscription!'),
                                    ];
                                }
                            }
                        }

                    } else {

                        if (isset($details->id)) {

                            // Validate subscription with paypal
                            $paypalClient = new PaypalClient();
                            $paypalOrder = $paypalClient->getOrder($details->id);

                            if (!empty($paypalOrder) && !empty($paypalOrder->id)) {

                                $orderId = $paypalOrder->id;
                                $orderStatus = $paypalOrder->status;
                                $orderPurchaseUnit = $paypalOrder->purchase_units[0];
                                $transactionId = isset($orderPurchaseUnit->payments->captures[0]->id) ?
                                    $orderPurchaseUnit->payments->captures[0]->id
                                    : null;

                                if ($transactionId && $orderStatus === "COMPLETED") {

                                    // Check if this order was saved in the database before
                                    $count = Subscription::find()->where(['gateway_id' => $orderId])->count();
                                    if ($count > 0) {
                                        // This order was processed before
                                        return [
                                            'success' => false,
                                            'action' => 'message',
                                            'message' => Yii::t('app', 'This order was processed before. Please contact us!'),
                                        ];
                                    }

                                    // Save Subscription
                                    $subscription = new Subscription();
                                    $subscription->user_id = Yii::$app->user->id;
                                    $subscription->product_id = $price->product_id;
                                    $subscription->price_id = $price->id;
                                    $subscription->name = $price->product->name;
                                    $subscription->type = $price->type;
                                    $subscription->trial_ends_at = $price->calculateTrialEndsAt();
                                    $subscription->ends_at = $price->calculateEndsAt(); // Calculate it because it's a one-time payment
                                    $subscription->gateway = Subscription::GATEWAY_PAYPAL;
                                    $subscription->gateway_id = $orderId;
                                    $subscription->gateway_status = $orderStatus;
                                    $subscription->form_limit = $price->product->form_limit;
                                    $subscription->submission_limit = $price->product->submission_limit;
                                    $subscription->info = [
                                        'currency_code' => $price->currency_code,
                                        'amount' => $price->amount,
                                        'interval' => $price->interval,
                                        'interval_count' => $price->interval_count,
                                        'description' => $price->product->description,
                                        'details' => $price->product->details,
                                    ];
                                    $subscription->save();

                                    // Save Transaction
                                    $transaction = new SubscriptionTransaction();
                                    $transaction->subscription_id = $subscription->id;
                                    $transaction->gateway = Subscription::GATEWAY_PAYPAL;
                                    $transaction->gateway_id = $orderId;
                                    $transaction->gateway_status = $orderStatus;
                                    $transaction->gateway_transaction_id = $transactionId;
                                    $transaction->gateway_time = isset($paypalOrder->create_time) ? strtotime($paypalOrder->create_time) : null;;
                                    $transaction->total = isset($orderPurchaseUnit->amount->value) ? $orderPurchaseUnit->amount->value * 100 : null; // In cents
                                    $transaction->currency_code = isset($orderPurchaseUnit->amount->currency_code) ? $orderPurchaseUnit->amount->currency_code : null;
                                    $transaction->payload = Json::encode($paypalOrder);
                                    $transaction->created_by = $subscription->created_by;
                                    $transaction->save();

                                    // Update User Roles
                                    $subscription->updateUserRoles();

                                    return [
                                        'success' => true,
                                        'action' => 'redirect',
                                        'url' => Url::to(['/subscription/user/index']),
                                        'message' => Yii::t('app', 'Thanks for your order!'),
                                    ];

                                }
                            }
                        }
                    }

                } elseif ($token || $paymentIntent) { // Stripe

                    // Get User & Billing Information
                    /** @var User $user */
                    $user = Yii::$app->user->identity;

                    if ($token && $price->isRecurring()) {

                        try {

                            /**
                             * Create Subscription on Stripe
                             */

                            $stripeClient = new StripeClient();

                            // Create customer
                            $customer = $stripeClient->customer([
                                'email' => $user->email,
                                'source'  => $token
                            ]);

                            // Get Plan
                            $plan = $stripeClient->retrievePlanByID($price->getStripeId());

                            // Creates a new subscription
                            $stripeSubscription = $stripeClient->createSubscription($customer->id, [
                                ['price' => $plan->id],
                            ]);

                            /**
                             * Create Subscription
                             */
                            $subscription = new Subscription();
                            $subscription->user_id = Yii::$app->user->id;
                            $subscription->product_id = $price->product_id;
                            $subscription->price_id = $price->id;
                            $subscription->name = $price->product->name;
                            $subscription->type = $price->type;
                            $subscription->trial_ends_at = $price->calculateTrialEndsAt();
                            $subscription->ends_at = null; // NULL because it's a recurring payment
                            $subscription->gateway = Subscription::GATEWAY_STRIPE;
                            $subscription->gateway_id = $stripeSubscription->id;
                            $subscription->gateway_status = $stripeSubscription->status;
                            $subscription->form_limit = $price->product->form_limit;
                            $subscription->submission_limit = $price->product->submission_limit;
                            $subscription->info = [
                                'currency_code' => $price->currency_code,
                                'amount' => $price->amount,
                                'interval' => $price->interval,
                                'interval_count' => $price->interval_count,
                                'description' => $price->product->description,
                                'details' => $price->product->details,
                                'next_billing_time' => $stripeSubscription->current_period_end,
                            ];
                            $subscription->save();

                            // Update User Roles
                            $subscription->updateUserRoles();

                            return [
                                'success' => true,
                                'action' => 'redirect',
                                'url' => Url::to(['/subscription/user/index']),
                                'message' => Yii::t('app', 'Thanks for your subscription!'),
                            ];

                        } catch(\Exception $e) {
                            Yii::error($e);
                        }

                    } elseif ($paymentIntent && !$price->isRecurring()) {

                        try {

                            /**
                             * Confirm Payment Intent on Stripe
                             */

                            $stripeClient = new StripeClient();
                            $stripePayment = $stripeClient->confirmPaymentIntent($paymentIntent);
                            /** @var Charge $stripeCharge */
                            $stripeCharge = isset($stripePayment->charges->data[0]) ? $stripePayment->charges->data[0] : null;

                            /**
                             * Create Subscription
                             */
                            if (isset($stripePayment->id, $stripePayment->status)) {
                                $subscription = new Subscription();
                                $subscription->user_id = Yii::$app->user->id;
                                $subscription->product_id = $price->product_id;
                                $subscription->price_id = $price->id;
                                $subscription->name = $price->product->name;
                                $subscription->type = $price->type;
                                $subscription->trial_ends_at = $price->calculateTrialEndsAt();
                                $subscription->ends_at = $price->calculateEndsAt(); // Calculate it because it's a one-time payment
                                $subscription->gateway = Subscription::GATEWAY_STRIPE;
                                $subscription->gateway_id = $stripePayment->id;
                                $subscription->gateway_status = $stripePayment->status;
                                $subscription->form_limit = $price->product->form_limit;
                                $subscription->submission_limit = $price->product->submission_limit;
                                $subscription->info = [
                                    'currency_code' => $price->currency_code,
                                    'amount' => $price->amount,
                                    'interval' => $price->interval,
                                    'interval_count' => $price->interval_count,
                                    'description' => $price->product->description,
                                    'details' => $price->product->details,
                                ];
                                $subscription->save();

                                // Save Transaction
                                $transaction = new SubscriptionTransaction();
                                $transaction->subscription_id = $subscription->id;
                                $transaction->gateway = Subscription::GATEWAY_STRIPE;
                                $transaction->gateway_id = $stripePayment->id;
                                $transaction->gateway_status = $stripePayment->status;
                                $transaction->gateway_transaction_id = isset($stripeCharge->id) ? $stripeCharge->id : null;
                                $transaction->gateway_time = isset($stripeCharge->created) ? $stripeCharge->created : null;
                                $transaction->total = isset($stripePayment->amount_received) ? $stripePayment->amount_received : null;
                                $transaction->currency_code = isset($stripePayment->currency) ? $stripePayment->currency : null;
                                $transaction->payload = Json::encode($stripePayment);
                                $transaction->created_by = $subscription->created_by;
                                $transaction->save();

                                // Update User Roles
                                $subscription->updateUserRoles();

                                return [
                                    'success' => true,
                                    'action' => 'redirect',
                                    'url' => Url::to(['/subscription/user/index']),
                                    'message' => Yii::t('app', 'Thanks for your order!'),
                                ];
                            }

                        } catch(\Exception $e) {
                            Yii::error($e);
                        }
                    }
                }
            }
        }

        throw new NotFoundHttpException('Page not found.');

    }

    /**
     * Cancel Subscription at the end of the billing cycle
     *
     * @return Response
     * @throws NotFoundHttpException
     * @throws \Stripe\Exception\ApiErrorException
     */
    public function actionCancel()
    {
        $id = Yii::$app->request->post('id');

        if (($model = Subscription::findOne(['id' => $id])) !== null) {

            if ($model->type === SubscriptionPrice::TYPE_RECURRING) {
                $model->cancel();
                Yii::$app->session->setFlash('success', Yii::t('app', 'Your subscription has been cancelled.'));
                return $this->redirect(['/subscription/user/index']);
            }
        }

        throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
    }

    /**
     * Cancel Subscription Now
     *
     * @return Response
     * @throws \Exception
     */
    public function actionCancelNow()
    {
        $id = Yii::$app->request->post('id');

        if (($model = Subscription::findOne($id)) !== null) {
            $model->markAsCancelled();
            // Downgrade User Roles
            $model->downgradeUserRoles();

            Yii::$app->session->setFlash('success', Yii::t('app', 'Your subscription has been cancelled.'));
            return $this->redirect(['/subscription/user/index']);
        }

        throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
    }

    /**
     * List Receipts
     *
     * @return string
     */
    public function actionReceipts()
    {
        $this->layout = '/admin';

        $searchModel = new SubscriptionTransactionSearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

        return $this->render('receipts', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * Show Receipt
     *
     * @param $id
     * @return string
     * @throws NotFoundHttpException
     */
    public function actionReceipt($id)
    {
        if (($model = SubscriptionTransaction::findOne(['id' => $id])) !== null) {
            $this->layout = '/public';

            return $this->render('receipt', [
                'model' => $model,
            ]);
        }

        throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
    }

    /**
     * Update Billing Information
     *
     * @return string|Response
     */
    public function actionBillingInfo()
    {
        $this->layout = '/admin';

        $model = SubscriptionBillingInformation::findOne(['user_id' => Yii::$app->user->id]);
        $model = $model !== null ? $model : new SubscriptionBillingInformation;

        if ($model->load(Yii::$app->request->post()) && $model->validate()) {

            $model->user_id = Yii::$app->user->id;
            $model->save(false);

            Yii::$app->getSession()->setFlash(
                'success',
                Yii::t('app', 'Your billing information has been successfully updated.')
            );

            return $this->redirect(['/subscription/user/billing-info']);
        }

        return $this->render('billing-info', [
            'model' => $model,
        ]);
    }
}