diff --git a/modules/payment/commerce_payment.info.yml b/modules/payment/commerce_payment.info.yml new file mode 100644 index 0000000..4bb9b72 --- /dev/null +++ b/modules/payment/commerce_payment.info.yml @@ -0,0 +1,7 @@ +name: Commerce Payment +type: module +description: 'Provides payment functionality.' +package: Commerce +core: 8.x +dependencies: + - commerce_order diff --git a/modules/payment/commerce_payment.links.action.yml b/modules/payment/commerce_payment.links.action.yml new file mode 100644 index 0000000..09c4a7a --- /dev/null +++ b/modules/payment/commerce_payment.links.action.yml @@ -0,0 +1,5 @@ +entity.commerce_payment_gateway.add_form: + route_name: entity.commerce_payment_gateway.add_form + title: 'Add payment gateway' + appears_on: + - entity.commerce_payment_gateway.collection diff --git a/modules/payment/commerce_payment.links.menu.yml b/modules/payment/commerce_payment.links.menu.yml new file mode 100644 index 0000000..d1fd162 --- /dev/null +++ b/modules/payment/commerce_payment.links.menu.yml @@ -0,0 +1,5 @@ +entity.commerce_payment_gateway.collection: + title: 'Payment gateways' + route_name: 'entity.commerce_payment_gateway.collection' + parent: 'commerce.configuration' + description: 'Manage your payment gateways.' diff --git a/modules/payment/commerce_payment.links.task.yml b/modules/payment/commerce_payment.links.task.yml new file mode 100644 index 0000000..4a851e8 --- /dev/null +++ b/modules/payment/commerce_payment.links.task.yml @@ -0,0 +1,4 @@ +entity.commerce_payment_gateway.edit_form: + route_name: entity.commerce_payment_gateway.edit_form + base_route: entity.commerce_payment_gateway.edit_form + title: Edit diff --git a/modules/payment/commerce_payment.module b/modules/payment/commerce_payment.module new file mode 100755 index 0000000..eab7922 --- /dev/null +++ b/modules/payment/commerce_payment.module @@ -0,0 +1,28 @@ +id() === 'commerce_order') { + $fields['payment_gateway'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Payment gateway')) + ->setDescription(t('The payment gateway.')) + ->setRequired(TRUE) + ->setSetting('target_type', 'commerce_payment_gateway'); + + $fields['payment_method'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Payment method')) + ->setDescription(t('The payment method.')) + ->setRequired(TRUE) + ->setSetting('target_type', 'commerce_payment_method'); + } +} diff --git a/modules/payment/commerce_payment.permissions.yml b/modules/payment/commerce_payment.permissions.yml new file mode 100644 index 0000000..5b0aaf7 --- /dev/null +++ b/modules/payment/commerce_payment.permissions.yml @@ -0,0 +1,7 @@ +administer payment gateways: + title: 'Administer payment gateways' + 'restrict access': TRUE + +administer payments: + title: 'Administer payments' + 'restrict access': TRUE diff --git a/modules/payment/commerce_payment.routing.yml b/modules/payment/commerce_payment.routing.yml new file mode 100644 index 0000000..874b79b --- /dev/null +++ b/modules/payment/commerce_payment.routing.yml @@ -0,0 +1,7 @@ +entity.commerce_payment_gateway.collection: + path: '/admin/commerce/config/payment-gateways' + defaults: + _entity_list: 'commerce_payment_gateway' + _title: 'Payment gateways' + requirements: + _permission: 'administer payment gateways' diff --git a/modules/payment/commerce_payment.services.yml b/modules/payment/commerce_payment.services.yml new file mode 100644 index 0000000..4336693 --- /dev/null +++ b/modules/payment/commerce_payment.services.yml @@ -0,0 +1,12 @@ +services: + plugin.manager.commerce_payment_gateway: + class: Drupal\commerce_payment\PaymentGatewayManager + parent: default_plugin_manager + + plugin.manager.commerce_payment_method_type: + class: Drupal\commerce_payment\PaymentMethodTypeManager + parent: default_plugin_manager + + plugin.manager.commerce_payment_type: + class: Drupal\commerce_payment\PaymentTypeManager + parent: default_plugin_manager diff --git a/modules/payment/commerce_payment.workflow_groups.yml b/modules/payment/commerce_payment.workflow_groups.yml new file mode 100644 index 0000000..6db4c4c --- /dev/null +++ b/modules/payment/commerce_payment.workflow_groups.yml @@ -0,0 +1,3 @@ +payment: + label: Payment + entity_type: commerce_payment diff --git a/modules/payment/commerce_payment.workflows.yml b/modules/payment/commerce_payment.workflows.yml new file mode 100644 index 0000000..1b99254 --- /dev/null +++ b/modules/payment/commerce_payment.workflows.yml @@ -0,0 +1,49 @@ +payment_default: + id: payment_default + group: payment + label: 'Default' + states: + new: + label: 'New' + authorization: + label: 'Authorization' + authorization_void: + label: 'Authorization (Voided)' + authorization_expired: + label: 'Authorization (Expired)' + capture_completed: + label: 'Capture (Completed)' + capture_partally_refunded: + label: 'Capture (Partially refunded)' + capture_refunded: + label: 'Capture (Refunded)' + transitions: + # Transitions are required, but aren't actually used in this case. + authorize: + label: 'Authorize payment' + from: [new] + to: authorization + void: + label: 'Void payment' + from: [authorization] + to: authorization_void + expire: + label: 'Expire payment' + from: [authorization] + to: authorization_expired + authorize_capture: + label: 'Authorize and capture payment' + from: [new] + to: capture_completed + capture: + label: 'Capture payment' + from: [authorization] + to: capture_completed + partially_refund: + label: 'Partially refund payment' + from: [capture_completed] + to: capture_partally_refunded + refund: + label: 'Refund payment' + from: [capture_completed, capture_partally_refunded] + to: capture_refunded diff --git a/modules/payment/config/schema/commerce_payment.schema.yml b/modules/payment/config/schema/commerce_payment.schema.yml new file mode 100644 index 0000000..36a1809 --- /dev/null +++ b/modules/payment/config/schema/commerce_payment.schema.yml @@ -0,0 +1,33 @@ +commerce_payment.commerce_payment_gateway.*: + type: config_entity + label: 'Payment gateway' + mapping: + id: + type: string + label: 'Machine-readable name' + label: + type: label + label: 'Label' + weight: + type: integer + label: 'Weight' + plugin: + type: string + label: 'Plugin' + configuration: + type: commerce_payment.commerce_payment_gateway.plugin.[%parent.plugin] + +commerce_payment.commerce_payment_gateway.plugin.*: + type: commerce_payment_gateway_configuration + +commerce_payment_gateway_configuration: + type: mapping + mapping: + mode: + type: string + label: 'Mode' + payment_method_types: + type: sequence + label: 'Payment method types' + sequence: + type: string diff --git a/modules/payment/src/Annotation/CommercePaymentGateway.php b/modules/payment/src/Annotation/CommercePaymentGateway.php new file mode 100644 index 0000000..a629b13 --- /dev/null +++ b/modules/payment/src/Annotation/CommercePaymentGateway.php @@ -0,0 +1,97 @@ + + * 'add-payment-method' => "Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\Form\PaymentMethodAddForm", + * 'capture-payment' => "Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\Form\PaymentCaptureForm", + * + * + * @var array + */ + public $forms = []; + + /** + * The payment type used by the payment gateway. + * + * @var string + */ + public $payment_type = 'payment_default'; + + /** + * The payment method types handled by the payment gateway. + * + * @var string[] + */ + public $payment_method_types = []; + + /** + * Constructs a new CommercePaymentGateway object. + * + * @param array $values + * The annotation values. + */ + public function __construct($values) { + // Define default modes. + if (empty($values['modes'])) { + $values['modes'] = [ + 'test' => t('Test'), + 'live' => t('Live'), + ]; + } + parent::__construct($values); + } + +} diff --git a/modules/payment/src/Annotation/CommercePaymentMethodType.php b/modules/payment/src/Annotation/CommercePaymentMethodType.php new file mode 100644 index 0000000..1a8e91c --- /dev/null +++ b/modules/payment/src/Annotation/CommercePaymentMethodType.php @@ -0,0 +1,43 @@ + [ + 'id' => 'visa', + 'label' => t('Visa'), + 'number_prefixes' => ['4'], + ], + 'mastercard' => [ + 'id' => 'mastercard', + 'label' => t('MasterCard'), + 'number_prefixes' => ['51-55', '222100-272099'], + ], + 'amex' => [ + 'id' => 'amex', + 'label' => t('American Express'), + 'number_prefixes' => ['34', '37'], + 'number_lengths' => [15], + 'security_code_length' => 4, + ], + 'dinersclub' => [ + 'id' => 'dinersclub', + 'label' => t('Diners Club'), + 'number_prefixes' => ['300-305', '309', '36', '38', '39'], + 'number_lengths' => [14], + ], + 'discover' => [ + 'id' => 'discover', + 'label' => t('Discover Card'), + 'number_prefixes' => ['6011', '622126-622925', '644-649', '65'], + 'number_lengths' => [16, 19], + ], + 'jcb' => [ + 'id' => 'jcb', + 'label' => t('JCB'), + 'number_prefixes' => ['3528-3589'], + ], + 'unionpay' => [ + 'id' => 'unionpay', + 'label' => t('UnionPay'), + 'number_prefixes' => ['62', '88'], + 'number_lengths' => [16, 17, 18, 19], + 'uses_luhn' => FALSE, + ], + 'maestro' => [ + 'id' => 'maestro', + 'label' => t('Maestro'), + 'number_prefixes' => [ + '5018', '5020', '5038', '5612', '5893', '6304', + '6759', '6761', '6762', '6763', '0604', '6390', + ], + 'number_lengths' => [12, 13, 14, 15, 16, 17, 18, 19], + ], + ]; + foreach ($definitions as $id => $definition) { + self::$types[$id] = new CreditCardType($definition); + } + + return self::$types; + } + + /** + * Gets the labels of all available credit card types. + * + * @return array + * The labels, keyed by ID. + */ + public static function getTypeLabels() { + $types = self::getTypes(); + $type_labels = array_map(function ($type) { + return $type->getLabel(); + }, $types); + + return $type_labels; + } + + /** + * Detects the credit card type based on the number. + * + * @param string $number + * The credit card number. + * + * @return \Drupal\commerce_payment\CreditCardType|null + * The credit card type, or NULL if unknown. + */ + public static function detectType($number) { + if (!is_numeric($number)) { + return FALSE; + } + $types = self::getTypes(); + foreach ($types as $type) { + foreach ($type->getNumberPrefixes() as $prefix) { + if (self::matchPrefix($number, $prefix)) { + return $type; + } + } + } + + return FALSE; + } + + /** + * Checks whether the given credit card number matches the given prefix. + * + * @param string $number + * The credit card number. + * @param string $prefix + * The prefix to match against. Can be a single number such as '43' or a + * range such as '30-35'. + * + * @return bool + * TRUE if the credit card number matches the prefix, FALSE otherwise. + */ + public static function matchPrefix($number, $prefix) { + if (is_numeric($prefix)) { + return substr($number, 0, strlen($prefix)) == $prefix; + } + else { + list($start, $end) = explode('-', $prefix); + $number = substr($number, 0, strlen($start)); + return $number >= $start && $number <= $end; + } + } + + /** + * Validates the given credit card number. + * + * @param string $number + * The credit card number. + * @param \Drupal\commerce_payment\CreditCardType $type + * The credit card type. + * + * @return bool + * TRUE if the credit card number is valid, FALSE otherwise. + */ + public static function validateNumber($number, CreditCardType $type) { + if (!is_numeric($number)) { + return FALSE; + } + if (!in_array(strlen($number), $type->getNumberLengths())) { + return FALSE; + } + if ($type->usesLuhn() && !self::validateLuhn($number)) { + return FALSE; + } + + return TRUE; + } + + /** + * Validates the given credit card number using the Luhn algorithm. + * + * @param string $number + * The credit card number. + * + * @return bool + * TRUE if the credit card number is valid, FALSE otherwise. + */ + public static function validateLuhn($number) { + $total = 0; + foreach (array_reverse(str_split($number)) as $i => $digit) { + $digit = $i % 2 ? $digit * 2 : $digit; + $digit = $digit > 9 ? $digit - 9 : $digit; + $total += $digit; + } + return ($total % 10 === 0); + } + + /** + * Validates the given credit card security code. + * + * @param string $security_code + * The credit card security code. + * @param \Drupal\commerce_payment\CreditCardType $type + * The credit card type. + * + * @return bool + * TRUE if the credit card security code is valid, FALSE otherwise. + */ + public static function validateSecurityCode($security_code, CreditCardType $type) { + if (!is_numeric($security_code)) { + return FALSE; + } + if (strlen($security_code) != $type->getSecurityCodeLength()) { + return FALSE; + } + + return TRUE; + } + +} diff --git a/modules/payment/src/CreditCardType.php b/modules/payment/src/CreditCardType.php new file mode 100644 index 0000000..7b6b563 --- /dev/null +++ b/modules/payment/src/CreditCardType.php @@ -0,0 +1,139 @@ +id = $definition['id']; + $this->label = $definition['label']; + $this->numberPrefixes = $definition['number_prefixes']; + if (isset($definition['number_lengths'])) { + $this->numberLengths = $definition['number_lengths']; + } + if (isset($definition['security_code_length'])) { + $this->securityCodeLength = $definition['security_code_length']; + } + if (isset($definition['uses_luhn'])) { + $this->usesLuhn = $definition['uses_luhn']; + } + } + + /** + * Gets the credit card type ID. + * + * @return string + * The credit card type ID. + */ + public function getId() { + return $this->id; + } + + /** + * Gets the credit card type label. + * + * @return string + * The credit card type label. + */ + public function getLabel() { + return $this->label; + } + + /** + * Gets the credit card type number prefixes. + * + * @return array + * The credit card type number prefixes. + */ + public function getNumberPrefixes() { + return $this->numberPrefixes; + } + + /** + * Gets the credit card type number lengths. + * + * @return array + * The credit card type number lengths. + */ + public function getNumberLengths() { + return $this->numberLengths; + } + + /** + * Gets the credit card type security code length. + * + * @return string + * The credit card type security code length. + */ + public function getSecurityCodeLength() { + return $this->securityCodeLength; + } + + /** + * Gets whether the credit card type uses Luhn validation. + * + * @return bool + * TRUE if the credit card type uses Luhn validation, FALSE otherwise. + */ + public function usesLuhn() { + return $this->usesLuhn; + } + +} diff --git a/modules/payment/src/Entity/Payment.php b/modules/payment/src/Entity/Payment.php new file mode 100644 index 0000000..26e229e --- /dev/null +++ b/modules/payment/src/Entity/Payment.php @@ -0,0 +1,266 @@ +createInstance($this->bundle()); + } + + /** + * {@inheritdoc} + */ + public function getPaymentGateway() { + return $this->get('payment_gateway')->entity; + } + + /** + * {@inheritdoc} + */ + public function getPaymentGatewayId() { + return $this->get('payment_gateway')->target_id; + } + + /** + * {@inheritdoc} + */ + public function getPaymentMethod() { + return $this->get('payment_method')->entity; + } + + /** + * {@inheritdoc} + */ + public function getPaymentMethodId() { + return $this->get('payment_method')->target_id; + } + + /** + * {@inheritdoc} + */ + public function getOrder() { + return $this->get('order_id')->entity; + } + + /** + * {@inheritdoc} + */ + public function getOrderId() { + return $this->get('order_id')->target_id; + } + + /** + * {@inheritdoc} + */ + public function getRemoteId() { + return $this->get('remote_id')->value; + } + + /** + * {@inheritdoc} + */ + public function setRemoteId($remote_id) { + $this->set('remote_id', $remote_id); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getAmount() { + return $this->get('amount')->first()->toPrice(); + } + + /** + * {@inheritdoc} + */ + public function setAmount(Price $amount) { + $this->set('amount', $amount); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRefundedAmount() { + return $this->get('refunded_amount')->first()->toPrice(); + } + + /** + * {@inheritdoc} + */ + public function setRefundedAmount(Price $refunded_amount) { + $this->set('refunded_amount', $refunded_amount); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getState() { + return $this->get('state')->first(); + } + + /** + * {@inheritdoc} + */ + public function getAuthorizedTime() { + return $this->get('authorized')->value; + } + + /** + * {@inheritdoc} + */ + public function setAuthorizedTime($timestamp) { + $this->set('authorized', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getAuthorizationExpiresTime() { + return $this->get('authorization_expires')->value; + } + + /** + * {@inheritdoc} + */ + public function setAuthorizationExpiresTime($timestamp) { + $this->set('authorization_expires', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getCapturedTime() { + return $this->get('captured')->value; + } + + /** + * {@inheritdoc} + */ + public function setCapturedTime($timestamp) { + $this->set('captured', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = parent::baseFieldDefinitions($entity_type); + + $fields['payment_method'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Payment method')) + ->setDescription(t('The payment method.')) + ->setSetting('target_type', 'commerce_payment_method') + ->setReadOnly(TRUE); + + $fields['order_id'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Order')) + ->setDescription(t('The parent order.')) + ->setSetting('target_type', 'commerce_order') + ->setReadOnly(TRUE); + + $fields['remote_id'] = BaseFieldDefinition::create('string') + ->setLabel(t('Remote ID')) + ->setDescription(t('The remote payment ID.')) + ->setSetting('max_length', 255) + ->setDisplayConfigurable('view', TRUE); + + $fields['amount'] = BaseFieldDefinition::create('commerce_price') + ->setLabel(t('Amount')) + ->setDescription(t('The payment amount.')) + ->setRequired(TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['refunded_amount'] = BaseFieldDefinition::create('commerce_price') + ->setLabel(t('Refunded amount')) + ->setDescription(t('The refunded payment amount.')) + ->setDisplayConfigurable('view', TRUE); + + $fields['state'] = BaseFieldDefinition::create('state') + ->setLabel(t('State')) + ->setDescription(t('The payment state.')) + ->setRequired(TRUE) + ->setSetting('max_length', 255) + ->setDisplayOptions('view', [ + 'label' => 'hidden', + 'type' => 'list_default', + 'weight' => 0, + ]) + ->setDisplayConfigurable('view', TRUE) + ->setSetting('workflow_callback', ['\Drupal\commerce_payment\Entity\Payment', 'getWorkflowId']); + + $fields['authorized'] = BaseFieldDefinition::create('timestamp') + ->setLabel(t('Authorized')) + ->setDescription(t('The time when the payment was authorized.')) + ->setDisplayConfigurable('view', TRUE); + + $fields['authorization_expires'] = BaseFieldDefinition::create('timestamp') + ->setLabel(t('Authorization expires')) + ->setDescription(t('The time when the payment authorization expires.')) + ->setDisplayConfigurable('view', TRUE); + + $fields['captured'] = BaseFieldDefinition::create('timestamp') + ->setLabel(t('Captured')) + ->setDescription(t('The time when the payment was captured.')) + ->setDisplayConfigurable('view', TRUE); + + return $fields; + } + + /** + * Gets the workflow ID for the state field. + * + * @param \Drupal\commerce_payment\Entity\PaymentInterface $payment + * The payment. + * + * @return string + * The workflow ID. + */ + public static function getWorkflowId(PaymentInterface $payment) { + return $payment->getType()->getWorkflowId(); + } + +} diff --git a/modules/payment/src/Entity/PaymentGateway.php b/modules/payment/src/Entity/PaymentGateway.php new file mode 100644 index 0000000..7e9c87c --- /dev/null +++ b/modules/payment/src/Entity/PaymentGateway.php @@ -0,0 +1,161 @@ +weight; + } + + /** + * {@inheritdoc} + */ + public function setWeight($weight) { + $this->weight = $weight; + return $weight; + } + + /** + * {@inheritdoc} + */ + public function getPlugin() { + return $this->getPluginCollection()->get($this->plugin); + } + + /** + * {@inheritdoc} + */ + public function getPluginId() { + return $this->plugin; + } + + /** + * {@inheritdoc} + */ + public function setPluginId($plugin_id) { + $this->plugin = $plugin_id; + $this->pluginCollection = NULL; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getPluginCollections() { + return [ + 'configuration' => $this->getPluginCollection(), + ]; + } + + /** + * Gets the plugin collection that holds the payment gateway plugin. + * + * Ensures the plugin collection is initialized before returning it. + * + * @return \Drupal\commerce_payment\PaymentGatewayPluginCollection + * The plugin collection. + */ + protected function getPluginCollection() { + if (!$this->pluginCollection) { + $plugin_manager = \Drupal::service('plugin.manager.commerce_payment_gateway'); + $this->pluginCollection = new PaymentGatewayPluginCollection($plugin_manager, $this->plugin, $this->configuration, $this->id); + } + return $this->pluginCollection; + } + +} diff --git a/modules/payment/src/Entity/PaymentGatewayInterface.php b/modules/payment/src/Entity/PaymentGatewayInterface.php new file mode 100644 index 0000000..1ab4100 --- /dev/null +++ b/modules/payment/src/Entity/PaymentGatewayInterface.php @@ -0,0 +1,59 @@ +getType()->buildLabel($this); + } + + /** + * {@inheritdoc} + */ + public function getType() { + $payment_method_type_manager = \Drupal::service('plugin.manager.commerce_payment_method_type'); + return $payment_method_type_manager->createInstance($this->bundle()); + } + + /** + * {@inheritdoc} + */ + public function getPaymentGateway() { + return $this->get('payment_gateway')->entity; + } + + /** + * {@inheritdoc} + */ + public function getPaymentGatewayId() { + return $this->get('payment_gateway')->target_id; + } + + /** + * {@inheritdoc} + */ + public function getOwner() { + return $this->get('uid')->entity; + } + + /** + * {@inheritdoc} + */ + public function getOwnerId() { + return $this->get('uid')->target_id; + } + + /** + * {@inheritdoc} + */ + public function setOwnerId($uid) { + $this->set('uid', $uid); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setOwner(UserInterface $account) { + $this->set('uid', $account->id()); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRemoteId() { + return $this->get('remote_id')->value; + } + + /** + * {@inheritdoc} + */ + public function setRemoteId($remote_id) { + $this->set('remote_id', $remote_id); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getBillingProfile() { + return $this->get('billing_profile')->entity; + } + + /** + * {@inheritdoc} + */ + public function setBillingProfile(ProfileInterface $profile) { + $this->set('billing_profile', $profile->id()); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getBillingProfileId() { + return $this->get('billing_profile')->target_id; + } + + /** + * {@inheritdoc} + */ + public function setBillingProfileId($billingProfileId) { + $this->set('billing_profile', $billingProfileId); + return $this; + } + + /** + * {@inheritdoc} + */ + public function isReusable() { + return $this->get('reusable')->value; + } + + /** + * {@inheritdoc} + */ + public function setReusable($reusable) { + $this->set('reusable', $reusable); + return $this; + } + + /** + * {@inheritdoc} + */ + public function isDefault() { + return $this->get('is_default')->value; + } + + /** + * {@inheritdoc} + */ + public function setDefault($default) { + $this->set('is_default', $default); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getExpiresTime() { + return $this->get('expires')->value; + } + + /** + * {@inheritdoc} + */ + public function setExpiresTime($timestamp) { + $this->set('expires', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getCreatedTime() { + return $this->get('created')->value; + } + + /** + * {@inheritdoc} + */ + public function setCreatedTime($timestamp) { + $this->set('created', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = parent::baseFieldDefinitions($entity_type); + + $fields['payment_gateway'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Payment gateway')) + ->setDescription(t('The payment gateway.')) + ->setRequired(TRUE) + ->setSetting('target_type', 'commerce_payment_gateway'); + + $fields['uid'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Owner')) + ->setDescription(t('The payment method owner.')) + ->setSetting('target_type', 'user') + ->setSetting('handler', 'default') + ->setDefaultValueCallback('Drupal\commerce_payment\Entity\PaymentMethod::getCurrentUserId') + ->setDisplayOptions('view', [ + 'label' => 'above', + 'type' => 'author', + 'weight' => 0, + ]) + ->setDisplayConfigurable('view', TRUE); + + $fields['remote_id'] = BaseFieldDefinition::create('string') + ->setLabel(t('Remote ID')) + ->setDescription(t('The payment method remote ID.')) + ->setSetting('max_length', 255) + ->setDisplayConfigurable('view', TRUE); + + $fields['billing_profile'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Billing profile')) + ->setDescription(t('Billing profile')) + ->setSetting('target_type', 'profile') + ->setSetting('handler', 'default') + ->setSetting('handler_settings', ['target_bundles' => ['billing']]) + ->setDisplayOptions('form', [ + 'type' => 'options_select', + 'weight' => 0, + 'settings' => [], + ]) + ->setDisplayConfigurable('view', TRUE); + + $fields['reusable'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Reusable')) + ->setDescription(t('Whether the payment method is reusable.')) + ->setDefaultValue(TRUE) + ->setDisplayConfigurable('view', TRUE); + + // 'default' is a reserved SQL word, hence the 'is_' prefix. + $fields['is_default'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Default')) + ->setDescription(t("Whether this is the user's default payment method.")); + + $fields['expires'] = BaseFieldDefinition::create('timestamp') + ->setLabel(t('Expires')) + ->setDescription(t('The time when the payment authorization expires.')) + ->setDisplayConfigurable('view', TRUE); + + $fields['created'] = BaseFieldDefinition::create('created') + ->setLabel(t('Created')) + ->setDescription(t('The time when the payment was created.')); + + $fields['changed'] = BaseFieldDefinition::create('changed') + ->setLabel(t('Changed')) + ->setDescription(t('The time when the payment was last edited.')); + + return $fields; + } + + /** + * Default value callback for 'uid' base field definition. + * + * @see ::baseFieldDefinitions() + * + * @return array + * An array of default values. + */ + public static function getCurrentUserId() { + return [\Drupal::currentUser()->id()]; + } + +} diff --git a/modules/payment/src/Entity/PaymentMethodInterface.php b/modules/payment/src/Entity/PaymentMethodInterface.php new file mode 100644 index 0000000..3bf181b --- /dev/null +++ b/modules/payment/src/Entity/PaymentMethodInterface.php @@ -0,0 +1,165 @@ +pluginManager = $plugin_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.commerce_payment_gateway') + ); + } + + /** + * {@inheritdoc} + */ + public function form(array $form, FormStateInterface $form_state) { + $form = parent::form($form, $form_state); + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $gateway */ + $gateway = $this->entity; + $plugins = array_map(function ($definition) { + return $definition['label']; + }, $this->pluginManager->getDefinitions()); + + $form['#tree'] = TRUE; + $form['label'] = [ + '#type' => 'textfield', + '#title' => $this->t('Name'), + '#maxlength' => 255, + '#default_value' => $gateway->label(), + '#required' => TRUE, + ]; + $form['id'] = [ + '#type' => 'machine_name', + '#default_value' => $gateway->id(), + '#machine_name' => [ + 'exists' => '\Drupal\commerce_payment\Entity\PaymentGateway::load', + ], + ]; + $form['plugin'] = [ + '#type' => 'select', + '#title' => $this->t('Plugin'), + '#options' => $plugins, + '#default_value' => $gateway->getPluginId(), + '#required' => TRUE, + '#disabled' => !$gateway->isNew(), + ]; + if (!$gateway->isNew()) { + $form['configuration'] = [ + '#parents' => ['configuration'], + ]; + $form['configuration'] = $gateway->getPlugin()->buildConfigurationForm($form['configuration'], $form_state); + } + + // Payment gateway is technically not a bundle config entity, but the logic + // for preventing id change on edit applies the same. + return $this->protectBundleIdElement($form); + } + + /** + * {@inheritdoc} + */ + protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) { + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $entity */ + // The parent method tries to initialize the plugin collection before + // setting the plugin. + $entity->setPluginId($form_state->getValue('plugin')); + + parent::copyFormValuesToEntity($entity, $form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + parent::validateForm($form, $form_state); + + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $gateway */ + $gateway = $this->entity; + if (!$gateway->isNew()) { + $gateway->getPlugin()->validateConfigurationForm($form['configuration'], $form_state); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + parent::submitForm($form, $form_state); + + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $gateway */ + $gateway = $this->entity; + if (!$gateway->isNew()) { + $gateway->getPlugin()->submitConfigurationForm($form['configuration'], $form_state); + } + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $status = $this->entity->save(); + drupal_set_message($this->t('Saved the %label payment gateway.', ['%label' => $this->entity->label()])); + if ($status == SAVED_UPDATED) { + $form_state->setRedirect('entity.commerce_payment_gateway.collection'); + } + elseif ($status == SAVED_NEW) { + // Send the user to the Edit form to see the plugin configuration form. + $form_state->setRedirect('entity.commerce_payment_gateway.edit_form', [ + 'commerce_payment_gateway' => $this->entity->id(), + ]); + } + } + +} diff --git a/modules/payment/src/PaymentGatewayListBuilder.php b/modules/payment/src/PaymentGatewayListBuilder.php new file mode 100644 index 0000000..b28b87d --- /dev/null +++ b/modules/payment/src/PaymentGatewayListBuilder.php @@ -0,0 +1,66 @@ +t('Payment gateway'); + $header['mode'] = $this->t('Mode'); + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $entity */ + $gateway_plugin = $entity->getPlugin(); + $modes = $gateway_plugin->getSupportedModes(); + $mode = $modes ? $modes[$gateway_plugin->getMode()] : $this->t('N/A'); + $row['label'] = $entity->label(); + // $this->weightKey determines whether the table will be rendered as a form. + if (!empty($this->weightKey)) { + $row['mode']['#markup'] = $mode; + } + else { + $row['mode'] = $mode; + } + + return $row + parent::buildRow($entity); + } + + /** + * {@inheritdoc} + */ + public function render() { + $entities = $this->load(); + // If there are less than 2 gateways, disable dragging. + if (count($entities) <= 1) { + unset($this->weightKey); + } + return parent::render(); + } + +} diff --git a/modules/payment/src/PaymentGatewayManager.php b/modules/payment/src/PaymentGatewayManager.php new file mode 100644 index 0000000..494916c --- /dev/null +++ b/modules/payment/src/PaymentGatewayManager.php @@ -0,0 +1,49 @@ +alterInfo('commerce_payment_gateway_info'); + $this->setCacheBackend($cache_backend, 'commerce_payment_gateway_plugins'); + } + + /** + * {@inheritdoc} + */ + public function processDefinition(&$definition, $plugin_id) { + parent::processDefinition($definition, $plugin_id); + + foreach (['id', 'label', 'display_label'] as $required_property) { + if (empty($definition[$required_property])) { + throw new PluginException(sprintf('The payment gateway %s must define the %s property.', $plugin_id, $required_property)); + } + } + } + +} diff --git a/modules/payment/src/PaymentGatewayPluginCollection.php b/modules/payment/src/PaymentGatewayPluginCollection.php new file mode 100644 index 0000000..530a13c --- /dev/null +++ b/modules/payment/src/PaymentGatewayPluginCollection.php @@ -0,0 +1,60 @@ +entityId = $entity_id; + } + + /** + * {@inheritdoc} + * + * @return \Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\PaymentGatewayInterface + * The payment gateway plugin. + */ + public function &get($instance_id) { + return parent::get($instance_id); + } + + /** + * {@inheritdoc} + */ + protected function initializePlugin($instance_id) { + if (!$instance_id) { + throw new PluginException("The payment gateway '{$this->entityId}' did not specify a plugin."); + } + + parent::initializePlugin($instance_id); + } + +} diff --git a/modules/payment/src/PaymentMethodStorage.php b/modules/payment/src/PaymentMethodStorage.php new file mode 100644 index 0000000..2d47577 --- /dev/null +++ b/modules/payment/src/PaymentMethodStorage.php @@ -0,0 +1,48 @@ +getQuery() + ->condition('uid', $account->id()) + ->condition('payment_gateway', $payment_gateway->id()) + ->condition('reusable', TRUE) + ->sort('created', 'DESC'); + $result = $query->execute(); + if (empty($result)) { + return []; + } + + return $this->loadMultiple($result); + } + + /** + * {@inheritdoc} + */ + protected function doCreate(array $values) { + if (!isset($values['payment_gateway'])) { + throw new EntityStorageException('Missing "payment_gateway" property when creating a payment method.'); + } + + return parent::doCreate($values); + } + +} diff --git a/modules/payment/src/PaymentMethodStorageInterface.php b/modules/payment/src/PaymentMethodStorageInterface.php new file mode 100644 index 0000000..87832e6 --- /dev/null +++ b/modules/payment/src/PaymentMethodStorageInterface.php @@ -0,0 +1,26 @@ +alterInfo('commerce_payment_method_type_info'); + $this->setCacheBackend($cache_backend, 'commerce_payment_method_type_plugins'); + } + + /** + * {@inheritdoc} + */ + public function processDefinition(&$definition, $plugin_id) { + parent::processDefinition($definition, $plugin_id); + + foreach (['id', 'label', 'create_label'] as $required_property) { + if (empty($definition[$required_property])) { + throw new PluginException(sprintf('The payment method type %s must define the %s property.', $plugin_id, $required_property)); + } + } + } + +} diff --git a/modules/payment/src/PaymentStorage.php b/modules/payment/src/PaymentStorage.php new file mode 100644 index 0000000..864989b --- /dev/null +++ b/modules/payment/src/PaymentStorage.php @@ -0,0 +1,24 @@ +alterInfo('commerce_payment_type_info'); + $this->setCacheBackend($cache_backend, 'commerce_payment_type_plugins'); + } + + /** + * {@inheritdoc} + */ + public function processDefinition(&$definition, $plugin_id) { + parent::processDefinition($definition, $plugin_id); + + foreach (['id', 'label'] as $required_property) { + if (empty($definition[$required_property])) { + throw new PluginException(sprintf('The payment type %s must define the %s property.', $plugin_id, $required_property)); + } + } + } + +} diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php new file mode 100644 index 0000000..516c307 --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -0,0 +1,162 @@ +entityTypeManager = $entity_type_manager; + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, CheckoutFlowInterface $checkout_flow = NULL) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $checkout_flow, + $container->get('entity_type.manager'), + $container->get('renderer') + ); + } + + /** + * {@inheritdoc} + */ + public function buildPaneSummary() { + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */ + $payment_gateway = $this->order->payment_gateway; + if (!$payment_gateway) { + return ''; + } + + $payment_method = $this->order->payment_method; + if ($payment_gateway instanceof SupportsStoredPaymentMethods && $payment_method) { + $view_builder = $this->entityTypeManager->getViewBuilder('commerce_payment_method'); + $payment_method_view = $view_builder->view($payment_method, 'default'); + $summary = $this->renderer->render($payment_method_view); + } + else { + $billing_profile = $this->order->getBillingProfile(); + $profile_view_builder = $this->entityTypeManager->getViewBuilder('profile'); + $profile_view = $profile_view_builder->view($billing_profile, 'default'); + $summary = $payment_gateway->getPlugin()->getDisplayLabel(); + $summary .= $this->renderer->render($profile_view); + } + + return $summary; + } + + /** + * {@inheritdoc} + */ + public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) { + $payment_gateway_storage = $this->entityTypeManager->getStorage('commerce_payment_method'); + /** @var \Drupal\commerce_payment\PaymentMethodStorageInterface $payment_method_storage */ + $payment_method_storage = $this->entityTypeManager->getStorage('commerce_payment_method'); + /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface[] $payment_gateways */ + $payment_gateways = $payment_gateway_storage->loadMultiple(); + + $options = []; + $default_option = NULL; + $owner = $this->order->getOwner(); + foreach ($payment_gateways as $payment_gateway) { + if ($owner) { + $payment_methods = $payment_method_storage->loadReusable($owner, $payment_gateway); + foreach ($payment_methods as $payment_method) { + $options[$payment_method->id()] = $payment_method->label(); + } + } + $payment_method_types = $payment_gateway->getPlugin()->getPaymentMethodTypes(); + foreach ($payment_method_types as $payment_method_type) { + $options['new'] = $payment_method_type->getCreateLabel(); + } + } + + $pane_form['payment_method'] = [ + '#type' => 'radios', + '#title' => $this->t('Payment method'), + '#options' => $options, + '#default_value' => $default_option, + ]; + $payment_method = $payment_method_storage->create([ + 'type' => 'credit_card', + 'payment_gateway' => $payment_gateway->id(), + 'uid' => $this->order->getOwnerId(), + ]); + $plugin_form = $payment_gateway->getForm('add-payment-method'); + $pane_form['add'] = $plugin_form->buildForm($pane_form['add'], $form_state, $payment_method); + + return $pane_form; + } + + /** + * {@inheritdoc} + */ + public function validatePaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { + + } + + /** + * {@inheritdoc} + */ + public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { + + } + +} diff --git a/modules/payment/src/Plugin/Commerce/PaymentGateway/Form/PaymentMethodAddForm.php b/modules/payment/src/Plugin/Commerce/PaymentGateway/Form/PaymentMethodAddForm.php new file mode 100644 index 0000000..e802ed7 --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/PaymentGateway/Form/PaymentMethodAddForm.php @@ -0,0 +1,51 @@ + 'container', + ]; + $plugin_form['billing_information'] = [ + '#type' => 'inline_entity_form', + '#entity_type' => 'profile', + '#bundle' => 'billing', + //'#default_value' => $payment_method->getBillingProfile(), + '#save_entity' => FALSE, + ]; + // @todo Needs to be moved to a #process, or a widget setting. + // Remove the details wrapper from the address field. + //if (!empty($plugin_form['address']['widget'][0])) { + // $plugin_form['address']['widget'][0]['#type'] = 'container'; + //} + + return $plugin_form; + } + + /** + * {@inheritdoc} + */ + public function validatePluginForm(array &$plugin_form, FormStateInterface $form_state, array &$complete_form) { + $billing_profile = clone $plugin_form['#billing_profile']; + $form_display = EntityFormDisplay::collectRenderDisplay($billing_profile, 'default'); + $form_display->extractFormValues($billing_profile, $plugin_form, $form_state); + $form_display->validateFormValues($billing_profile, $plugin_form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitPluginForm(array &$plugin_form, FormStateInterface $form_state, array &$complete_form) { + $billing_profile = clone $plugin_form['#billing_profile']; + $form_display = EntityFormDisplay::collectRenderDisplay($billing_profile, 'default'); + $form_display->extractFormValues($billing_profile, $plugin_form, $form_state); + $billing_profile->save(); + $this->order->setBillingProfile($billing_profile); + } + +} diff --git a/modules/payment/src/Plugin/Commerce/PaymentGateway/Form/PaymentMethodEditForm.php b/modules/payment/src/Plugin/Commerce/PaymentGateway/Form/PaymentMethodEditForm.php new file mode 100644 index 0000000..e69de29 diff --git a/modules/payment/src/Plugin/Commerce/PaymentGateway/OnsitePaymentGatewayBase.php b/modules/payment/src/Plugin/Commerce/PaymentGateway/OnsitePaymentGatewayBase.php new file mode 100644 index 0000000..37a7445 --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/PaymentGateway/OnsitePaymentGatewayBase.php @@ -0,0 +1,10 @@ +entityTypeManager = $entity_type_manager; + // Instantiate the types right away to ensure that their IDs are valid. + $this->paymentType = $payment_type_manager->createInstance($this->pluginDefinition['payment_type']); + foreach ($this->pluginDefinition['payment_method_types'] as $plugin_id) { + $this->paymentMethodTypes[$plugin_id] = $payment_method_type_manager->createInstance($plugin_id); + } + $this->setConfiguration($configuration); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('plugin.manager.commerce_payment_type'), + $container->get('plugin.manager.commerce_payment_method_type') + ); + } + + /** + * {@inheritdoc} + */ + public function getLabel() { + return $this->pluginDefinition['label']; + } + + /** + * {@inheritdoc} + */ + public function getDisplayLabel() { + return $this->configuration['display_label']; + } + + /** + * {@inheritdoc} + */ + public function getPaymentType() { + return $this->paymentType; + } + + /** + * {@inheritdoc} + */ + public function getPaymentMethodTypes() { + // Filter out payment method types disabled by the merchant. + return array_intersect_key($this->paymentMethodTypes, array_flip($this->configuration['payment_method_types'])); + } + + /** + * {@inheritdoc} + */ + public function getMode() { + return $this->configuration['mode']; + } + + /** + * {@inheritdoc} + */ + public function getSupportedModes() { + return $this->pluginDefinition['modes']; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = NestedArray::mergeDeep($this->defaultConfiguration(), $configuration); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + $modes = array_keys($this->getSupportedModes()); + + return [ + 'mode' => $modes ? reset($modes) : '', + 'payment_method_types' => [], + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $modes = $this->getSupportedModes(); + $payment_method_types = array_map(function ($payment_method_type) { + return $payment_method_type->getLabel(); + }, $this->paymentMethodTypes); + + $form['mode'] = [ + '#type' => 'radios', + '#title' => $this->t('Mode'), + '#options' => $modes, + '#default_value' => $this->configuration['mode'], + '#required' => TRUE, + '#access' => !empty($modes), + ]; + $form['payment_method_types'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Payment method types'), + '#options' => $payment_method_types, + '#default_value' => $this->configuration['payment_method_types'], + '#required' => TRUE, + '#access' => count($payment_method_types) > 1, + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {} + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + if (!$form_state->getErrors()) { + $values = $form_state->getValue($form['#parents']); + $values['payment_method_types'] = array_filter($values['payment_method_types']); + + $this->configuration['mode'] = $values['mode']; + $this->configuration['payment_method_types'] = array_keys($values['payment_method_types']); + } + } + + /** + * {@inheritdoc} + */ + public function getForm($form_id) { + $forms = $this->pluginDefinition['forms']; + if (empty($forms[$form_id])) { + throw new \InvalidArgumentException(sprintf('Invalid form "%s" requested for plugin "%s"', $form_id, $this->getPluginId())); + } + + return new $forms[$form_id]($this); + } + +} diff --git a/modules/payment/src/Plugin/Commerce/PaymentGateway/PaymentGatewayInterface.php b/modules/payment/src/Plugin/Commerce/PaymentGateway/PaymentGatewayInterface.php new file mode 100644 index 0000000..bc42a0f --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/PaymentGateway/PaymentGatewayInterface.php @@ -0,0 +1,79 @@ +card_type->value); + $args = [ + '@card_type' => $card_type->getLabel(), + '@card_number' => $payment_method->card_number->value, + ]; + return $this->t('@card_type ending in @card_number', $args); + } + + /** + * {@inheritdoc} + */ + public function buildFieldDefinitions() { + $fields = parent::buildFieldDefinitions(); + + $fields['card_type'] = BundleFieldDefinition::create('list_string') + ->setLabel(t('Card type')) + ->setDescription(t('The credit card type.')) + ->setRequired(TRUE) + ->setSetting('allowed_values_function', ['\Drupal\commerce_payment\CreditCard', 'getTypeLabels']); + + $fields['card_number'] = BundleFieldDefinition::create('string') + ->setLabel(t('Card number')) + ->setDescription(t('The last 4 digits of the credit card number')) + ->setRequired(TRUE); + + $fields['card_exp_month'] = BundleFieldDefinition::create('integer') + ->setLabel(t('Card expiration month')) + ->setDescription(t('The credit card expiration month.')) + ->setRequired(TRUE) + ->setSetting('size', 'tiny'); + + $fields['card_exp_year'] = BundleFieldDefinition::create('integer') + ->setLabel(t('Card expiration year')) + ->setDescription(t('The credit card expiration year.')) + ->setRequired(TRUE) + ->setSetting('size', 'tiny'); + + return $fields; + } + +} diff --git a/modules/payment/src/Plugin/Commerce/PaymentMethodType/PayPal.php b/modules/payment/src/Plugin/Commerce/PaymentMethodType/PayPal.php new file mode 100644 index 0000000..72dc8c7 --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/PaymentMethodType/PayPal.php @@ -0,0 +1,43 @@ + $payment_method->paypal_mail->value, + ]; + return $this->t('PayPal account (@paypal_mail)', $args); + } + + /** + * {@inheritdoc} + */ + public function buildFieldDefinitions() { + $fields = parent::buildFieldDefinitions(); + + $fields['paypal_mail'] = BundleFieldDefinition::create('email') + ->setLabel(t('PayPal Email')) + ->setDescription(t('The email address associated with the PayPal account.')) + ->setRequired(TRUE); + + return $fields; + } + +} diff --git a/modules/payment/src/Plugin/Commerce/PaymentMethodType/PaymentMethodTypeBase.php b/modules/payment/src/Plugin/Commerce/PaymentMethodType/PaymentMethodTypeBase.php new file mode 100644 index 0000000..140864a --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/PaymentMethodType/PaymentMethodTypeBase.php @@ -0,0 +1,33 @@ +pluginDefinition['label']; + } + + /** + * {@inheritdoc} + */ + public function getCreateLabel() { + return $this->pluginDefinition['create_label']; + } + + /** + * {@inheritdoc} + */ + public function buildFieldDefinitions() { + return []; + } + +} diff --git a/modules/payment/src/Plugin/Commerce/PaymentMethodType/PaymentMethodTypeInterface.php b/modules/payment/src/Plugin/Commerce/PaymentMethodType/PaymentMethodTypeInterface.php new file mode 100644 index 0000000..3ffc5ad --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/PaymentMethodType/PaymentMethodTypeInterface.php @@ -0,0 +1,40 @@ +pluginDefinition['label']; + } + + /** + * {@inheritdoc} + */ + public function getWorkflowId() { + return $this->pluginDefinition['workflow']; + } + +} diff --git a/modules/payment/src/Plugin/Commerce/PaymentType/PaymentTypeInterface.php b/modules/payment/src/Plugin/Commerce/PaymentType/PaymentTypeInterface.php new file mode 100644 index 0000000..259f40e --- /dev/null +++ b/modules/payment/src/Plugin/Commerce/PaymentType/PaymentTypeInterface.php @@ -0,0 +1,28 @@ +