SubscriptionPricesForm.php (6903B)
1 <?php 2 3 namespace Drupal\taler_turnstile\Form; 4 5 use Drupal\Core\Form\ConfigFormBase; 6 use Drupal\Core\Form\FormStateInterface; 7 use Drupal\Core\Config\ConfigFactoryInterface; 8 use Drupal\Core\Url; 9 use Drupal\taler_turnstile\TalerMerchantApiService; 10 use Symfony\Component\DependencyInjection\ContainerInterface; 11 12 /** 13 * Configure subscription prices. 14 */ 15 class SubscriptionPricesForm extends ConfigFormBase { 16 17 /** 18 * The Taler Merchant API service. 19 * 20 * @var \Drupal\taler_turnstile\TalerMerchantApiService 21 */ 22 protected $apiService; 23 24 /** 25 * Constructs a SubscriptionPricesForm object. 26 * 27 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory 28 * The factory for configuration objects. 29 * @param \Drupal\taler_turnstile\TalerMerchantApiService $api_service 30 * The API service. 31 */ 32 public function __construct(ConfigFactoryInterface $config_factory, TalerMerchantApiService $api_service) { 33 parent::__construct($config_factory); 34 $this->apiService = $api_service; 35 } 36 37 /** 38 * {@inheritdoc} 39 */ 40 public static function create(ContainerInterface $container) { 41 return new static( 42 $container->get('config.factory'), 43 $container->get('taler_turnstile.api_service') 44 ); 45 } 46 47 /** 48 * {@inheritdoc} 49 */ 50 protected function getEditableConfigNames() { 51 return ['taler_turnstile.settings']; 52 } 53 54 /** 55 * {@inheritdoc} 56 */ 57 public function getFormId() { 58 return 'taler_turnstile_subscription_prices_form'; 59 } 60 61 /** 62 * {@inheritdoc} 63 */ 64 public function buildForm(array $form, FormStateInterface $form_state) { 65 $config = $this->config('taler_turnstile.settings'); 66 67 // Check if backend is configured 68 $backend_url = $config->get('payment_backend_url'); 69 $access_token = $config->get('access_token'); 70 71 if (empty($backend_url) || empty($access_token)) { 72 $this->messenger()->addError( 73 $this->t('Turnstile payment backend is not configured. Please <a href="@url">configure the backend</a> first.', [ 74 '@url' => Url::fromRoute('taler_turnstile.settings')->toString(), 75 ]) 76 ); 77 return parent::buildForm($form, $form_state); 78 } 79 80 $form['description'] = [ 81 '#type' => 'item', 82 '#markup' => $this->t('<p>Set the price for buying each subscription type in different currencies. Leave a field empty to prevent users from buying that subscription with that currency.</p>'), 83 ]; 84 85 // Get subscriptions and currencies from API. Show the specific 86 // backend error (timeout / 401 / 502 / ...) so the admin doesn't 87 // have to dig through the logs to figure out what's wrong. 88 $subs_result = $this->apiService->getSubscriptions(); 89 $cur_result = $this->apiService->getCurrencies(); 90 91 if (!$cur_result->isOk()) { 92 $this->messenger()->addError($cur_result->toUserMessage($backend_url)); 93 return parent::buildForm($form, $form_state); 94 } 95 $currencies = is_array($cur_result->data) ? $cur_result->data : []; 96 if (empty($currencies)) { 97 $this->messenger()->addError($this->t('The merchant backend returned no currencies.')); 98 return parent::buildForm($form, $form_state); 99 } 100 101 if (!$subs_result->isOk()) { 102 $this->messenger()->addError($subs_result->toUserMessage($backend_url)); 103 return parent::buildForm($form, $form_state); 104 } 105 // %none% is always present; strip it to count "real" subscriptions. 106 $subscriptions = is_array($subs_result->data) ? $subs_result->data : []; 107 $real = $subscriptions; 108 unset($real['%none%']); 109 if (empty($real)) { 110 $this->messenger()->addWarning($this->t('No subscriptions configured in Taler merchant backend.')); 111 return parent::buildForm($form, $form_state); 112 } 113 114 $form['subscription_prices'] = [ 115 '#type' => 'fieldset', 116 '#title' => $this->t('Subscription Prices'), 117 '#description' => $this->t('Set the price for buying each subscription type in different currencies.'), 118 '#tree' => TRUE, 119 ]; 120 121 $existing_prices = $config->get('subscription_prices') ?: []; 122 123 foreach ($subscriptions as $subscription_id => $subscription) { 124 $subscription_label = $subscription['label'] ?? $subscription['name']; 125 126 // Skip the %none% case as you can't buy "no subscription" 127 if ($subscription_id === '%none%') { 128 continue; 129 } 130 131 $form['subscription_prices'][$subscription_id] = [ 132 '#type' => 'details', 133 '#title' => $subscription_label, 134 '#description' => $subscription['description'] ?? '', 135 '#open' => TRUE, 136 ]; 137 138 foreach ($currencies as $currency) { 139 $currency_code = $currency['code']; 140 $currency_label = $currency['label'] ?? $currency['code']; 141 142 $form['subscription_prices'][$subscription_id][$currency_code] = [ 143 '#type' => 'number', 144 '#title' => $currency_label, 145 '#default_value' => $existing_prices[$subscription_id][$currency_code] ?? '', 146 '#min' => 0, 147 // HTML's step attribute wants a bare decimal ("0.01"), 148 // not a Taler amount ("EUR:0.01"). 149 '#step' => isset($currency['step']) 150 ? $currency['step']->numericString() 151 : '0.01', 152 '#size' => 20, 153 '#description' => $this->t('Leave empty to prevent buying this subscription with @currency.', [ 154 '@currency' => $currency_code, 155 ]), 156 ]; 157 } // for all currencies 158 } // for all subscriptions 159 160 return parent::buildForm($form, $form_state); 161 } 162 163 /** 164 * {@inheritdoc} 165 */ 166 public function validateForm(array &$form, FormStateInterface $form_state) { 167 parent::validateForm($form, $form_state); 168 169 // Validate subscription prices 170 $subscription_prices = $form_state->getValue('subscription_prices'); 171 if (is_array($subscription_prices)) { 172 foreach ($subscription_prices as $subscription_id => $currencies) { 173 if (is_array($currencies)) { 174 foreach ($currencies as $currency_code => $price) { 175 // Skip empty values as they are allowed 176 if ($price === '' || $price === NULL) { 177 continue; 178 } 179 180 // Validate that the price is a valid non-negative number 181 if (!is_numeric($price) || $price < 0) { 182 $form_state->setErrorByName( 183 "subscription_prices][{$subscription_id}][{$currency_code}", 184 $this->t('Subscription prices cannot be negative.') 185 ); 186 } 187 } 188 } 189 } 190 } 191 } 192 193 194 /** 195 * {@inheritdoc} 196 */ 197 public function submitForm(array &$form, FormStateInterface $form_state) { 198 $config = $this->config('taler_turnstile.settings'); 199 $subscription_prices = $form_state->getValue('subscription_prices'); 200 201 $config->set('subscription_prices', $subscription_prices); 202 $config->save(); 203 204 parent::submitForm($form, $form_state); 205 } 206 207 }