turnstile

Drupal paywall plugin
Log | Files | Refs | README | LICENSE

commit cde2563f6d36b5a74971c82eb0e060a3b1613d30
parent b0ba51442b71776779dcb09776e9257856fed10d
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 11 Oct 2025 22:58:12 +0200

add subscription prices to settings, update TODO list

Diffstat:
MREADME.md | 17+++++++++--------
Msrc/Form/TurnstileSettingsForm.php | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 99 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md @@ -46,14 +46,15 @@ Navigate to `/admin/config/content/Turnstile` to configure: ## TODO -- add subscription prices to settings -- actually get subscriptions to work: v1 contracts, etc. - note that the logic is now more funky, customers could have a 50%-off - subscription. So need to check if we have a subscription cookie - set for a subscription that for the current category would give us - a price of zero! -- move HTTP logic from TurnstileSettingsForm to the API class! -- use order expiration from merchant backend (with new v1.1 implementation) +- REFACTOR: move HTTP logic from TurnstileSettingsForm to the API class! +- actually get subscriptions to work: + * use new subscription prices( \Drupal::config('turnstile.settings')[$subscription_id][$currency_code] ) + * empty means no choice to buy this subscription in this currency! + * when adding choice to buy subscription, need to ADD price of subscription + price of article with that subscription + for the purchase price! + * update session state to remember in session which subscriptions user proved to us and when they expire + * *if* user is about to buy an article for which under a non-expired subscription we have a price of 0, then skip payment +- LATER: use order expiration from merchant backend (with new v1.1 implementation) instead of hard-coding 1 day! ## How it Works diff --git a/src/Form/TurnstileSettingsForm.php b/src/Form/TurnstileSettingsForm.php @@ -12,6 +12,7 @@ use Drupal\field\Entity\FieldConfig; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\turnstile\TurnstileFieldManager; +use Drupal\turnstile\TalerMerchantApiService; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -34,17 +35,29 @@ class TurnstileSettingsForm extends ConfigFormBase { protected $fieldManager; /** + * The Taler Merchant API service. + * + * @var \Drupal\turnstile\TalerMerchantApiService + */ + protected $apiService; + + /** * Constructs a TurnstileSettingsForm object. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The factory for configuration objects. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. + * @param \Drupal\turnstile\TurnstileFieldManager $field_manager + * The field manager. + * @param \Drupal\turnstile\TalerMerchantApiService $api_service + * The API service. */ - public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, TurnstileFieldManager $field_manager) { + public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, TurnstileFieldManager $field_manager, TalerMerchantApiService $api_service) { parent::__construct($config_factory); $this->entityTypeManager = $entity_type_manager; $this->fieldManager = $field_manager; + $this->apiService = $api_service; } /** @@ -54,7 +67,8 @@ class TurnstileSettingsForm extends ConfigFormBase { return new static( $container->get('config.factory'), $container->get('entity_type.manager'), - $container->get('turnstile.field_manager') + $container->get('turnstile.field_manager'), + $container->get('turnstile.api_service') ); } @@ -116,15 +130,87 @@ class TurnstileSettingsForm extends ConfigFormBase { '#default_value' => $config->get('grant_access_on_error') ?: '', ]; + // Get subscriptions and currencies from API. + $subscriptions = $this->apiService->getSubscriptions(); + $currencies = $this->apiService->getCurrencies(); + + if (empty($subscriptions) || empty($currencies)) { + $this->messenger()->addWarning($this->t('Unable to load subscriptions or currencies from API. Please check your configuration.')); + } + + $form['subscription_prices'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Subscription prices'), + '#description' => $this->t('Set the price for buying each subscription type in different currencies.'), + '#tree' => TRUE, + ]; + + $existing_prices = $config->get('subscription_prices') ?: []; + + foreach ($subscriptions as $subscription) { + $subscription_id = $subscription['id'] ?? $subscription['name']; + $subscription_label = $subscription['label'] ?? $subscription['name']; + + // Skip the %none% case as you can't buy "no subscription" + if ($subscription_id === '%none%') { + continue; + } + + $form['subscription_prices'][$subscription_id] = [ + '#type' => 'details', + '#title' => $subscription_label, + '#open' => FALSE, + ]; + + foreach ($currencies as $currency) { + $currency_code = $currency['code'] ?? $currency['name']; + $currency_label = $currency['label'] ?? $currency['code']; + + $form['subscription_prices'][$subscription_id][$currency_code] = [ + '#type' => 'number', + '#title' => $currency_label, + '#default_value' => $existing_prices[$subscription_id][$currency_code] ?? '', + '#min' => 0, + '#step' => $currency['step'] ?? 0.01, + '#size' => 20, + '#description' => $this->t('Leave empty to prevent buying the subscription with this currency.'), + ]; + } + } + return parent::buildForm($form, $form_state); } + /** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { parent::validateForm($form, $form_state); + // Validate subscription prices + $subscription_prices = $form_state->getValue('subscription_prices'); + if (is_array($subscription_prices)) { + foreach ($subscription_prices as $subscription_id => $currencies) { + if (is_array($currencies)) { + foreach ($currencies as $currency_code => $price) { + // Skip empty values as they are allowed (treated as zero). + if ($price === '' || $price === NULL) { + continue; + } + + // Validate that the price is a valid non-negative number. + if (!is_numeric($price) || $price < 0) { + $form_state->setErrorByName( + "subscription_prices[$subscription_id][$currency_code]", + $this->t('Subscription prices cannot be negative.') + ); + } + } + } + } + } + // Test the access token and backend URL. $payment_backend_url = $form_state->getValue('payment_backend_url'); $access_token = $form_state->getValue('access_token'); @@ -284,12 +370,14 @@ class TurnstileSettingsForm extends ConfigFormBase { } $grant_access_on_error = $form_state->getValue('grant_access_on_error'); + $subscription_prices = $form_state->getValue('subscription_prices'); // Save configuration. $config->set('enabled_content_types', $new_enabled_types); $config->set('payment_backend_url', $payment_backend_url); $config->set('access_token', $access_token); $config->set('grant_access_on_error', $grant_access_on_error); + $config->set('subscription_prices', $subscription_prices); $config->save(); parent::submitForm($form, $form_state);