<?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\services;

use app\modules\subscription\models\SubscriptionProduct;
use app\modules\subscription\services\paypal\SubscriptionsCancelPostRequest;
use app\modules\subscription\services\paypal\SubscriptionsGetRequest;
use app\modules\subscription\services\paypal\SubscriptionsRevisePostRequest;
use app\modules\subscription\services\paypal\SubscriptionTransactionsGetRequest;
use app\modules\subscription\services\paypal\WebhooksVerifySignaturePostRequest;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Orders\OrdersGetRequest;
use Yii;
use yii\base\InvalidArgumentException;
use yii\web\HeaderCollection;

/**
 * Class PaypalClient
 * @package app\modules\subscription\services
 */
class PaypalClient
{

    protected $client = null;
    protected $environment = null;
    protected $clientId = null;
    protected $clientSecret = null;
    protected $webhookId = null;

    /**
     * PaypalClient constructor.
     */
    public function __construct()
    {
        $settings = Yii::$app->settings;

        // Paypal env
        $environment = $settings->get('subscription.environment');
        $environment = $environment == SubscriptionProduct::MODE_LIVE ? 'production' : 'sandbox';

        // Paypal keys
        $clientId = $settings->get('subscription.paypalLiveClientID');
        $clientSecret = $settings->get('subscription.paypalLiveClientSecret');

        // Paypal Webhook ID
        $webhookId = $settings->get('subscription.paypalLiveWebhookId');

        if ($environment === 'sandbox') {
            $clientId = $settings->get('subscription.paypalSandboxClientID');
            $clientSecret = $settings->get('subscription.paypalSandboxClientSecret');
            $webhookId = $settings->get('subscription.paypalSandboxWebhookId');
        }

        if (empty($environment) || empty($clientId) || empty($clientSecret)) {
            throw new InvalidArgumentException();
        }

        $this->environment = $environment;
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->client = new PayPalHttpClient($this->getEnvironment());
        $this->webhookId = $webhookId;
    }

    /**
     * Get Paypal Environment
     *
     * Set up and return PayPal PHP SDK environment with PayPal access credentials.
     * This sample uses SandboxEnvironment. In production, use LiveEnvironment.
     */
    public function getEnvironment()
    {
        if ($this->environment === 'production') {
            return new ProductionEnvironment(
                $this->clientId,
                $this->clientSecret
            );
        }

        return new SandboxEnvironment(
            $this->clientId,
            $this->clientSecret
        );
    }

    /**
     * Get Paypal Client
     *
     * Returns PayPal HTTP client instance with environment that has access
     * credentials context. Use this instance to invoke PayPal APIs, provided the
     * credentials have access.
     */
    public function getClient()
    {
        return $this->client;
    }

    /**
     * You can use this function to retrieve an order by passing order ID as an argument.
     *
     * @param $orderId
     * @return array|bool|string
     */
    public function getOrder($orderId)
    {
        // Call PayPal to get the transaction details
        $response = $this->client->execute(new OrdersGetRequest($orderId));

        if (isset($response->result, $response->result->id)) {
            return $response->result;
        }

        return false;
    }

    /**
     * Get Paypal Subscription object
     *
     * @param $subscriptionId
     * @return array|false|string
     */
    public function getSubscription($subscriptionId)
    {
        // Call PayPal to get the subscription details
        $response = $this->client->execute(new SubscriptionsGetRequest($subscriptionId));

        if (isset($response->result, $response->result->id)) {
            return $response->result;
        }

        return false;
    }

    /**
     * Get Paypal Transaction Object
     *
     * @param $subscriptionId
     * @param $startTime
     * @param $endTime
     * @return false
     */
    public function getSubscriptionTransactions($subscriptionId, $startTime, $endTime)
    {
        // Call PayPal to get the subscription transactions
        $response = $this->client->execute(new SubscriptionTransactionsGetRequest($subscriptionId, $startTime, $endTime));

        if (isset($response->result, $response->result->transactions)) {
            return $response->result->transactions;
        }

        return false;
    }

    /**
     * Revise Paypal Subscription
     *
     * @param $subscriptionId
     * @param $planId
     * @return array|false|string
     */
    public function reviseSubscription($subscriptionId, $planId)
    {
        // Call PayPal to revise the subscription
        $response = $this->client->execute(new SubscriptionsRevisePostRequest($subscriptionId, $planId));

        if (isset($response->result, $response->result->plan_id)) {
            return $response->result;
        }

        return false;
    }

    /**
     * Cancel Paypal Subscription
     *
     * @param $subscriptionId
     * @return bool
     */
    public function cancelSubscription($subscriptionId)
    {
        // Call PayPal to cancel the subscription
        $response = $this->client->execute(new SubscriptionsCancelPostRequest($subscriptionId));

        return isset($response->statusCode) && (int) $response->statusCode === 204;
    }

    /**
     * Verify Webhook Signature
     *
     * @param HeaderCollection $headers
     * @param array $payload
     * @return bool
     */
    public function verifyWebhookSignature($headers, $payload)
    {
        // Call PayPal to verify webhook signature
        $response = $this->client->execute(new WebhooksVerifySignaturePostRequest(
            $headers->get('PAYPAL-AUTH-ALGO'),
            $headers->get('PAYPAL-CERT-URL'),
            $headers->get('PAYPAL-TRANSMISSION-ID'),
            $headers->get('PAYPAL-TRANSMISSION-SIG'),
            $headers->get('PAYPAL-TRANSMISSION-TIME'),
            $this->webhookId,
            $payload
        ));

        return isset($response->result->verification_status) && $response->result->verification_status === 'SUCCESS';
    }

}
