uc_gc_client-8.x-1.x-dev/src/Plugin/Ubercart/PaymentMethod/GoCardlessClient.php
src/Plugin/Ubercart/PaymentMethod/GoCardlessClient.php
<?php
namespace Drupal\uc_gc_client\Plugin\Ubercart\PaymentMethod;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\uc_payment\PaymentMethodPluginBase;
use Drupal\uc_gc_client\Controller\GoCardlessPartner;
use Drupal\uc_order\OrderInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Defines the GoCardless Client payment method.
*
* @UbercartPaymentMethod(
* id = "gc_client",
* name = @Translation("GoCardless Client"),
* )
*/
class GoCardlessClient extends PaymentMethodPluginBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'uc_gc_client_settings';
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$default_config = \Drupal::config('uc_gc_client.settings');
$config = [];
foreach ([
'sandbox',
'payment_limit',
'create_payment',
'payments_tab',
'currencies',
'fixer',
'checkout_review',
'checkout_label',
'log_webhook',
'log_api',
'warnings_email',
'dom',
] as $element) {
$config[$element] = $default_config->get($element);
}
return $config;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$partner = new GoCardlessPartner();
$form['#cache'] = ['max-age' => 0];
$config = $this->configuration;
$this->settings = \Drupal::config('uc_gc_client.settings')->get();
$sandbox = $this->settings['sandbox'];
$ext = $sandbox ? '_sandbox' : '_live';
$connected = FALSE;
if (!is_null($this->settings['partner_user' . $ext]) && !is_null($this->settings['partner_pass' . $ext])) {
$response = $partner->get();
$host = \Drupal::request()->getHost();
if (isset($response->client_domain)) {
if ($host == $response->client_domain) {
$connected = TRUE;
}
}
}
$form['connect'] = [
'#type' => 'details',
'#title' => $this->t('Connect with GoCardless'),
'#open' => TRUE,
'#tree' => FALSE,
];
$form['connect']['sandbox'] = [
'#type' => 'checkbox',
'#title' => $this->t('<b>Enable Sandbox</b>'),
'#description' => $this->t('Sandbox: GoCardless will operate in a test environment, and no real banking activity will occur.'),
'#default_value' => $sandbox,
'#ajax' => [
'callback' => [$this, 'sandboxCallback'],
],
];
// GoCardless verification status display, and onboarding link.
if ($connected) {
$result = $partner->api([
'endpoint' => 'creditors',
'action' => 'list',
]);
if ($result && $result->response->status_code == 200) {
$creditors = $result->response->body->creditors;
if (!empty($creditors)) {
$creditor = array_shift($creditors);
if ($creditor->verification_status == 'successful') {
$verified_markup = '<div class="messages messages--status">' . t('Your GoCardless account is verified.') . '</div>';
}
else {
$verification_status = $creditor->verification_status == 'in_review'? 'In review' : 'Action required';
$client_url = \Drupal\Core\Url::fromRoute('<current>', array(), array("absolute" => TRUE))->toString();
$partner_url = $this->settings['partner_url'];
$env = $sandbox ? 'sandbox' : 'live';
$onboarding_url = $partner_url . '/gc_partner/onboarding_direct?env=' . $env . '&client_url=' . $client_url;
$verified_markup = '<div class="messages messages--warning">' . t("Your GoCardless account verification status is '@verification_status'. <a href='@onboarding_url'>Click here</a> to complete GoCardless onboarding and get verified.", array(
'@onboarding_url' => $onboarding_url,
'@verification_status' => $verification_status,
)) . '</div>';
}
$form['connect']['verified'] = [
'#markup' => $verified_markup,
];
}
}
}
$markup = '<p><b>Connect / disconnect with GoCardless</b></p>';
if (!$connected) {
$markup .= "<p>After clicking 'Connect' you will be redirected to the GoCardless where you can create an account and connect your site as a client of Seamless-CMS.co.uk</p>";
$form['connect']['markup'] = [
'#markup' => $markup,
];
if (!\Drupal::state()->get('uc_gc_client_payment_method_id')) {
$connect_disabled = TRUE;
$connect_suffix = '<br /><i>' . $this->t('Save the form before you can connect.') . '</i>';
}
elseif ($sandbox == 0 && !isset($_SERVER['HTTPS'])) {
$connect_disabled = TRUE;
$connect_suffix = '<br /><i>' . $this->t('Site needs to be secure (https) before you can connect to GoCardless LIVE.') . '</i>';
}
$form['connect']['submit'] = [
'#type' => 'submit',
'#disabled' => !$sandbox && !isset($_SERVER['HTTPS']) ? TRUE : FALSE,
'#value' => $sandbox ? $this->t('Connect SANDBOX') : $this->t('Connect LIVE'),
'#submit' => [[$this, 'submitConnect']],
'#disabled' => isset($connect_disabled) ? $connect_disabled : FALSE,
'#suffix' => isset($connect_suffix) ? $connect_suffix : NULL,
];
}
else {
$form['connect']['markup'] = [
'#markup' => $markup,
];
$form['connect']['ext'] = [
'#type' => 'value',
'#value' => $ext,
];
$disconnect_message = t('Are you sure you want to disconnect your site from GoCardless?');
$disconnect_onclick = 'if (!confirm("' . $disconnect_message . '")) {return false;}';
$form['connect']['disconnect'] = [
'#type' => 'submit',
'#value' => $sandbox ? $this->t('Disconnect SANDBOX') : $this->t('Disconnect LIVE'),
'#submit' => [[$this, 'submitDisconnect']],
'#attributes' => [
'onclick' => $disconnect_onclick,
],
];
}
if ($connected) {
if (!$webhook_secret = $this->settings['partner_webhook' . $ext]) {
$webhook_secret = $partner->api([
'endpoint' => 'webhook_secret',
])->response;
\Drupal::service('config.factory')->getEditable('uc_gc_client.settings')->set('partner_webhook' . $ext, $webhook_secret)->save();
}
global $base_url;
$webhook_url = $base_url . '/gc_client/webhook';
$gc_webhook_url = $this->t('https://manage@env.gocardless.com/developers/webhook-endpoints', [
'@env' => $sandbox ? '-sandbox' : '',
]);
$webhook_secret_markup = "<p id='webhook_secret'><b>" . $this->t('Webhook secret: ') . "</b>" . $webhook_secret . "</p>";
$webhook_secret_markup .= '<p>' . $this->t('To receive webhooks create / update a Webhook Endpoint at your GoCardless account <a target="new" href="@gc_webhook_url">here</a>, and set the Webhook URL as <i>@webhook_url</i>, and the Webhook Secret as the random 30 byte string that has been generated for you above.', [
'@webhook_url' => $webhook_url,
'@gc_webhook_url' => $gc_webhook_url,
]) . '</p>';
$form['connect']['webhook_secret_markup'] = [
'#title' => 'Webhook secret',
'#type' => 'markup',
'#markup' => $webhook_secret_markup,
];
$form['connect']['webhook_submit'] = [
'#type' => 'submit',
'#value' => $this->t('Change secret'),
'#suffix' => $this->t('If you change this, and have already set up your webhook endpoint in your GoCardless account, you will need to update it there as well.'),
'#ajax' => [
'callback' => [$this, 'webhookSecretCallback'],
'wrapper' => 'webhook_secret'
],
];
}
// Global.
$form['global'] = [
'#type' => 'details',
'#title' => $this->t('Global settings'),
'#open' => TRUE,
'#tree' => FALSE,
];
$form['global']['payments_tab'] = [
'#type' => 'checkbox',
'#title' => $this->t('Hide Payments tab'),
'#default_value' => $config['payments_tab'],
'#description' => $this->t("Ubercart Payments do not work with GoCardless, so checking this will hide the Payments tab when viewing any orders created through GoCardless. (Cache needs to be cleared to make this work.)"),
];
$form['global']['currencies'] = [
'#type' => 'checkbox',
'#title' => $this->t('Create payments in foreign currencies'),
'#default_value' => $config['currencies'],
'#description' => $this->t("Use foreign currency, and adjust payment amount for international customers, according to current exchange rates at <a target='new' href='http://fixer.io'>fixer.io</a>. This only applies when the currency of the customer's country is different to the store's default currency. SEPA and /or AutoGiro regions need to be enabled on your GoCardless account for this to work."),
];
$form['global']['fixer'] = [
'#type' => 'textfield',
'#title' => $this->t('fixer.io'),
'#default_value' => $config['fixer'],
'#description' => $this->t("API key for fixer.io"),
'#size' => 40,
'#maxlength' => 40,
'#states' => [
'visible' => [
'input[name="currencies"]' => ['checked' => TRUE],
],
],
];
$form['global']['payment_limit'] = [
'#type' => 'number',
'#title' => $this->t('Maximum payments'),
'#default_value' => $config['payment_limit'],
'#size' => 3,
'#min' => 1,
'#description' => $this->t("The maximum number of One-off payments that can be raised automatically, per order, per day. (Does not apply to Subscription payments).<br />If the amount is exceeded, a warning email is sent to the specified address above. Leave unset for unlimitted."),
'#required' => FALSE,
];
$form['global']['warnings_email'] = [
'#type' => 'textfield',
'#title' => $this->t('Email'),
'#default_value' => $config['warnings_email'],
'#description' => $this->t('Email address to send warnings.'),
'#size' => 40,
'#maxlength' => 40,
];
// Checkout options.
$form['checkout'] = [
'#type' => 'details',
'#title' => $this->t('Checkout settings'),
'#open' => TRUE,
'#tree' => FALSE,
];
$form['checkout']['checkout_review'] = [
'#title' => $this->t('<b>Disable Checkout Review page</b>'),
'#type' => 'checkbox',
'#default_value' => $config['checkout_review'],
'#required' => FALSE,
'#description' => $this->t("Check this to emit the Checkout Review page. <b>Do not</b> use this if you are using other payment methods in addition to GoCardless Client."),
];
$form['checkout']['checkout_label'] = [
'#type' => 'textfield',
'#title' => $this->t('Checkout button label'),
'#description' => $this->t('Customize the label of the final checkout button when the customer is about to pay.'),
'#default_value' => $config['checkout_label'],
];
// Logging options.
$form['log'] = [
'#title' => $this->t('Logging'),
'#type' => 'details',
'#open' => TRUE,
'#tree' => FALSE,
];
$form['log']['log_webhook'] = [
'#prefix' => '<p>',
'#suffix' => '</p>',
'#type' => 'checkbox',
'#title' => $this->t('<b>Enable webhook logging</b>'),
'#description' => $this->t('Webhooks recieved from GoCardless will be written to the log.'),
'#default_value' => $config['log_webhook'],
];
$form['log']['log_api'] = [
'#prefix' => '<p>',
'#suffix' => '</p>',
'#type' => 'checkbox',
'#title' => $this->t('<b>Enable API logging</b>'),
'#description' => $this->t('Responses from the Partner site to API posts will be written to the log.'),
'#default_value' => $config['log_api'],
];
return $form;
}
/**
* Ajax callback function.
*
* @see buildConfigurationForm()
*/
public function sandboxCallback(array &$form, FormStateInterface $form_state) {
$sandbox = $form_state->getValue('sandbox');
\Drupal::configFactory()->getEditable('uc_gc_client.settings')
->set('sandbox', $sandbox)->save();
$id = $form_state->getValue('id');
$path = '/admin/store/config/payment/';
$path .= empty($id) ? 'add/gc_client' : 'method/' . $id;
$response = new AjaxResponse();
$response->addCommand(new RedirectCommand($path));
return $response;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
foreach ($form_state->getValues() as $element => $value) {
if (!in_array($element, [
'disconnect',
'type',
'label',
'id',
'ext',
'submit',
'op',
'form_id',
'form_token',
'form_build_id',
])) {
$form_state->setValue(['settings', $element], $value);
}
}
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$id = $form_state->getValue('id');
\Drupal::state()->set('uc_gc_client_payment_method_id', 'uc_payment.method.' . $id);
}
/**
* Redirect to Partner site to activate the GoCardless OAuth Flow.
*/
public function submitConnect(array &$form, FormStateInterface $form_state) {
global $base_url;
$client_url = urlencode($base_url . '/gc_client/connect_complete');
$partner_domain = \drupal::config('uc_gc_client.settings')->get('partner_url');
$site_email = \drupal::config('system.site')->get('mail');
$form_state->getvalue('sandbox') ? $env = 'sandbox' : $env = 'live';
$url = $partner_domain . '/gc_partner/connect/' . $env . '?mail=' . $site_email . '&client_url=' . $client_url . '&module=Ubercart 8.x';
$response = new trustedredirectresponse($url);
$form_state->setresponse($response);
// Remove the 'desination=' parameter from query string if it exists.
$request = \Drupal::request();
$request->query->set('destination', NULL);
}
/**
* Disconnects client site from GC partner site.
*/
public function submitDisconnect(array &$form, FormStateInterface $form_state) {
$partner = new GoCardlessPartner();
$result = $partner->api([
'endpoint' => 'oauth',
'action' => 'revoke',
]);
if ($result->response == 200) {
drupal_set_message($this->t('You have disconnected successfully from GoCardless'));
}
else {
drupal_set_message($this->t('There was a problem disconnecting from GoCardless'), 'error');
}
if (isset($_SESSION['uc_gc_client_cookie_created'])) {
unset($_SESSION['uc_gc_client_cookie_created']);
}
$ext = $form_state->getValue('ext');
$config = \Drupal::service('config.factory')->getEditable('uc_gc_client.settings');
$config->set('org_id' . $ext, NULL)->save();
$config->set('partner_user' . $ext, NULL)->save();
$config->set('partner_pass' . $ext, NULL)->save();
}
/**
* {@inheritdoc}
*/
public function cartReview (OrderInterface $order) {
if ($this->getConfiguration()['checkout_review']) {
$this->orderSubmit($order);
}
}
/**
* {@inheritdoc}
*/
public function orderSubmit(OrderInterface $order) {
$mandate_details = uc_gc_client_mandate_details($order);
$partner = new GoCardlessPartner();
$result = $partner->api([
'endpoint' => 'redirect_flows',
'action' => 'create',
'mandate_details' => $mandate_details,
'order_id' => $order->id(),
]);
if ($result && UrlHelper::isValid($result->response)) {
$redirect = new TrustedRedirectResponse($result->response);
return $redirect->send();
}
else {
return $this->t('We were unable to create your new direct debit mandate, please contact the site administrator for assistance.');
}
}
/**
* Ajax callback function.
*
* On submitting 'Change secret' a new client secret is obtained, saved to
* the module config and returned for display on the settings form.
*
* @return array
* The new render array for display on the settings form.
*
* @see buildConfigurationForm().
*/
public function webhookSecretCallback(array &$form, FormStateInterface $form_state) {
drupal_set_message($this->t('You have changed your webhook secret. If you have already added a secret to your GoCardless account, you will have to update it there as well.'), 'warning');
$ext = $this->settings['sandbox'] ? '_sandbox' : '_live';
$partner = new GoCardlessPartner();
$webhook_secret = $partner->api([
'endpoint' => 'webhook_secret',
])->response;
\Drupal::service('config.factory')->getEditable('uc_gc_client.settings')->set('partner_webhook' . $ext, $webhook_secret)->save();
$output = "<p id='webhook_secret'><b>" . $this->t('Webhook secret: ') . "</b>" . $webhook_secret . "</p>";
return ['#markup' => $output];
}
}
