From 0bdfd38f035769651620e00c1557c29583e004a4 Mon Sep 17 00:00:00 2001 From: milosws Date: Tue, 3 Mar 2020 11:04:52 +0100 Subject: [PATCH] Changed wrong class of Issue #2838380. --- .../Controller/PaymentCheckoutController.php | 33 +++++++++ modules/payment/src/PaymentMethodStorage.php | 4 +- modules/payment/src/PaymentOptionsBuilder.php | 13 +++- .../CheckoutPane/PaymentInformation.php | 10 +++ .../Commerce/CheckoutPane/PaymentProcess.php | 6 +- .../OnsitePaymentGatewayInterface.php | 20 +----- ...itePaymentMethodsWithPurchaseInterface.php | 29 ++++++++ .../SupportsCreatingPaymentInterface.php | 33 +++++++++ ...upportsDeletingPaymentMethodsInterface.php | 25 +++++++ .../SupportsStoredPaymentMethodsInterface.php | 15 +--- .../Controller/DummyRedirectController.php | 25 ++++++- .../StoredOffsiteRedirectWithPurchase.php | 72 +++++++++++++++++++ 12 files changed, 242 insertions(+), 43 deletions(-) create mode 100644 modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsCreatingOffsitePaymentMethodsWithPurchaseInterface.php create mode 100644 modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsCreatingPaymentInterface.php create mode 100644 modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsDeletingPaymentMethodsInterface.php create mode 100644 modules/payment_example/src/Plugin/Commerce/PaymentGateway/StoredOffsiteRedirectWithPurchase.php diff --git a/modules/payment/src/Controller/PaymentCheckoutController.php b/modules/payment/src/Controller/PaymentCheckoutController.php index 84159e11..7026837f 100644 --- a/modules/payment/src/Controller/PaymentCheckoutController.php +++ b/modules/payment/src/Controller/PaymentCheckoutController.php @@ -7,6 +7,7 @@ use Drupal\commerce_checkout\CheckoutOrderManagerInterface; use Drupal\commerce_order\Entity\OrderInterface; use Drupal\commerce_payment\Exception\PaymentGatewayException; use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\OffsitePaymentGatewayInterface; +use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsCreatingOffsitePaymentMethodsWithPurchaseInterface; use Drupal\Core\Access\AccessException; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Messenger\MessengerInterface; @@ -94,6 +95,38 @@ class PaymentCheckoutController implements ContainerInjectionInterface { $checkout_flow = $order->get('checkout_flow')->entity; $checkout_flow_plugin = $checkout_flow->getPlugin(); + if ($payment_gateway_plugin instanceof SupportsCreatingOffsitePaymentMethodsWithPurchaseInterface) { + try { + $payment_method_storage = \Drupal::entityTypeManager()->getStorage('commerce_payment_method'); + /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */ + $payment_method = $payment_method_storage->create([ + // Payment method type may depend on the request payload. We create + // payment method stub based on default payment method type available + // for payment gateway plugin but module developers can swap it + // with custom payment method. Keep in mind that $payment_method is + // passed by reference and code below relies on that reference to add + // payment method to the order. + 'type' => $payment_gateway_plugin->getDefaultPaymentMethodType()->getPluginId(), + 'payment_gateway' => $payment_gateway, + 'uid' => $order->getCustomerId(), + 'billing_profile' => $order->getBillingProfile(), + 'payment_gateway_mode' => $payment_gateway_plugin->getMode(), + ]); + // In case payment gateway supports payment method. + $payment_method = $payment_gateway_plugin->createPaymentMethod($payment_method, $order, $request); + + // Add payment method to the order. + $order->set('payment_method', $payment_method); + $order->save(); + } + catch (\Exception $e) { + // If creating payment method failed we allow onReturn to handle + // creating Payment, which is required to fulfill the payment. + // This exception is muted and logged. + \Drupal::logger('commerce_payment')->error($e->getMessage()); + } + } + try { $payment_gateway_plugin->onReturn($order, $request); $redirect_step_id = $checkout_flow_plugin->getNextStepId($step_id); diff --git a/modules/payment/src/PaymentMethodStorage.php b/modules/payment/src/PaymentMethodStorage.php index 6b39df04..3521b7a7 100644 --- a/modules/payment/src/PaymentMethodStorage.php +++ b/modules/payment/src/PaymentMethodStorage.php @@ -4,7 +4,7 @@ namespace Drupal\commerce_payment; use Drupal\commerce\CommerceContentEntityStorage; use Drupal\commerce_payment\Entity\PaymentGatewayInterface; -use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsStoredPaymentMethodsInterface; +use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsCreatingPaymentInterface; use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface; @@ -86,7 +86,7 @@ class PaymentMethodStorage extends CommerceContentEntityStorage implements Payme if ($account->isAnonymous()) { return []; } - if (!($payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface)) { + if (!($payment_gateway->getPlugin() instanceof SupportsCreatingPaymentInterface)) { return []; } diff --git a/modules/payment/src/PaymentOptionsBuilder.php b/modules/payment/src/PaymentOptionsBuilder.php index d58864a2..886734f3 100644 --- a/modules/payment/src/PaymentOptionsBuilder.php +++ b/modules/payment/src/PaymentOptionsBuilder.php @@ -4,6 +4,7 @@ namespace Drupal\commerce_payment; use Drupal\commerce\EntityHelper; use Drupal\commerce_order\Entity\OrderInterface; +use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsCreatingPaymentInterface; use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsStoredPaymentMethodsInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -44,6 +45,12 @@ class PaymentOptionsBuilder implements PaymentOptionsBuilderInterface { } /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface[] $payment_gateways_with_payment_methods */ $payment_gateways_with_payment_methods = array_filter($payment_gateways, function ($payment_gateway) { + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ + return $payment_gateway->getPlugin() instanceof SupportsCreatingPaymentInterface; + }); + + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface[] $payment_gateways_with_payment_methods */ + $payment_gateways_supports_creating_payment_methods = array_filter($payment_gateways, function ($payment_gateway) { /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ return $payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface; }); @@ -92,7 +99,7 @@ class PaymentOptionsBuilder implements PaymentOptionsBuilderInterface { // 3) Add options to create new stored payment methods of supported types. $payment_method_type_counts = []; // Count how many new payment method options will be built per gateway. - foreach ($payment_gateways_with_payment_methods as $payment_gateway) { + foreach ($payment_gateways_supports_creating_payment_methods as $payment_gateway) { $payment_method_types = $payment_gateway->getPlugin()->getPaymentMethodTypes(); foreach ($payment_method_types as $payment_method_type_id => $payment_method_type) { @@ -105,7 +112,7 @@ class PaymentOptionsBuilder implements PaymentOptionsBuilderInterface { } } - foreach ($payment_gateways_with_payment_methods as $payment_gateway) { + foreach ($payment_gateways_supports_creating_payment_methods as $payment_gateway) { $payment_gateway_plugin = $payment_gateway->getPlugin(); $payment_method_types = $payment_gateway_plugin->getPaymentMethodTypes(); @@ -132,7 +139,7 @@ class PaymentOptionsBuilder implements PaymentOptionsBuilderInterface { // 4) Add options for the remaining gateways (off-site, manual, etc). /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface[] $other_payment_gateways */ - $other_payment_gateways = array_diff_key($payment_gateways, $payment_gateways_with_payment_methods); + $other_payment_gateways = array_diff_key($payment_gateways, $payment_gateways_supports_creating_payment_methods); foreach ($other_payment_gateways as $payment_gateway) { $payment_gateway_id = $payment_gateway->id(); $options[$payment_gateway_id] = new PaymentOption([ diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index e713d6b5..6263f7cc 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -7,6 +7,7 @@ use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface; use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneBase; use Drupal\commerce_payment\PaymentOption; use Drupal\commerce_payment\PaymentOptionsBuilderInterface; +use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsCreatingPaymentInterface; use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsStoredPaymentMethodsInterface; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityTypeManagerInterface; @@ -400,6 +401,15 @@ class PaymentInformation extends CheckoutPaneBase { $this->order->setBillingProfile($billing_profile); } } + elseif ($payment_gateway->getPlugin() instanceof SupportsCreatingPaymentInterface && $selected_option->getPaymentMethodId()) { + /** @var \Drupal\commerce_payment\PaymentMethodStorageInterface $payment_method_storage */ + $payment_method_storage = $this->entityTypeManager->getStorage('commerce_payment_method'); + $payment_method = $payment_method_storage->load($selected_option->getPaymentMethodId()); + /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */ + $this->order->set('payment_gateway', $payment_method->getPaymentGateway()); + $this->order->set('payment_method', $payment_method); + $this->order->setBillingProfile($payment_method->getBillingProfile()); + } else { $this->order->set('payment_gateway', $payment_gateway); $this->order->set('payment_method', NULL); diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php index 4bff60ad..c5f0527b 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php @@ -10,7 +10,7 @@ use Drupal\commerce_payment\Exception\DeclineException; use Drupal\commerce_payment\Exception\PaymentGatewayException; use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\ManualPaymentGatewayInterface; use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\OffsitePaymentGatewayInterface; -use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\OnsitePaymentGatewayInterface; +use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsCreatingPaymentInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Link; @@ -173,9 +173,9 @@ class PaymentProcess extends CheckoutPaneBase { $payment = $this->createPayment($payment_gateway); $next_step_id = $this->checkoutFlow->getNextStepId($this->getStepId()); - if ($payment_gateway_plugin instanceof OnsitePaymentGatewayInterface) { + if ($payment_gateway_plugin instanceof SupportsCreatingPaymentInterface && $payment_method = $this->order->payment_method->entity) { try { - $payment->payment_method = $this->order->payment_method->entity; + $payment->payment_method = $payment_method; $payment_gateway_plugin->createPayment($payment, $this->configuration['capture']); $this->checkoutFlow->redirectToStep($next_step_id); } diff --git a/modules/payment/src/Plugin/Commerce/PaymentGateway/OnsitePaymentGatewayInterface.php b/modules/payment/src/Plugin/Commerce/PaymentGateway/OnsitePaymentGatewayInterface.php index 6108efe9..6ca5e919 100644 --- a/modules/payment/src/Plugin/Commerce/PaymentGateway/OnsitePaymentGatewayInterface.php +++ b/modules/payment/src/Plugin/Commerce/PaymentGateway/OnsitePaymentGatewayInterface.php @@ -2,8 +2,6 @@ namespace Drupal\commerce_payment\Plugin\Commerce\PaymentGateway; -use Drupal\commerce_payment\Entity\PaymentInterface; - /** * Defines the base interface for on-site payment gateways. * @@ -27,22 +25,6 @@ use Drupal\commerce_payment\Entity\PaymentInterface; * checkout step that contains the PaymentInformation checkout pane, to provide * a different payment method. */ -interface OnsitePaymentGatewayInterface extends PaymentGatewayInterface, SupportsStoredPaymentMethodsInterface { - - /** - * Creates a payment. - * - * @param \Drupal\commerce_payment\Entity\PaymentInterface $payment - * The payment. - * @param bool $capture - * Whether the created payment should be captured (VS authorized only). - * Allowed to be FALSE only if the plugin supports authorizations. - * - * @throws \InvalidArgumentException - * If $capture is FALSE but the plugin does not support authorizations. - * @throws \Drupal\commerce_payment\Exception\PaymentGatewayException - * Thrown when the transaction fails for any reason. - */ - public function createPayment(PaymentInterface $payment, $capture = TRUE); +interface OnsitePaymentGatewayInterface extends PaymentGatewayInterface, SupportsStoredPaymentMethodsInterface, SupportsCreatingPaymentInterface { } diff --git a/modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsCreatingOffsitePaymentMethodsWithPurchaseInterface.php b/modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsCreatingOffsitePaymentMethodsWithPurchaseInterface.php new file mode 100644 index 00000000..3d803243 --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsCreatingOffsitePaymentMethodsWithPurchaseInterface.php @@ -0,0 +1,29 @@ +currentRequest->request->get('total'); if ($total > 20) { - return new TrustedRedirectResponse($return); + return new TrustedRedirectResponse($return . '?' . $this->buildQuery()); } return new TrustedRedirectResponse($cancel); @@ -66,10 +66,31 @@ class DummyRedirectController implements ContainerInjectionInterface { $total = $this->currentRequest->query->get('total'); if ($total > 20) { - return new TrustedRedirectResponse($return); + return new TrustedRedirectResponse($return . '?' . $this->buildQuery()); } return new TrustedRedirectResponse($cancel); } + /** + * Builds query parameters passed back to the site. + * + * @param array $query + * Query parameters. + * + * @return string + * Query string. + */ + protected function buildQuery(array $query = []) { + $query = [ + 'cardnomask' => 'xxxxxxxxxxxx1111', + 'cardprefix' => '4111', + 'cardexpdate' => '1220', + 'txn_id' => '12345678', + 'payment_status' => 'OK', + 'customtext' => 'Srećno', + ] + $query; + return http_build_query($query); + } + } diff --git a/modules/payment_example/src/Plugin/Commerce/PaymentGateway/StoredOffsiteRedirectWithPurchase.php b/modules/payment_example/src/Plugin/Commerce/PaymentGateway/StoredOffsiteRedirectWithPurchase.php new file mode 100644 index 00000000..11488e13 --- /dev/null +++ b/modules/payment_example/src/Plugin/Commerce/PaymentGateway/StoredOffsiteRedirectWithPurchase.php @@ -0,0 +1,72 @@ +card_type = CreditCard::detectType($request->query->get('cardprefix'))->getId(); + $paymentMethod->card_number = substr($request->query->get('cardnomask'), -4); + $paymentMethod->card_exp_month = substr($request->query->get('cardexpdate'), 0, 2); + $paymentMethod->card_exp_year = substr($request->query->get('cardexpdate'), -2); + + $paymentMethod->setRemoteId($request->query->get('txn_id')); + $expires = CreditCard::calculateExpirationTimestamp($paymentMethod->card_exp_month->value, $paymentMethod->card_exp_year->value); + $paymentMethod->setExpiresTime($expires); + $paymentMethod->save(); + } + + /** + * {@inheritdoc} + */ + public function deletePaymentMethod(PaymentMethodInterface $payment_method) { + $payment_method->delete(); + } + + /** + * {@inheritdoc} + */ + public function createPayment(PaymentInterface $payment, $capture = TRUE) { + $this->assertPaymentState($payment, ['new']); + $payment_method = $payment->getPaymentMethod(); + $this->assertPaymentMethod($payment_method); + + // Perform the create payment request here, throw an exception if it fails. + // See \Drupal\commerce_payment\Exception for the available exceptions. + // Remember to take into account $capture when performing the request. + $payment_method_token = $payment_method->getRemoteId(); + $next_state = $capture ? 'completed' : 'authorization'; + $payment->setState($next_state); + $payment->setRemoteId($payment_method_token); + $payment->save(); + } + +} -- 2.17.1