/home/edulekha/crm.edulekha.com/application/libraries/gateways/Stripe_ideal_gateway.php
<?php

defined('BASEPATH') or exit('No direct script access allowed');

class Stripe_ideal_gateway extends App_gateway
{
    public $webhookEndPoint;

    public $apiVersion = '2019-08-14';

    public $webhookEvents = ['source.chargeable', 'source.failed', 'source.canceled'];

	public bool $processingFees = false;

	public function __construct()
    {
        $this->webhookEndPoint = site_url('gateways/stripe_ideal/webhook');

        /**
        * Call App_gateway __construct function
        */
        parent::__construct();

        /**
        * REQUIRED
        * Gateway unique id
        * The ID must be alpha/alphanumeric
        */
        $this->setId('stripe_ideal');

        /**
         * REQUIRED
         * Gateway name
         */
        $this->setName('Stripe iDEAL');

        /**
         * Add gateway settings
        */
        $this->setSettings([
            [
                'name'      => 'api_secret_key',
                'encrypted' => true,
                'label'     => 'settings_paymentmethod_stripe_api_secret_key',
            ],
            [
                'name'  => 'api_publishable_key',
                'label' => 'settings_paymentmethod_stripe_api_publishable_key',
            ],
            [
                'name'          => 'description_dashboard',
                'label'         => 'settings_paymentmethod_description',
                'type'          => 'textarea',
                'default_value' => 'Payment for Invoice {invoice_number}',
            ],
            [
                'name'             => 'statement_descriptor',
                'label'            => 'ideal_customer_statement_descriptor',
                'type'             => 'textarea',
                'default_value'    => 'Payment for Invoice {invoice_number}',
                'field_attributes' => ['maxlength' => 22],
                'after'            => '<p class="mbot15">Statement descriptors are limited to 22 characters, cannot use the special characters <, >, \', ", or *, and must not consist solely of numbers.</p>',
            ],
            [
                'name'             => 'currencies',
                'label'            => 'settings_paymentmethod_currencies',
                'default_value'    => 'EUR',
                'field_attributes' => ['disabled' => true],
            ],
        ]);

        hooks()->add_action('before_render_payment_gateway_settings', 'stripe_ideal_gateway_webhook_check');
        hooks()->add_action('before_render_payment_gateway_settings', 'stripe_ideal_gateway_depreciation_notice');
    }

    /**
     * Determine the Stripe environment based on the keys
     *
     * @return string
     */
    public function environment()
    {
        $environment = 'production';
        $apiKey      = $this->decryptSetting('api_secret_key');

        if (strpos($apiKey, 'sk_test') !== false) {
            $environment = 'test';
        }

        return $environment;
    }

    /**
     * Check whether the environment is test
     *
     * @return boolean
     */
    public function is_test()
    {
        return $this->environment() === 'test';
    }

    /**
     * Get the current webhook object based on the endpoint
     *
     * @return boolean|\Stripe\WebhookEndpoint
     */
    public function get_webhook_object()
    {
        \Stripe\Stripe::setApiVersion($this->apiVersion);
        \Stripe\Stripe::setApiKey($this->decryptSetting('api_secret_key'));

        $endpoints = \Stripe\WebhookEndpoint::all();
        $webhook   = false;

        foreach ($endpoints->data as $endpoint) {
            if ($endpoint->url == $this->webhookEndPoint) {
                $webhook = $endpoint;

                break;
            }
        }

        return $webhook;
    }

    public function create_webhook()
    {
        \Stripe\Stripe::setApiVersion($this->apiVersion);
        \Stripe\Stripe::setApiKey($this->decryptSetting('api_secret_key'));

        $webhook = \Stripe\WebhookEndpoint::create([
            'url'            => $this->webhookEndPoint,
            'enabled_events' => $this->webhookEvents,
            'api_version'    => $this->apiVersion,
            'metadata'       => ['identification_key' => $this->get_webhook_identification_key()],
        ]);

        update_option('stripe_ideal_webhook_id', $webhook->id);
        update_option('stripe_ideal_webhook_signing_secret', $webhook->secret);

        return $webhook;
    }

    public function get_webhook_identification_key()
    {
        return 'ideal-' . get_option('identification_key');
    }

    public function get_source($id)
    {
        \Stripe\Stripe::setApiVersion($this->apiVersion);
        \Stripe\Stripe::setApiKey($this->decryptSetting('api_secret_key'));

        return \Stripe\Source::retrieve($id);
    }

    public function charge($source, $amount, $invoice_id)
    {
        \Stripe\Stripe::setApiVersion($this->apiVersion);
        \Stripe\Stripe::setApiKey($this->decryptSetting('api_secret_key'));

        return \Stripe\Charge::create([
                'currency'    => 'eur',
                'amount'      => $amount,
                'source'      => $source,
                'description' => str_replace('{invoice_number}', format_invoice_number($invoice_id), $this->getSetting('description_dashboard')),
                'metadata'    => [
                    'invoice_id'        => $invoice_id,
                    'pcrm-stripe-ideal' => true,
                ],
            ]);
    }

    public function finish_payment($charge)
    {
        $success = $this->addPayment(
            [
                'amount'        => ($charge->amount / 100),
                'invoiceid'     => $charge->metadata->invoice_id,
                'transactionid' => $charge->id,
                'paymentmethod' => strtoupper($charge->source->ideal->bank),
            ]
        );

        return (bool) $success;
    }

    public function process_payment($data)
    {
        $name = $data['invoice']->client->company;
        // Address information
        $country = '';

        $db_country = get_country_short_name($data['invoice']->billing_country);
        if ($db_country != '') {
            $country = $db_country;
        }

        $city        = $data['invoice']->billing_city;
        $line1       = $data['invoice']->billing_street;
        $postal_code = $data['invoice']->billing_zip;
        $state       = $data['invoice']->billing_state;

        $address = [
            'city'        => "$city",
            'country'     => "$country",
            'line1'       => "$line1",
            'postal_code' => "$postal_code",
            'state'       => "$state",
        ];

        $stripe_data = [
            'type'  => 'ideal',
            'ideal' => [
                'statement_descriptor' => str_replace('{invoice_number}', format_invoice_number($data['invoice']->id), $this->getSetting('statement_descriptor')),
            ],
            'amount'   => $data['amount'] * 100,
            'currency' => 'eur',

            'owner' => [
                'name'    => $name,
                'address' => $address,
            ],

            'redirect' => [
                'return_url' => site_url('gateways/stripe_ideal/response/' . $data['invoice']->id . '/' . $data['invoice']->hash),
            ],

            'metadata' => [
                'invoice_id'        => $data['invoice']->id,
                'pcrm-stripe-ideal' => true,
            ],
        ];

        try {
            \Stripe\Stripe::setApiVersion($this->apiVersion);
            \Stripe\Stripe::setApiKey($this->decryptSetting('api_secret_key'));

            $source = \Stripe\Source::create($stripe_data);

            if ($source->created != '') {
                redirect($source->redirect->url);
            } else {
                if (!empty($source->failure_reason)) {
                    set_alert('warning', $source->failure_reason);
                }
            }
        } catch (Exception $e) {
            set_alert('warning', $e->getMessage());
        }

        redirect(site_url('invoice/' . $data['invoice']->id . '/' . $data['invoice']->hash));
    }
}

function stripe_ideal_gateway_webhook_check($gateway)
{
    if ($gateway['id'] === 'stripe_ideal') {
        $CI = &get_instance();

        if (!empty($CI->stripe_ideal_gateway->decryptSetting('api_secret_key')) && $gateway['active'] == '1') {
            try {
                $webhook = $CI->stripe_ideal_gateway->get_webhook_object();
            } catch (Exception $e) {
                echo '<div class="alert alert-warning">';
                // useful when user add wrong keys
                // e.q. This API call cannot be made with a publishable API key. Please use a secret API key. You can find a list of your API keys at https://dashboard.stripe.com/account/apikeys.
                echo $e->getMessage();
                echo '</div>';

                return;
            }

            $environment = $CI->stripe_ideal_gateway->environment();
            $endpoint    = $CI->stripe_ideal_gateway->webhookEndPoint;

            if ($CI->session->has_userdata('stripe-ideal-webhook-failure')) {
                echo '<div class="alert alert-warning" style="margin-bottom:15px;">';
                echo 'The system was unable to create the <b>required</b> webhook endpoint for Stripe Ideal.';
                echo '<br />You should consider creating webhook manually directly via Stripe dashboard for your environment (' . $environment . ')';
                echo '<br /><br /><b>Webhook URL:</b><br />' . $endpoint;
                echo '<br /><br /><b>Webhook events:</b><br />' . implode(',<br />', $CI->stripe_ideal_gateway->webhookEvents());
                echo '</div>';
            }

            if (!$webhook || !startsWith($webhook->url, site_url())) {
                echo '<div class="alert alert-warning">';
                echo 'Webhook endpoint (' . $endpoint . ') not found for ' . $environment . ' environment.';
                echo '<br />Click <a href="' . site_url('gateways/stripe_ideal/create_webhook') . '">here</a> to create the webhook directly in Stripe.';
                echo '</div>';
            } elseif ($webhook && $webhook->id != get_option('stripe_ideal_webhook_id')) {
                echo '<div class="alert alert-warning">';
                echo 'The application stored Stripe webhook id does not match the configured webhook.';
                echo '<br />Click <a href="' . site_url('gateways/stripe_ideal/create_webhook?recreate=true') . '">here</a> to re-create the webhook directly in Stripe and delete the old webhook.';
                echo '</div>';
            } elseif ($webhook && $webhook->status != 'enabled') {
                echo '<div class="alert alert-warning">';
                echo 'Your Stripe configured webhook is disabled, you should consider enabling your webhook via Stripe dashboard or by clicking <a href="' . site_url('gateways/stripe_ideal/enable_webhook') . '">here</a>.';
                echo '</div>';
            }
        }
    }
}

function stripe_ideal_gateway_depreciation_notice(array $gateway)
{
    if ($gateway['id'] === 'stripe_ideal') {
        $moduleURL = admin_url('modules');
        echo <<<HTML
            <div class="alert alert-warning">
                The Stripe iDEAL gateway is deprecated, and Stripe no longer supports its API. We recommend migrating to the new gateway (Stripe iDEAL V2), available as a module. Be sure to activate it in <a href="{$moduleURL}">Setup > Modules</a>
            </div>
        HTML;
    }
}