braintree_cashier-8.x-4.x-dev/src/Form/PlanSelectFormBase.php
src/Form/PlanSelectFormBase.php
<?php
namespace Drupal\braintree_cashier\Form;
use Drupal\braintree_api\BraintreeApiService;
use Drupal\braintree_cashier\Entity\BraintreeCashierBillingPlan;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\user\Entity\User;
use Money\Currencies\ISOCurrencies;
use Money\Formatter\IntlMoneyFormatter;
use Money\Parser\DecimalMoneyParser;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\braintree_cashier\BraintreeCashierService;
/**
* Class PlanSelectFormBase.
*/
class PlanSelectFormBase extends FormBase {
/**
* Symfony\Component\HttpFoundation\RequestStack definition.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The billing plan entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $billingPlanStorage;
/**
* The braintree API service.
*
* @var \Drupal\braintree_api\BraintreeApiService
*/
protected $braintreeApi;
/**
* The Braintree Cashier logger channel.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* The discount entity storate.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $discountStorage;
/**
* The money parser.
*
* @var \Money\MoneyParser
*/
protected $moneyParser;
/**
* The international money formatter.
*
* @var \Money\Formatter\IntlMoneyFormatter
*/
protected $moneyFormatter;
/**
* The braintree cashier service.
*
* @var \Drupal\braintree_cashier\BraintreeCashierService
*/
protected $bcService;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a new PlanSelectFormBase object.
*/
public function __construct(RequestStack $request_stack, EntityTypeManagerInterface $entityTypeManager, BraintreeApiService $braintreeApi, LoggerChannelInterface $logger, BraintreeCashierService $braintreeCashierService) {
$this->requestStack = $request_stack;
$this->billingPlanStorage = $entityTypeManager->getStorage('braintree_cashier_billing_plan');
$this->braintreeApi = $braintreeApi;
$this->logger = $logger;
$this->discountStorage = $entityTypeManager->getStorage('braintree_cashier_discount');
$this->bcService = $braintreeCashierService;
$this->entityTypeManager = $entityTypeManager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('request_stack'),
$container->get('entity_type.manager'),
$container->get('braintree_api.braintree_api'),
$container->get('logger.channel.braintree_cashier'),
$container->get('braintree_cashier.braintree_cashier_service')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'plan_select_form_base';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Get the plan ID from the query parameter.
$plan_id = $this->requestStack->getCurrentRequest()->query->get('plan_id');
if (empty($plan_id)) {
// Since the plan ID wasn't found in the URL, try to get it from the
// session.
$plan_id = $this->requestStack->getCurrentRequest()
->getSession()
->get('plan_id');
}
if (!empty($plan_id) && is_numeric($plan_id)) {
/** @var \Drupal\braintree_cashier\Entity\BraintreeCashierBillingPlanInterface $billing_plan */
$billing_plan = $this->billingPlanStorage->load($plan_id);
if (!empty($billing_plan) && $billing_plan->isAvailableForPurchase()) {
$default_value = $billing_plan->id();
}
}
$form['plan_entity_id'] = [
'#type' => 'select',
'#title' => $this->t('Choose a plan'),
'#attributes' => [
'id' => 'edit-plan-entity-id',
],
'#options' => $this->getBillingPlanOptions(),
'#multiple' => FALSE,
'#default_value' => !empty($default_value) ? $default_value : NULL,
'#attached' => [
'library' => [
'braintree_cashier/plan_select',
],
],
];
if ($this->config('braintree_cashier.settings')->get('enable_coupon_field')) {
$form['coupon_container'] = [
'#type' => 'fieldset',
'#title' => $this->t('Coupon'),
'#attributes' => [
'class' => [
'container-inline',
'fieldgroup',
'form-composite',
],
],
];
$form['coupon_container']['coupon_code'] = [
'#type' => 'textfield',
'#title' => $this->t('Coupon code'),
'#title_display' => 'invisible',
'#attributes' => [
'id' => 'coupon-code',
'placeholder' => $this->t('Coupon code'),
],
];
$form['coupon_container']['confirm_coupon_code'] = [
'#type' => 'button',
'#name' => 'confirm_coupon',
'#value' => $this->t('Confirm coupon'),
'#ajax' => [
'callback' => '::confirmCouponCode',
'progress' => [
'type' => 'throbber',
'message' => NULL,
],
],
'#submit' => [[$this, 'confirmCouponSubmit']],
'#limit_validation_errors' => [],
'#suffix' => '<div id="coupon-result"></div>',
];
}
$entity_type_definition = $this->entityTypeManager->getDefinition('braintree_cashier_billing_plan');
$form['#cache']['tags'] = $entity_type_definition->getListCacheTags();
return $form;
}
/**
* Determine the billing plan options for the drop-down select list.
*
* @return array
* Return an array keyed by the billing plan entity id with a value
* that is the billing plan description.
*/
public function getBillingPlanOptions() {
$query = $this->billingPlanStorage->getQuery();
$query->condition('is_available_for_purchase', TRUE)
->condition('environment', $this->braintreeApi->getEnvironment())
->sort('weight');
$entity_ids = $query->execute();
$options = [];
foreach ($entity_ids as $entity_id) {
/** @var \Drupal\braintree_cashier\Entity\BraintreeCashierBillingPlan $billing_plan */
$billing_plan = $this->billingPlanStorage->load($entity_id);
$options[$billing_plan->id()] = $billing_plan->getDescription();
}
return $options;
}
/**
* Submit handler for AJAX coupon confirmation button.
*
* Rebuilds the form.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public function confirmCouponSubmit(array &$form, FormStateInterface $form_state) {
$form_state->setRebuild();
}
/**
* AJAX callback to validate the coupon code.
*/
public function confirmCouponCode(array $form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$coupon_code = $form_state->getValue('coupon_code');
$plan_entity_id = $form_state->getValue('plan_entity_id');
$billing_plan = BraintreeCashierBillingPlan::load($plan_entity_id);
$account = User::load($this->currentUser()->id());
if (empty($billing_plan) || !$this->bcService->discountExists($billing_plan, $coupon_code, $account) || $this->bcService->getBraintreeDiscount($coupon_code) === FALSE) {
$message = [
'#prefix' => '<div class="coupon-result coupon-result--error">',
'#markup' => $this->t('The code %coupon_code is invalid.', [
'%coupon_code' => $coupon_code,
]),
'#suffix' => '</div>',
];
// Remove the coupon code from the text field.
$response->addCommand(new InvokeCommand('#coupon-code', 'val', ['']));
}
else {
$braintree_discount = $this->bcService->getBraintreeDiscount($coupon_code);
// Setup Money.
$currencies = new ISOCurrencies();
$moneyParser = new DecimalMoneyParser($currencies);
$numberFormatter = new \NumberFormatter($this->bcService->getLocale(), \NumberFormatter::CURRENCY);
$moneyFormatter = new IntlMoneyFormatter($numberFormatter, $currencies);
$amount = $moneyParser->parse($braintree_discount->amount, $this->config('braintree_cashier.settings')
->get('currency_code'));
$formatted_amount = $moneyFormatter->format($amount);
/** @var \Drupal\braintree_cashier\Entity\BraintreeCashierBillingPlanInterface $billing_plan */
$billing_plan = $this->billingPlanStorage->load($plan_entity_id);
$braintree_plan = $this->bcService->getBraintreeBillingPlan($billing_plan->getBraintreePlanId());
switch ($braintree_plan->billingFrequency) {
case 1:
$interval = 'month';
break;
case 12:
$interval = 'year';
break;
default:
$interval = $braintree_plan->billingFrequency . ' months';
}
if (empty($braintree_discount->numberOfBillingCycles)) {
$duration = 'every ' . $interval;
}
elseif ($braintree_discount->numberOfBillingCycles == 1) {
$duration = 'for the first ' . $interval;
}
elseif ($braintree_discount->numberOfBillingCycles > 1 && $interval = 'month') {
$duration = 'each month for ' . $braintree_discount->numberOfBillingCycles . ' months';
}
else {
$duration = 'every ' . $interval . ' for ' . $braintree_discount->numberOfBillingCycles . ' billing cycles';
}
$message = [
'#prefix' => '<div class="coupon-result coupon-result--success">',
'#markup' => $this->t('Success! This code gives a discount of %discount_amount @duration.', [
'%discount_amount' => $formatted_amount,
'@duration' => $duration,
]),
'#suffix' => '</div>',
];
$response->addCommand(new InvokeCommand('#coupon-code', 'removeClass', ['error']));
}
$response->addCommand(new HtmlCommand('#coupon-result', $message));
return $response;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
// Validate that the billing plan can be loaded.
$values = $form_state->getValues();
/** @var \Drupal\braintree_cashier\Entity\BraintreeCashierBillingPlanInterface $billing_plan */
$billing_plan = $this->billingPlanStorage->load($values['plan_entity_id']);
/** @var \Drupal\user\Entity\User $account */
$account = User::load($this->currentUser()->id());
if ($form_state->getTriggeringElement()['#name'] != 'confirm_coupon') {
if (empty($billing_plan) || !$billing_plan->isAvailableForPurchase()) {
$message = $this->t('The plan selected, %plan, is invalid. Please choose a different plan', [
'%plan' => $values['plan_entity_id'],
]);
$form_state->setErrorByName('plan_entity_id', $message);
$this->logger->error($message);
}
if (!empty($values['coupon_code']) && !empty($billing_plan) && !$this->bcService->discountExists($billing_plan, $values['coupon_code'], $account)) {
$message = $this->t('The coupon code %coupon_code is invalid.', [
'%coupon_code' => $values['coupon_code'],
]);
$form_state->setErrorByName('coupon_code', $message);
$this->logger->error($message);
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
}
}
