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;
}
}
