public $_paymentProcessor;
public $_recurPaymentProcessors;
+ /**
+ * array of processor options in the format id => array($id => $label)
+ * WARNING it appears that the format used to differ to this and there are places in the code that
+ * expect the old format. $this->_paymentProcessors provides the additional data which this
+ * array seems to have provided in the past
+ * @var array
+ */
public $_processors;
/**
- * the id of the contribution that we are proceessing
+ * available payment processors with full details including the key 'object' indexed by their id
+ * @var array
+ */
+ protected $_paymentProcessors = array();
+ /**
+ * the id of the contribution that we are processing
*
* @var int
* @public
}
/**
- * @return array (0 => array(int $ppId => string $label), 1 => array(...payproc details...))
- */
- /**
- * @return array of valid processors
+ * @return array of valid processors. The array resembles the DB table but also has 'object' as a key
* @throws Exception
*/
public function getValidProcessors() {
if ($this->_mode) {
$this->assign(CRM_Financial_BAO_PaymentProcessor::hasPaymentProcessorSupporting(array('supportsFutureRecurStartDate')), TRUE);
- $processors = $this->getValidProcessors();
- if (empty($processors)) {
+ $this->_paymentProcessors = $this->getValidProcessors();
+ if (!isset($this->_paymentProcessor['id'])) {
+ // if the payment processor isn't set yet (as indicated by the presence of an id,) we'll grab the first one which should be the default
+ $this->_paymentProcessor = reset($this->_paymentProcessors);
+ }
+ if (empty($this->_paymentProcessors)) {
throw new CRM_Core_Exception(ts('You will need to configure the %1 settings for your Payment Processor before you can submit a credit card transactions.', array(1 => $this->_mode)));
}
$this->_processors = array();
- foreach ($processors as $id => $processor) {
+ foreach ($this->_paymentProcessors as $id => $processor) {
$this->_processors[$id] = ts($processor['name']);
}
//get the valid recurring processors.
$this->assignBillingType();
$this->_fields = array();
- CRM_Core_Payment_Form::setPaymentFieldsByType(CRM_Utils_Array::value('payment_type', $this->_processors), $this);
+ CRM_Core_Payment_Form::setPaymentFieldsByProcessor($this, $this->_paymentProcessor);
}
catch (CRM_Core_Exception $e) {
CRM_Core_Error::fatal($e->getMessage());
* @access public
*/
public function buildQuickForm() {
+ //@todo document the purpose of cdType (if still in use)
if ($this->_cdType) {
CRM_Custom_Form_CustomData::buildQuickForm($this);
return;
$paneNames[ts('Premium Information')] = 'Premium';
}
- $ccPane = NULL;
if ($this->_mode) {
- if (CRM_Utils_Array::value('payment_type', $this->_processors) & CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT
- ) {
- $ccPane = array(ts('Direct Debit Information') => 'DirectDebit');
- }
- else {
- $ccPane = array(ts('Credit Card Information') => 'CreditCard');
+ if (CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE) == TRUE) {
+ $buildRecurBlock = TRUE;
+ foreach ($this->billingPane as $name => $label) {
+ //@todo reduce variation so we don't have to convert 'credit_card' to 'CreditCard'
+ $paneNames[$label] = CRM_Utils_String::convertStringToCamel($name);
+ }
}
}
- if (is_array($ccPane)) {
- $paneNames = array_merge($ccPane, $paneNames);
- }
- $buildRecurBlock = FALSE;
foreach ($paneNames as $name => $type) {
$urlParams = "snippet=4&formType={$type}";
if ($this->_mode) {
$allPanes[$name]['open'] = 'true';
}
- if ($type == 'CreditCard') {
- $buildRecurBlock = TRUE;
- $this->add('hidden', 'hidden_CreditCard', 1);
- CRM_Core_Payment_Form::buildCreditCard($this, TRUE);
- }
- elseif ($type == 'DirectDebit') {
- $buildRecurBlock = TRUE;
- $this->add('hidden', 'hidden_DirectDebit', 1);
- CRM_Core_Payment_Form::buildDirectDebit($this, TRUE);
+ if ($type == 'CreditCard' || $type == 'DirectDebit') {
+ //@todo would be good to align tpl name with form name...
+ $this->add('hidden', 'hidden_' . $type, 1);
}
else {
$additionalInfoFormFunction = 'build' . $type;
/**
* Getter for accessing member vars
*
+ * @param $name
+ *
+ * @return null
*/
function getVar($name) {
return isset($this->$name) ? $this->$name : NULL;
}
+ /**
+ * get name for the payment information type
+ *
+ * @return string
+ */
+ public function getPaymentTypeName() {
+ return $this->_paymentProcessor['payment_type'] == 1 ? 'credit_card' : 'debit_card';
+ }
+
+ /**
+ * get label for the payment information type
+ *
+ * @return string
+ */
+ public function getPaymentTypeLabel() {
+ return $this->_paymentProcessor['payment_type'] == 1 ? 'Credit Card' : 'Debit Card';
+ }
+
/**
* get array of fields that should be displayed on the payment form
* @todo make payment type an option value & use it in the function name - currently on debit & credit card work
* @throws CiviCRM_API3_Exception
*/
public function getPaymentFormFields() {
- if ($this->_paymentProcessor['payment_type'] == 4) {
+ if ($this->_paymentProcessor['billing_mode'] == 4) {
return array();
}
return $this->_paymentProcessor['payment_type'] == 1 ? $this->getCreditCardFormFields() : $this->getDirectDebitFormFields();
/**
* get array of fields that should be displayed on the payment form for credit cards
+ *
* @return array
*/
protected function getCreditCardFormFields() {
/**
* get array of fields that should be displayed on the payment form for direct debits
+ *
* @return array
*/
protected function getDirectDebitFormFields() {
);
}
+ /**
+ * return an array of all the details about the fields potentially required for payment fields
+ * Only those determined by getPaymentFormFields will actually be assigned to the form
+ *
+ * @return array field metadata
+ */
+ public function getPaymentFormFieldsMetadata() {
+ //@todo convert credit card type into an option value
+ $creditCardType = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::creditCard();
+ return array(
+ 'credit_card_number' => array(
+ 'htmlType' => 'text',
+ 'name' => 'credit_card_number',
+ 'title' => ts('Card Number'),
+ 'cc_field' => TRUE,
+ 'attributes' => array(
+ 'size' => 20,
+ 'maxlength' => 20,
+ 'autocomplete' => 'off'
+ ),
+ 'is_required' => TRUE,
+ ),
+ 'cvv2' => array(
+ 'htmlType' => 'text',
+ 'name' => 'cvv2',
+ 'title' => ts('Security Code'),
+ 'cc_field' => TRUE,
+ 'attributes' => array(
+ 'size' => 5,
+ 'maxlength' => 10,
+ 'autocomplete' => 'off'
+ ),
+ 'is_required' => CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME,
+ 'cvv_backoffice_required',
+ NULL,
+ 1
+ ),
+ 'rules' => array(
+ array(
+ 'rule_message' => ts('Please enter a valid value for your card security code. This is usually the last 3-4 digits on the card\'s signature panel.'),
+ 'rule_name' => 'integer',
+ 'rule_parameters' => NULL,
+ )),
+ ),
+ 'credit_card_exp_date' => array(
+ 'htmlType' => 'date',
+ 'name' => 'credit_card_exp_date',
+ 'title' => ts('Expiration Date'),
+ 'cc_field' => TRUE,
+ 'attributes' => CRM_Core_SelectValues::date('creditCard'),
+ 'is_required' => TRUE,
+ 'rules' => array(
+ array(
+ 'rule_message' => ts('Card expiration date cannot be a past date.'),
+ 'rule_name' => 'currentDate',
+ 'rule_parameters' => TRUE,
+ )),
+ ),
+ 'credit_card_type' => array(
+ 'htmlType' => 'select',
+ 'name' => 'credit_card_type',
+ 'title' => ts('Card Type'),
+ 'cc_field' => TRUE,
+ 'attributes' => $creditCardType,
+ 'is_required' => FALSE,
+ ),
+ 'account_holder' => array(
+ 'htmlType' => 'text',
+ 'name' => 'account_holder',
+ 'title' => ts('Account Holder'),
+ 'cc_field' => TRUE,
+ 'attributes' => array(
+ 'size' => 20,
+ 'maxlength' => 34,
+ 'autocomplete' => 'on'
+ ),
+ 'is_required' => TRUE,
+ ),
+ //e.g. IBAN can have maxlength of 34 digits
+ 'bank_account_number' => array(
+ 'htmlType' => 'text',
+ 'name' => 'bank_account_number',
+ 'title' => ts('Bank Account Number'),
+ 'cc_field' => TRUE,
+ 'attributes' => array(
+ 'size' => 20,
+ 'maxlength' => 34,
+ 'autocomplete' => 'off'
+ ),
+ 'rules' => array(
+ array(
+ 'rule_message' => ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
+ 'rule_name' => 'nopunctuation',
+ 'rule_parameters' => NULL,
+ )),
+ 'is_required' => TRUE,
+ ),
+ //e.g. SWIFT-BIC can have maxlength of 11 digits
+ 'bank_identification_number' => array(
+ 'htmlType' => 'text',
+ 'name' => 'bank_identification_number',
+ 'title' => ts('Bank Identification Number'),
+ 'cc_field' => TRUE,
+ 'attributes' => array(
+ 'size' => 20,
+ 'maxlength' => 11,
+ 'autocomplete' => 'off'
+ ),
+ 'is_required' => TRUE,
+ 'rules' => array(
+ array(
+ 'rule_message' => ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
+ 'rule_name' => 'nopunctuation',
+ 'rule_parameters' => NULL,
+ )),
+ ),
+ 'bank_name' => array(
+ 'htmlType' => 'text',
+ 'name' => 'bank_name',
+ 'title' => ts('Bank Name'),
+ 'cc_field' => TRUE,
+ 'attributes' => array(
+ 'size' => 20,
+ 'maxlength' => 64,
+ 'autocomplete' => 'off'
+ ),
+ 'is_required' => TRUE,
+
+ )
+ );
+ }
/**
* This function collects all the information from a web/api form and invokes
/**
* Add payment fields are depending on payment type
- *
+ * @deprecated - use the setPaymentFieldsByPaymentType which leverages the processor to determine the fields
* @param int $type eg CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT
* @param CRM_Core_Form $form
*/
}
}
+
+ /**
+ * Add payment fields are depending on payment type
+ *
+ * @param CRM_Core_Form $form
+ * @param array $processor array of properties including 'object' as loaded from CRM_Financial_BAO_PaymentProcessor::getPaymentProcessors
+ */
+ static public function setPaymentFieldsByProcessor(&$form, $processor) {
+ $form->billingFieldSets = array();
+ $paymentFields = self::getPaymentFields($processor);
+ dpm($paymentFields);
+ $paymentTypeName = self::getPaymentTypeName($processor);
+ $paymentTypeLabel = self::getPaymentTypeLabel($processor);
+ //@todo if we switch to iterating through the fieldsets we won't need to assign these directly
+ $form->assign('paymentTypeName', $paymentTypeName);
+ $form->assign('paymentTypeLabel', $paymentTypeLabel);
+
+ $form->billingFieldSets[$paymentTypeName]['fields'] = $form->_paymentFields = array_intersect_key(self::getPaymentFieldMetadata($processor), array_flip($paymentFields));
+ $form->billingPane = array($paymentTypeName => $paymentTypeLabel);
+ $form->assign('paymentFields', $paymentFields);
+ if ($processor['billing_mode'] != 4) {
+ //@todo setPaymentFields defines the billing fields - this should be moved to the processor class & renamed getBillingFields
+ //also set the billingFieldSet to hold all the details required to render the fieldset so we can iterate through the fieldset - making
+ // it easier to re-order. For not the billingFieldSets param is used to determine whether to show the billing pane
+ CRM_Core_Payment_Form::_setPaymentFields($form);
+ $form->billingFieldSets['billing_name_address-group']['fields'] = array();
+ }
+ }
/**
* create all common fields needed for a credit card or direct debit transaction
*
/**
* create all fields needed for a credit card transaction
- *
+ * @deprecated - use the setPaymentFieldsByPaymentType which leverages the processor to determine the fields
* @param CRM_Core_Form $form
*
* @return void
/**
* @param CRM_Core_Form $form
* @param bool $useRequired
+ * @param array $paymentFields
*/
- static function addCommonFields(&$form, $useRequired) {
- foreach ($form->_paymentFields as $name => $field) {
+ static function addCommonFields(&$form, $useRequired, $paymentFields) {
+ foreach ($paymentFields as $name => $field) {
if (!empty($field['cc_field'])) {
if ($field['htmlType'] == 'chainSelect') {
$form->addChainSelect($field['name'], array('required' => $useRequired && $field['is_required']));
/**
* create all fields needed for direct debit transaction
- *
+ * @deprecated - use the setPaymentFieldsByPaymentType which leverages the processor to determine the fields
* @param $form
*
* @return void
/**
* @param array $paymentProcessor
- * @todo it may be necessary to set details that affect it - mostly likely take Country as a param
+ * @todo it will be necessary to set details that affect it - mostly likely take Country as a param. Should we add generic
+ * setParams on processor class or just setCountry which we know we need?
*
- * @return mixed
+ * @return array
*/
static function getPaymentFields($paymentProcessor) {
- return array();
$paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
- return $paymentProcessorObject->getPaymentFields();
+ return $paymentProcessorObject->getPaymentFormFields();
}
/**
- * @param $form
+ * @param array $paymentProcessor
*
+ * @return array
+ */
+ static function getPaymentFieldMetadata($paymentProcessor) {
+ $paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
+ return $paymentProcessorObject->getPaymentFormFieldsMetadata();
+ }
+
+ /**
* @param array $paymentProcessor
- * @todo it may be necessary to set details that affect it - mostly likely take Country as a param
*
- * @return mixed
+ * @return string
*/
- protected static function setPaymentFields(&$form, $paymentProcessor) {
- $fields = self::getPaymentFields($paymentProcessor);
+ static function getPaymentTypeName($paymentProcessor) {
+ $paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
+ return $paymentProcessorObject->getPaymentTypeName();
+ }
+ /**
+ * @param array $paymentProcessor
+ *
+ * @return string
+ */
+ static function getPaymentTypeLabel($paymentProcessor) {
+ $paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
+ return ts(($paymentProcessorObject->getPaymentTypeLabel()) . ' Information');
}
- static function buildPaymentForm(&$form, $useRequired = FALSE) {
+ /**
+ * @param CRM_Core_Form $form
+ * @param array $processor array of properties including 'object' as loaded from CRM_Financial_BAO_PaymentProcessor::getPaymentProcessors
+ * @param bool $isBillingDataOptional
+ *
+ * @return bool
+ */
+ static function buildPaymentForm($form, $processor, $isBillingDataOptional){
+ self::setPaymentFieldsByProcessor($form, $processor);
+ self::addCommonFields($form, !$isBillingDataOptional, $form->_paymentFields);
+ self::addRules($form, $form->_paymentFields);
+ self::addPaypalExpressCode($form);
+ return (!empty($form->_paymentFields));
+ }
+ /**
+ * @param CRM_Core_Form $form
+ * @param array $paymentFields array of properties including 'object' as loaded from CRM_Financial_BAO_PaymentProcessor::getPaymentProcessors
+
+ * @param $paymentFields
+ */
+ static function addRules(&$form, $paymentFields) {
+ foreach ($paymentFields as $paymentField => $fieldSpecs) {
+ if (!empty($fieldSpecs['rules'])) {
+ foreach ($fieldSpecs['rules'] as $rule) {
+ $form->addRule($paymentField,
+ $rule['rule_message'],
+ $rule['rule_name'],
+ $rule['rule_parameters']
+ );
+ }
+ }
+ }
}
/**
- * Function to add all the credit card fields
+ * billing mode button is basically synonymous with paypal express - this is probably a good example of 'odds & sods' code we
+ * need to find a way for the payment processor to assign. A tricky aspect is that the payment processor may need to set the order
*
* @param $form
+ */
+ static function addPaypalExpressCode(&$form) {
+ if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
+ $form->_expressButtonName = $form->getButtonName('upload', 'express');
+ $form->assign('expressButtonName', $form->_expressButtonName);
+ $form->add('image',
+ $form->_expressButtonName,
+ $form->_paymentProcessor['url_button'],
+ array('class' => 'crm-form-submit')
+ );
+ }
+ }
+ /**
+ * Function to add all the credit card fields
+ * @deprecated Use BuildPaymentForm
+ * @param $form
* @param bool $useRequired
*
* @return void
static function buildCreditCard(&$form, $useRequired = FALSE) {
if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
self::setCreditCardFields($form);
- self::addCommonFields($form, $useRequired);
+ self::addCommonFields($form, $useRequired, $form->_paymentFields);
$form->addRule('cvv2',
ts('Please enter a valid value for your card security code. This is usually the last 3-4 digits on the card\'s signature panel.'),
}
+
if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
$form->_expressButtonName = $form->getButtonName('upload', 'express');
$form->assign('expressButtonName', $form->_expressButtonName);
static function buildDirectDebit(&$form, $useRequired = FALSE) {
if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
self::setDirectDebitFields($form);
- self::addCommonFields($form, $useRequired);
+ self::addCommonFields($form, $useRequired, $form->_paymentFields);
$form->addRule('bank_identification_number',
ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
'nopunctuation'
);
}
-
- if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
- $form->_expressButtonName = $form->getButtonName($form->buttonType(), 'express');
- $form->add('image',
- $form->_expressButtonName,
- $form->_paymentProcessor['url_button'],
- array('class' => 'crm-form-submit')
- );
- }
}
/**
}
}
* */
- $retrievalParameters = array('is_active' => TRUE, 'options' => array('sort' => 'is_default, name'));
+ $retrievalParameters = array('is_active' => TRUE, 'options' => array('sort' => 'is_default DESC, name'));
if ($isExcludeTest) {
$retrievalParameters['is_test'] = 0;
}
* @param array $capabilities
* @param bool $isIncludeTest
*
+ * @param array $ids
+ *
* @return array available processors
*/
- static function getPaymentProcessors($capabilities = array(), $isIncludeTest = FALSE, $reset = FALSE) {
+ static function getPaymentProcessors($capabilities = array(), $isIncludeTest = FALSE, $ids = array()) {
$processors = self::getAllPaymentProcessors(!$isIncludeTest);
if ($capabilities) {
foreach ($processors as $index => $processor) {
+ if (!empty($ids) && !in_array($processor['id'], $ids)) {
+ unset ($processors[$index]);
+ continue;
+ }
if (($error = $processor['object']->checkConfig()) != NULL) {
unset ($processors[$index]);
continue;
}
}
+ /**
+ * convert possibly underscore separated words to camel case with special handling for 'UF'
+ * e.g
+ * membership_payment returns MembershipPayment
+ * @param string $string
+ *
+ * @return string string
+ */
+ static function convertStringToCamel($string) {
+ $fragments = explode('_', $string);
+ foreach ($fragments as & $fragment) {
+ $fragment = ucfirst($fragment);
+ }
+ // Special case: UFGroup, UFJoin, UFMatch, UFField
+ if ($fragments[0] === 'Uf') {
+ $fragments[0] = 'UF';
+ }
+ return implode('', $fragments);
+ }
+
/**
*
* Takes a variable name and munges it randomly into another variable name
/**
* @param $entity
- * @param null $version
*
* @return string
*/
-function _civicrm_api_get_camel_name($entity, $version = NULL) {
- $fragments = explode('_', $entity);
- foreach ($fragments as & $fragment) {
- $fragment = ucfirst($fragment);
- }
- // Special case: UFGroup, UFJoin, UFMatch, UFField
- if ($fragments[0] === 'Uf') {
- $fragments[0] = 'UF';
- }
- return implode('', $fragments);
+function _civicrm_api_get_camel_name($entity) {
+ return CRM_Utils_String::convertStringToCamel($entity);
}
/**
*}
{crmRegion name="billing-block"}
<div id="payment_information">
- {if $paymentFields}
+ {if $paymentFields|@count}
<fieldset class="billing_mode-group {$paymentTypeName}_info-group">
<legend>
{$paymentTypeLabel}
</div>
</fieldset>
{/if}
- {if $billingDetailsFields}
+ {if $billingDetailsFields|@count}
{if $profileAddressFields}
<input type="checkbox" id="billingcheckbox" value="0">
<label for="billingcheckbox">{ts}My billing address is the same as above{/ts}</label>