apigee_m10n-8.x-1.7/src/Entity/Form/PurchasedProductForm.php
src/Entity/Form/PurchasedProductForm.php
<?php /* * Copyright 2021 Google Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ namespace Drupal\apigee_m10n\Entity\Form; use Apigee\Edge\Api\Monetization\Entity\LegalEntityInterface; use Apigee\Edge\Exception\ClientErrorException; use Drupal\Core\Cache\Cache; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\apigee_edge\Entity\Developer; use Drupal\apigee_edge\Entity\Form\FieldableEdgeEntityForm; use Drupal\apigee_m10n\Form\PrepaidBalanceConfigForm; use Drupal\apigee_m10n\MonetizationInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Purchased plan entity form. */ class PurchasedProductForm extends FieldableEdgeEntityForm { /** * Insufficient funds API error code. */ public const INSUFFICIENT_FUNDS_ERROR = 'mint.service.developer_usage_exceeds_balance'; public const DEVELOPER_WALLET_DOES_NOT_EXIST = 'keymanagement.service.developer_wallet_does_not_exist'; public const MY_PURCHASES_PRODUCT_CACHE_TAG = 'apigee_my_purchased_products'; /** * Messanger service. * * @var \Drupal\Core\Messenger\MessengerInterface */ protected $messenger; /** * The current_route_match service. * * @var \Drupal\Core\Routing\RouteMatchInterface */ protected $currentRouteMatch; /** * Apigee Monetization utility service. * * @var \Drupal\apigee_m10n\MonetizationInterface */ protected $monetization; /** * The config factory. * * @var \Drupal\Core\Config\ConfigFactoryInterface */ protected $config; /** * The module handler. * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ protected $moduleHandler; /** * The current user. * * @var \Drupal\Core\Session\AccountInterface */ protected $currentUser; /** * Constructs a PurchasedPlanForm object. * * @param \Drupal\Core\Messenger\MessengerInterface $messenger * Messenger service. * @param \Drupal\Core\Routing\RouteMatchInterface $current_route_match * The current_route_match service. * @param \Drupal\apigee_m10n\MonetizationInterface $monetization * Apigee Monetization utility service. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Drupal\Core\Session\AccountInterface $current_user * The current user. */ public function __construct(MessengerInterface $messenger, RouteMatchInterface $current_route_match, MonetizationInterface $monetization, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, AccountInterface $current_user) { $this->messenger = $messenger; $this->currentRouteMatch = $current_route_match; $this->monetization = $monetization; $this->config = $config_factory; $this->moduleHandler = $module_handler; $this->currentUser = $current_user; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('messenger'), $container->get('current_route_match'), $container->get('apigee_m10n.monetization'), $container->get('config.factory'), $container->get('module_handler'), $container->get('current_user') ); } /** * {@inheritdoc} */ public function getFormId() { // @todo Make sure we find a better way to handle names // without adding rate plan ID this form is getting cached // and when rendered as a formatter. // Also known issue in core @see https://www.drupal.org/project/drupal/issues/766146. return parent::getFormId() . '_' . $this->getEntity()->getRatePlan()->id(); } /** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); // Redirect to Rate Plan detail page on submit. $form['#action'] = $this->getEntity()->getRatePlan()->toUrl('purchase')->toString(); return $form; } /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { // If the user has already purchased this plan, show a message instead. /** @var \Drupal\apigee_m10n\Entity\RatePlanInterface $rate_plan */ $rate_plan = $this->getEntity()->getRatePlan(); if (($rate_plan = $this->getEntity()->getRatePlan()) && ($this->monetization->isDeveloperAlreadySubscribedX($this->currentUser->getEmail(), $rate_plan))) { return [ '#markup' => $this->t('You have already purchased %rate_plan.', [ '%rate_plan' => $rate_plan->getDisplayName(), ]), ]; } $form = parent::buildForm($form, $form_state); // We can't alter the form in the form() method because the actions buttons // get added on buildForm(). $this->insufficientFundsWorkflow($form, $form_state); return $form; } /** * {@inheritdoc} */ protected function actions(array $form, FormStateInterface $form_state) { $actions = parent::actions($form, $form_state); // Set the save label if one has been passed into storage. if (!empty($actions['submit']) && ($save_label = $form_state->get('save_label'))) { $actions['submit']['#value'] = $save_label; $actions['submit']['#button_type'] = 'primary'; } return $actions; } /** * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { try { // Auto assign legal name. $developer_id = $this->getEntity()->getDeveloper()->getEmail(); $developer = Developer::load($developer_id); $display_name = $this->getEntity()->getRatePlan()->getDisplayName(); Cache::invalidateTags([static::MY_PURCHASES_PRODUCT_CACHE_TAG]); if ($this->getEntity()->save()) { $this->messenger->addStatus($this->t('You have purchased %label plan', [ '%label' => $display_name, ])); $form_state->setRedirect('entity.purchased_product.developer_product_collection', ['user' => $this->getEntity()->getOwnerId()]); // Clear the cache to change the buy product page. $renderCache = \Drupal::service('cache.render'); $renderCache->invalidateAll(); } else { $this->messenger->addWarning($this->t('Unable to purchase %label plan', [ '%label' => $display_name, ])); } } catch (\Exception $e) { $client_error = $e->getPrevious(); if (($client_error instanceof ClientErrorException && $client_error->getEdgeErrorCode() === static::INSUFFICIENT_FUNDS_ERROR) || ($client_error instanceof ClientErrorException && $client_error->getEdgeErrorCode() === static::DEVELOPER_WALLET_DOES_NOT_EXIST)) { $rate_plan = $this->getEntity()->getRatePlan(); $minimum_amount = $rate_plan->getSetupFeesPriceValue(); $minimum_amount = reset($minimum_amount); $currency_id = $rate_plan->getCurrencyCode(); $amount_formatted = $this->monetization->formatCurrency($minimum_amount['amount'], $currency_id); $insufficient_funds_error_message = $this->t('You have insufficient funds to purchase plan %plan. @adenndum', [ '%plan' => $rate_plan->label(), '%amount' => $amount_formatted, '@adenndum' => $this->t('To purchase this plan you are required to add at least %amount to your account.', ['%amount' => $amount_formatted]), ]); $purchased_product = $this->getEntity(); $this->moduleHandler->alter('apigee_m10n_insufficient_balance_error_purchased_product_message', $insufficient_funds_error_message, $purchased_product); $this->messenger->addError($insufficient_funds_error_message); } else { $this->messenger->addError($e->getMessage()); } } } /** * Insufficient funds workflow. * * Handles the "add credit" link and purchase button status on purchase rate plan forms. * * @param array $form * The form to alter. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. * * @throws \Exception */ protected function insufficientFundsWorkflow(array &$form, FormStateInterface $form_state) { // Check if insufficient_funds_workflow is disabled, and do nothing if so. if (!$this->config->get(PrepaidBalanceConfigForm::CONFIG_NAME)->get('enable_insufficient_funds_workflow') !== TRUE) { return; } /** @var \Drupal\apigee_m10n\Entity\PurchasedProduct $purchased_product */ $purchased_product = $form_state->getFormObject()->getEntity(); $rate_plan = $purchased_product->getRatePlan(); $user = $purchased_product->getOwner(); /** @var \Drupal\apigee_m10n\ApigeeSdkControllerFactory $sdk */ $sdk = \Drupal::service('apigee_m10n.sdk_controller_factory'); try { $developer_billing_type = $sdk->developerBillingTypeController($user->getEmail())->getAllBillingDetails(); } catch (\Exception $e) { $developer_billing_type = NULL; } // If developer is prepaid, check for sufficient balance to purchase to the // rate plan. if ($developer_billing_type && $developer_billing_type->getbillingType() == LegalEntityInterface::BILLING_TYPE_PREPAID) { $prepaid_balances = []; foreach ($this->monetization->getDeveloperPrepaidBalancesX($user) as $prepaid_balance) { $prepaid_balances[$prepaid_balance->getBalance()->getCurrencyCode()] = $prepaid_balance->getBalance()->getUnits() + $prepaid_balance->getBalance()->getNanos(); } // Minimum balance needed is at least the setup fee. // @see https://docs.apigee.com/api-platform/monetization/create-rate-plans.html#rateplanops $setup_fee = $rate_plan->getRatePlanxFee(); $min_balance_needed = 0; foreach ($setup_fee as $key => $value) { $nanos = $value->getNanos() ?? 0; $units = $value->getUnits() ?? 0; $min_balance_needed = $units + $nanos; } $currency_id = $rate_plan->getCurrencyCode(); $prepaid_balances[$currency_id] = $prepaid_balances[$currency_id] ?? 0; if ($min_balance_needed > $prepaid_balances[$currency_id]) { $form['insufficient_balance'] = [ '#type' => 'container', ]; $form['insufficient_balance'][strtolower($currency_id)] = [ 'message' => [ '#type' => 'html_tag', '#tag' => 'p', '#value' => $this->t('You have insufficient funds to purchase this rate plan.'), ], ]; $form['startDate']['#access'] = FALSE; $form['actions']['submit']['#access'] = FALSE; } } } }