contacts_subscriptions-1.x-dev/src/Form/SubscriptionPaymentMethodAddForm.php
src/Form/SubscriptionPaymentMethodAddForm.php
<?php namespace Drupal\contacts_subscriptions\Form; use Drupal\commerce_payment\Entity\PaymentGatewayInterface; use Drupal\commerce_payment\Form\PaymentMethodAddForm; use Drupal\commerce_stripe\ErrorHelper; use Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\StripeInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Http\Exception\CacheableNotFoundHttpException; use Drupal\user\UserInterface; use Stripe\Customer; use Stripe\Exception\ApiErrorException; use Stripe\SetupIntent; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Add payment method form for subscriptions. */ class SubscriptionPaymentMethodAddForm extends PaymentMethodAddForm { /** * The invoice manager. * * @var \Drupal\contacts_subscriptions\InvoiceManager */ protected $invoiceManager; /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { $form = parent::create($container); $form->invoiceManager = $container->get('contacts_subscriptions.invoice_manager'); $form->entityTypeManager = $container->get('entity_type.manager'); return $form; } /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, UserInterface $user = NULL) { $cacheability = new CacheableMetadata(); $cacheability->addCacheContexts(['url.path']); if (!$user) { throw new CacheableNotFoundHttpException($cacheability, 'User required for payment method.'); } $cacheability->addCacheableDependency($user); $subscription = $this->entityTypeManager ->getStorage('contacts_subscription') ->loadByUser($user); $cacheability->addCacheableDependency($subscription); if (!$subscription->willRenew()) { throw new CacheableNotFoundHttpException($cacheability, 'User does not have a renewing membership.'); } if ($existing_method = $this->invoiceManager->getPaymentMethod($user)) { $form['existing'] = [ '#type' => 'item', '#title' => $this->t('Existing payment method:'), '#markup' => $existing_method->label(), ]; } // Ensure we are always collecting card details. $gateway = $this->entityTypeManager ->getStorage('commerce_payment_gateway') ->load('stripe'); $form_state->set('payment_gateway', $gateway); // Build the rest of the form. $form = parent::buildForm($form, $form_state, $user); return $form; } /** * {@inheritdoc} */ protected function buildPaymentMethodForm(array $form, FormStateInterface $form_state) { $form = parent::buildPaymentMethodForm($form, $form_state); $form['add_payment_method']['#inline_form']->getEntity()->setDefault(TRUE); $gateway = $form['add_payment_method']['#inline_form']->getEntity()->getPaymentGateway(); $gateway_plugin = $gateway->getPlugin(); if ($js_library = $gateway_plugin->getJsLibrary()) { $form['#attached']['library'][] = $js_library; } if ($gateway_plugin instanceof StripeInterface) { try { // We are not collecting payment now, so we want to use a SetupIntent. $intent = SetupIntent::create([ 'usage' => 'off_session', 'customer' => $this->getRemoteCustomerId($form_state->getBuildInfo()['args'][0], $gateway), ]); $form['payment_method']['payment_details']['#attached']['drupalSettings']['commerceStripe']['clientSecret'] = $intent->client_secret; $form['payment_method']['payment_details']['#attached']['drupalSettings']['commerceStripe']['futureUsage'] = TRUE; } catch (ApiErrorException $e) { ErrorHelper::handleException($e); } } return $form; } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); if ($form_state->get('step') === 'payment_method') { $user_id = $form_state->getBuildInfo()['args'][0]->id(); $form_state->setRedirect( 'contacts_subscriptions.manage', ['user' => $user_id], ); // Un-default all other payment methods. $new_method_id = $form['payment_method']['#inline_form']->getEntity()->id(); /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface[] $other_methods */ $other_methods = $this->entityTypeManager ->getStorage('commerce_payment_method') ->loadByProperties([ 'uid' => $user_id, 'reusable' => TRUE, ]); foreach ($other_methods as $payment_method) { if ($payment_method->id() != $new_method_id) { // Mark BACS as no longer reusable. if ($payment_method->getPaymentGatewayId() === 'bacs') { $payment_method->setReusable(FALSE); } $payment_method->setDefault(FALSE)->save(); } } } } /** * Gets the remote customer ID for the given user. * * The remote customer ID is specific to a payment gateway instance * in the configured mode. This allows the gateway to skip test customers * after the gateway has been switched to live mode. * * @param \Drupal\user\UserInterface $account * The user account. * @param \Drupal\commerce_payment\Entity\PaymentGatewayInterface $gateway * The gateway entity. * * @return string * The remote customer ID, or NULL if none found. */ protected function getRemoteCustomerId(UserInterface $account, PaymentGatewayInterface $gateway) { $gateway_plugin = $gateway->getPlugin(); $remote_id = $gateway_plugin->getRemoteCustomerId($account); if (!$account->isAnonymous() && !$remote_id && $gateway_plugin instanceof StripeInterface) { // If we haven't got a remote ID, create a customer. $email = $account->getEmail(); $customer = Customer::create([ 'email' => $email, 'description' => $account->label(), ]); $remote_id = $customer->id; $account->get('commerce_remote_id')->setByProvider($gateway->id() . '|' . $gateway_plugin->getMode(), $remote_id); $account->save(); } return $remote_id; } }