Unverified Commit 24f17028 authored by Tom Ashe's avatar Tom Ashe
Browse files

code enhancements

parent 8e359bea
Loading
Loading
Loading
Loading
Loading
+8 −3
Changes for commerce_stripe.module: 8 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ use Drupal\commerce_payment\Entity\PaymentGatewayInterface;
use Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\StripePaymentElementInterface;
use Drupal\commerce_stripe\PluginForm\Stripe\PaymentMethodAddForm;
use Drupal\commerce_stripe\StripeHelper;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
@@ -155,7 +156,7 @@ function commerce_stripe_preprocess_views_view_field(&$variables) {
/**
 * Implements hook_entity_operation().
 */
function commerce_stripe_entity_operation(EntityInterface $entity) {
function commerce_stripe_entity_operation(EntityInterface $entity, ?CacheableMetadata $cacheability = NULL) {
  $operations = [];
  if (!$entity instanceof PaymentGatewayInterface ||
    !StripeHelper::isStripeGateway($entity->getPlugin())) {
@@ -177,7 +178,9 @@ function commerce_stripe_entity_operation(EntityInterface $entity) {
    $url = Url::fromRoute('commerce_stripe.connect.oauth_connect_form', [
      'commerce_payment_gateway' => $entity->id(),
    ]);
    if ($url->access()) {
    $access = $url->access(NULL, TRUE);
    $cacheability?->addCacheableDependency($access);
    if ($access->isAllowed()) {
      $operations['stripe_connect'] = [
        'title' => t('Connect with Stripe'),
        'url' => $url,
@@ -189,7 +192,9 @@ function commerce_stripe_entity_operation(EntityInterface $entity) {
    $oauth_disconnect = Url::fromRoute('commerce_stripe.connect.oauth_disconnect_form', [
      'commerce_payment_gateway' => $entity->id(),
    ], ['absolute' => TRUE]);
    if ($oauth_disconnect->access()) {
    $access = $oauth_disconnect->access(NULL, TRUE);
    $cacheability?->addCacheableDependency($access);
    if ($access->isAllowed()) {
      $operations['stripe_disconnect'] = [
        'title' => t('Disconnect from Stripe'),
        'url' => $oauth_disconnect,
+32 −4
Changes for src/Controller/StripeConnectController.php: 32 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -7,7 +7,6 @@ use Drupal\commerce_payment\Entity\PaymentGatewayInterface;
use Drupal\commerce_stripe\StripeHelper;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Render\Markup;
use GuzzleHttp\Exception\RequestException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -66,6 +65,29 @@ class StripeConnectController extends ControllerBase {
      !StripeHelper::isStripeGateway($plugin)) {
      throw new AccessDeniedHttpException();
    }

    // Validate the state token to protect against CSRF attacks that could
    // otherwise bind this gateway to an attacker-controlled Stripe account.
    // The token stays in the session (rather than being consumed here) so
    // that a transient network failure or page reload can be retried
    // against the same token instead of hitting a permanent access-denied
    // error. Once the connection actually succeeds below, it's flagged as
    // completed so a later reload of the same URL can be recognized as a
    // harmless repeat rather than re-attempted or rejected.
    $session = $request->getSession();
    $session_key = 'commerce_stripe_connect_state.' . $commerce_payment_gateway->id();
    $stored_state = $session->get($session_key);
    $expected_state = is_array($stored_state) ? ($stored_state['token'] ?? NULL) : NULL;
    $state = $request->query->get('state');
    if (empty($expected_state) || empty($state) || !hash_equals($expected_state, $state)) {
      throw new AccessDeniedHttpException();
    }
    if (!empty($stored_state['completed'])) {
      // This exact connection already succeeded (e.g. the browser lost the
      // response and reloaded); the success message is already queued in
      // the session, so just send them back without repeating the exchange.
      return new RedirectResponse($commerce_payment_gateway->toUrl('collection')->toString());
    }
    $client = $this->clientFactory->fromOptions();
    try {
      $response = $client->post(StripeHelper::BASE_CONNECT_URL . '/oauth/token', [
@@ -75,9 +97,14 @@ class StripeConnectController extends ControllerBase {
      ]);
      $response = Json::decode($response->getBody()->getContents());
      if (isset($response['access_token'], $response['stripe_publishable_key'])) {
        $message = $this->t('Stripe credentials generated. This is the only time they will be displayed. Please copy and store them securely.');
        $html = Markup::create($message . sprintf('<br/><strong>Publishable key:</strong> %s<br/><strong>Access token:</strong> %s<br/><strong>Stripe User ID:</strong> %s', $response['stripe_publishable_key'], $response['access_token'], $response['stripe_user_id'] ?? ''));
        $this->messenger->addWarning($html);
        $access_token = $response['access_token'];
        $masked_access_token = strlen($access_token) > 4 ? str_repeat('•', strlen($access_token) - 4) . substr($access_token, -4) : $access_token;
        $message = $this->t('Stripe credentials generated and saved to the payment gateway configuration.<br/><strong>Publishable key:</strong> @publishable_key<br/><strong>Access token:</strong> @access_token<br/><strong>Stripe User ID:</strong> @stripe_user_id', [
          '@publishable_key' => $response['stripe_publishable_key'],
          '@access_token' => $masked_access_token,
          '@stripe_user_id' => $response['stripe_user_id'] ?? '',
        ]);
        $this->messenger->addWarning($message);
        $configuration = $commerce_payment_gateway->getPluginConfiguration();
        $configuration['publishable_key'] = $response['stripe_publishable_key'];
        $configuration['access_token'] = $response['access_token'];
@@ -87,6 +114,7 @@ class StripeConnectController extends ControllerBase {
        unset($configuration['secret_key']);
        $commerce_payment_gateway->setPluginConfiguration($configuration);
        $commerce_payment_gateway->save();
        $session->set($session_key, ['token' => $expected_state, 'completed' => TRUE]);

        return new RedirectResponse($commerce_payment_gateway->toUrl('collection')->toString());
      }
+23 −2
Changes for src/Form/StripeConnectConfirmationForm.php: 23 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ namespace Drupal\commerce_stripe\Form;

use Drupal\commerce_payment\Entity\PaymentGatewayInterface;
use Drupal\commerce_stripe\StripeHelper;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
@@ -34,10 +35,30 @@ class StripeConnectConfirmationForm extends ConfirmFormBase {
    $form = parent::buildForm($form, $form_state);

    $form['#attached']['library'][] = 'commerce_stripe/connect_buttons';

    // Generate a random state token to protect the OAuth callback against
    // CSRF, and store it in the session so it can be validated on return.
    // The token is embedded directly in the redirect_uri (rather than
    // passed as a separate parameter to the connect middleware) so that it
    // is preserved regardless of whether the middleware understands or
    // relays a "state" parameter of its own: it only needs to append
    // "code" to the redirect_uri it was given, which is required by the
    // OAuth flow itself.
    $state = Crypt::randomBytesBase64(32);
    $this->getRequest()
      ->getSession()
      ->set('commerce_stripe_connect_state.' . $commerce_payment_gateway->id(), [
        'token' => $state,
        'completed' => FALSE,
      ]);
    $oauth_return = Url::fromRoute('commerce_stripe.connect.oauth_return', [
      'commerce_payment_gateway' => $commerce_payment_gateway->id(),
    ], ['absolute' => TRUE])->toString();
    $stripe_connect_url_string = sprintf('%s/oauth/authorize?mode=%s&redirect_uri=%s', StripeHelper::BASE_CONNECT_URL, $commerce_payment_gateway->getPlugin()->getMode(), $oauth_return);
    ], [
      'absolute' => TRUE,
      'query' => ['state' => $state],
    ])->toString();

    $stripe_connect_url_string = sprintf('%s/oauth/authorize?mode=%s&redirect_uri=%s', StripeHelper::BASE_CONNECT_URL, $commerce_payment_gateway->getPlugin()->getMode(), rawurlencode($oauth_return));
    $stripe_connect_url = Url::fromUri($stripe_connect_url_string, [
      'absolute' => TRUE,
    ]);
+6 −7
Changes for src/Plugin/Commerce/PaymentGateway/StripePaymentElement.php: 6 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ use Drupal\commerce_stripe\IntentHelper;
use Drupal\commerce_stripe\Plugin\Commerce\PaymentMethodType\StripePaymentMethodTypeInterface;
use Drupal\commerce_stripe\WebhookEventState;
use Drupal\commerce_stripe_webhook_event\WebhookEvent;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
@@ -970,13 +969,13 @@ class StripePaymentElement extends OffsitePaymentGatewayBase implements StripePa
      $payload = $request->getContent();

      $webhook_signing_secret = $this->getWebhookSigningSecret();
      if (!empty($webhook_signing_secret)) {
        $webhook_event = Webhook::constructEvent($payload, $stripe_signature, $webhook_signing_secret);
      }
      else {
        $data = Json::decode($payload);
        $webhook_event = Event::constructFrom($data);
      if (empty($webhook_signing_secret)) {
        $this->logger->error('Rejected a webhook notification for payment gateway %gateway because no webhook signing secret is configured.', [
          '%gateway' => $this->parentEntity->id(),
        ]);
        return new Response('Webhook signing secret is not configured.', 500);
      }
      $webhook_event = Webhook::constructEvent($payload, $stripe_signature, $webhook_signing_secret);

      $webhook_event_id = NULL;
      if ($this->webhookEventModuleIsInstalled) {
+2 −1
Changes for src/Plugin/Commerce/PaymentMethodType/StripeDefault.php: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -50,7 +50,8 @@ class StripeDefault extends StripePaymentMethodTypeBase {
    $fields['stripe_payment_method_json'] = BundleFieldDefinition::create('string_long')
      ->setLabel(t('Stripe payment method JSON'))
      ->setDescription(t('The full Stripe PaymentMethod object as JSON.'))
      ->setRequired(FALSE);
      ->setRequired(FALSE)
      ->setInternal(TRUE);

    return $fields;
  }
Loading