This also reflects work towards allowing payment_instrument specific fields on the payment form - ie. check, credit_card_type, pan_truncation.
This idea is to finish the code allowing pay-later to be the manual payment processor indexed with a 0 on all payment forms, and have the
form render with appropriate fields. Note that I got this working on multiple forms, this commit reflects the smallest piece I could
break off that is complete within itself, and resolves an issue
I tested that it was still possible to enter checks on the offline contribution as well as membership & additional payment forms, and the
check number still shows up.
I also tested front end contribution credit card & pay later & front end event forms
$ids
));
- if (!$paymentProcessorID && $this->contribution_page_id) {
+ if (!isset($input['payment_processor_id']) && !$paymentProcessorID && $this->contribution_page_id) {
$paymentProcessorID = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionPage',
$this->contribution_page_id,
'payment_processor'
/**
* Is this a backoffice form
- * (this will affect whether paypal express code is displayed)
+ *
* @var bool
*/
public $isBackOffice = TRUE;
protected $_formType;
+ /**
+ * Payment instrument id for the transaction.
+ *
+ * @var int
+ */
+ public $paymentInstrumentID;
+
/**
* Array of fields to display on billingBlock.tpl - this is not fully implemented but basically intent is the panes/fieldsets on this page should
* be all in this array in order like
* @throws Exception
*/
public function getValidProcessors() {
- $defaultID = NULL;
$capabilities = array('BackOffice');
if ($this->_mode) {
$capabilities[] = (ucfirst($this->_mode) . 'Mode');
public function assignProcessors() {
//ensure that processor has a valid config
//only valid processors get display to user
-
- if ($this->_mode) {
- $this->assign('processorSupportsFutureStartDate', CRM_Financial_BAO_PaymentProcessor::hasPaymentProcessorSupporting(array('FutureRecurStartDate')));
- $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 ($this->_paymentProcessors as $id => $processor) {
- // @todo review this. The inclusion of this IF was to address test processors being incorrectly loaded.
- // However the function $this->getValidProcessors() is expected to only return the processors relevant
- // to the mode (using the actual id - ie. the id of the test processor for the test processor).
- // for some reason there was a need to filter here per commit history - but this indicates a problem
- // somewhere else.
- if ($processor['is_test'] == ($this->_mode == 'test')) {
- $this->_processors[$id] = ts($processor['name']);
- if (!empty($processor['description'])) {
- $this->_processors[$id] .= ' : ' . ts($processor['description']);
- }
- if ($processor['is_recur']) {
- $this->_recurPaymentProcessors[$id] = $this->_processors[$id];
- }
+ $this->assign('processorSupportsFutureStartDate', CRM_Financial_BAO_PaymentProcessor::hasPaymentProcessorSupporting(array('FutureRecurStartDate')));
+ $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 (!$this->_mode) {
+ $this->_paymentProcessor = $this->_paymentProcessors[0];
+ }
+ elseif (empty($this->_paymentProcessors) || array_keys($this->_paymentProcessors) === array(0)) {
+ 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 ($this->_paymentProcessors as $id => $processor) {
+ // @todo review this. The inclusion of this IF was to address test processors being incorrectly loaded.
+ // However the function $this->getValidProcessors() is expected to only return the processors relevant
+ // to the mode (using the actual id - ie. the id of the test processor for the test processor).
+ // for some reason there was a need to filter here per commit history - but this indicates a problem
+ // somewhere else.
+ if ($processor['is_test'] == ($this->_mode == 'test')) {
+ $this->_processors[$id] = ts($processor['name']);
+ if (!empty($processor['description'])) {
+ $this->_processors[$id] .= ' : ' . ts($processor['description']);
+ }
+ if ($processor['is_recur']) {
+ $this->_recurPaymentProcessors[$id] = $this->_processors[$id];
}
}
- CRM_Financial_Form_Payment::addCreditCardJs($id);
}
+ CRM_Financial_Form_Payment::addCreditCardJs($id);
+
$this->assign('recurringPaymentProcessorIds',
empty($this->_recurPaymentProcessors) ? '' : implode(',', array_keys($this->_recurPaymentProcessors))
);
list($this->userDisplayName, $this->userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID);
$this->assign('displayName', $this->userDisplayName);
}
- if ($this->_mode) {
- $this->assignProcessors();
-
- $this->assignBillingType();
-
- CRM_Core_Payment_Form::setPaymentFieldsByProcessor($this, $this->_paymentProcessor, FALSE, TRUE);
- }
+ $this->assignProcessors();
+ $this->assignBillingType();
+ CRM_Core_Payment_Form::setPaymentFieldsByProcessor($this, $this->_paymentProcessor, FALSE, TRUE, CRM_Utils_Request::retrieve('payment_instrument_id', 'Integer'));
}
catch (CRM_Core_Exception $e) {
CRM_Core_Error::fatal($e->getMessage());
return;
}
- CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE, TRUE);
+ CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE, TRUE, CRM_Utils_Request::retrieve('payment_instrument_id', 'Integer'));
$attributes = CRM_Core_DAO::getAttribute('CRM_Financial_DAO_FinancialTrxn');
$this->add('select', 'payment_processor_id', ts('Payment Processor'), $this->_processors, NULL);
$params['is_email_receipt'] = $isEmailReceipt;
}
$params['is_recur'] = $isRecur;
+ $params['payment_instrument_id'] = $contributionParams['payment_instrument_id'];
$recurringContributionID = self::processRecurringContribution($form, $params, $contactID, $financialType);
$nonDeductibleAmount = self::getNonDeductibleAmount($params, $financialType, $online, $form);
// also it reset any payment processor selection result into pending free membership
// so its a kind of hack to complete free membership at this point since there is no $form->_paymentProcessor info
if (!empty($membershipContribution) && !is_a($membershipContribution, 'CRM_Core_Error')) {
- $paymentProcessorIDs = explode(CRM_Core_DAO::VALUE_SEPARATOR, CRM_Utils_Array::value('payment_processor', $this->_values));
- if (empty($form->_paymentProcessor) && !empty($paymentProcessorIDs)) {
+ if (empty($form->_paymentProcessor)) {
+ // @todo this can maybe go now we are setting payment_processor_id = 0 more reliably.
+ $paymentProcessorIDs = explode(CRM_Core_DAO::VALUE_SEPARATOR, CRM_Utils_Array::value('payment_processor', $this->_values));
$this->_paymentProcessor['id'] = $paymentProcessorIDs[0];
}
$result = array('payment_status_id' => 1, 'contribution' => $membershipContribution);
$form->_values['fee'] = $priceSetFields['fields'];
$form->_priceSetId = $priceSetID;
$form->setFormAmountFields($priceSetID);
+ $capabilities = array();
+ if ($form->_mode) {
+ $capabilities[] = (ucfirst($form->_mode) . 'Mode');
+ }
+ $form->_paymentProcessors = CRM_Financial_BAO_PaymentProcessor::getPaymentProcessors($capabilities);
+ $form->_params['payment_processor_id'] = !empty($params['payment_processor_id']) ? $params['payment_processor_id'] : 0;
+ $form->_paymentProcessor = $form->_paymentProcessors[$form->_params['payment_processor_id']];
if (!empty($params['payment_processor_id'])) {
- $form->_paymentProcessor = civicrm_api3('payment_processor', 'getsingle', array(
- 'id' => $params['payment_processor_id'],
- ));
// The concept of contributeMode is deprecated as is the billing_mode concept.
if ($form->_paymentProcessor['billing_mode'] == 1) {
$form->_contributeMode = 'direct';
$form->_contributeMode = 'notify';
}
}
- else {
- $form->_params['payment_processor_id'] = 0;
- }
$priceFields = $priceFields[$priceSetID]['fields'];
CRM_Price_BAO_PriceSet::processAmount($priceFields, $paramsProcessedForForm, $lineItems, 'civicrm_contribution');
*/
public $_isBillingAddressRequiredForPayLater;
+ /**
+ * Is this a backoffice form
+ * (this will affect whether paypal express code is displayed)
+ * @var bool
+ */
+ public $isBackOffice = FALSE;
+
+ /**
+ * Payment instrument if for the transaction.
+ *
+ * This will generally be drawn from the payment processor and is ignored for
+ * front end forms.
+ *
+ * @var int
+ */
+ public $paymentInstrumentID;
+
/**
* Set variables up before form is built.
*
*/
protected $billingProfile;
+ /**
+ * Payment instrument ID.
+ *
+ * This is normally retrieved from the payment_processor table.
+ *
+ * @var int
+ */
+ protected $paymentInstrumentID;
+
+ /**
+ * Is this a back office transaction.
+ *
+ * @var bool
+ */
+ protected $backOffice = FALSE;
+
+ /**
+ * @return bool
+ */
+ public function isBackOffice() {
+ return $this->backOffice;
+ }
+
+ /**
+ * Set back office property.
+ *
+ * @param bool $isBackOffice
+ */
+ public function setBackOffice($isBackOffice) {
+ $this->backOffice = $isBackOffice;
+ }
+
+ /**
+ * Get payment instrument id.
+ *
+ * @return int
+ */
+ public function getPaymentInstrumentID() {
+ return $this->paymentInstrumentID ? $this->paymentInstrumentID : $this->_paymentProcessor['payment_instrument_id'];
+ }
+
+ /**
+ * Set payment Instrument id.
+ *
+ * By default we actually ignore the form value. The manual processor takes it more seriously.
+ *
+ * @param int $paymentInstrumentID
+ */
+ public function setPaymentInstrumentID($paymentInstrumentID) {
+ $this->paymentInstrumentID = $this->_paymentProcessor['payment_instrument_id'];
+ }
+
/**
* Set base return path (offsite processors).
*
* Display billing fields even for pay later.
* @param bool $isBackOffice
* Is this a back office function? If so the option to suppress the cvn needs to be evaluated.
+ * @param int $paymentInstrumentID
+ * ID of the payment processor.
*/
- static public function setPaymentFieldsByProcessor(&$form, $processor, $billing_profile_id = NULL, $isBackOffice = FALSE) {
+ static public function setPaymentFieldsByProcessor(&$form, $processor, $billing_profile_id = NULL, $isBackOffice = FALSE, $paymentInstrumentID = NULL) {
$form->billingFieldSets = array();
// Load the pay-later processor
// @todo load this right up where the other processors are loaded initially.
}
$processor['object']->setBillingProfile($billing_profile_id);
+ $processor['object']->setBackOffice($isBackOffice);
+ $processor['object']->setPaymentInstrumentID($paymentInstrumentID);
$paymentTypeName = self::getPaymentTypeName($processor);
$paymentTypeLabel = self::getPaymentTypeLabel($processor);
$form->assign('paymentTypeName', $paymentTypeName);
$form->assign('paymentTypeLabel', $paymentTypeLabel);
+ $form->assign('isBackOffice', $isBackOffice);
$form->_paymentFields = $form->billingFieldSets[$paymentTypeName]['fields'] = self::getPaymentFieldMetadata($processor);
$form->_paymentFields = array_merge($form->_paymentFields, self::getBillingAddressMetadata($processor, $form->_bltID));
$form->assign('paymentFields', self::getPaymentFields($processor));
protected static function addCommonFields(&$form, $paymentFields) {
$requiredPaymentFields = array();
foreach ($paymentFields as $name => $field) {
+ // @todo - remove the cc_field check - no longer useful.
if (!empty($field['cc_field'])) {
if ($field['htmlType'] == 'chainSelect') {
$form->addChainSelect($field['name'], array('required' => FALSE));
* @return array
*/
public static function getPaymentFields($paymentProcessor) {
- $paymentProcessorObject = Civi\Payment\System::singleton()->getByProcessor($paymentProcessor);
- return $paymentProcessorObject->getPaymentFormFields();
+ return $paymentProcessor['object']->getPaymentFormFields();
}
/**
* @return array
*/
public static function getPaymentFieldMetadata($paymentProcessor) {
- $paymentProcessorObject = Civi\Payment\System::singleton()->getByProcessor($paymentProcessor);
- return array_intersect_key($paymentProcessorObject->getPaymentFormFieldsMetadata(), array_flip(self::getPaymentFields($paymentProcessor)));
+ return array_intersect_key($paymentProcessor['object']->getPaymentFormFieldsMetadata(), array_flip(self::getPaymentFields($paymentProcessor)));
}
/**
* although the distinction is losing it's meaning as front end forms are used for back office and a permission
* for the 'enter without cvn' is probably more appropriate. Paypal std does not support another user
* entering details but once again the issue is not back office but 'another user'.
+ * @param int $paymentInstrumentID
+ * Payment instrument ID.
*
* @return bool
*/
- public static function buildPaymentForm(&$form, $processor, $billing_profile_id, $isBackOffice) {
+ public static function buildPaymentForm(&$form, $processor, $billing_profile_id, $isBackOffice, $paymentInstrumentID = NULL) {
//if the form has address fields assign to the template so the js can decide what billing fields to show
$profileAddressFields = $form->get('profileAddressFields');
if (!empty($profileAddressFields)) {
return NULL;
}
- self::setPaymentFieldsByProcessor($form, $processor, $billing_profile_id, $isBackOffice);
+ self::setPaymentFieldsByProcessor($form, $processor, $billing_profile_id, $isBackOffice, $paymentInstrumentID);
self::addCommonFields($form, $form->_paymentFields);
self::addRules($form, $form->_paymentFields);
return (!empty($form->_paymentFields));
$this->result = $result;
}
+ /**
+ * Set payment instrument id.
+ *
+ * @param int $paymentInstrumentID
+ */
+ public function setPaymentInstrumentID($paymentInstrumentID) {
+ $this->paymentInstrumentID = $paymentInstrumentID;
+ }
+
/**
* Get the name of the payment type.
*
return TRUE;
}
+ /**
+ * Are back office payments supported.
+ *
+ * @return bool
+ */
+ protected function supportsBackOffice() {
+ return TRUE;
+ }
+
/**
* Submit a manual payment.
*
* @param CRM_Core_Form $form
*/
protected function addPaypalExpressCode(&$form) {
+ // @todo use $this->isBackOffice() instead, test.
if (empty($form->isBackOffice)) {
$form->_expressButtonName = $form->getButtonName('upload', 'express');
$form->assign('expressButtonName', $form->_expressButtonName);
}
if ($form->_type) {
+ // @todo not sure when this would be true. Never passed in.
$form->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($form->_type, $form->_mode);
}
}
$form->set('paymentProcessor', $form->_paymentProcessor);
$form->_paymentObject = System::singleton()->getByProcessor($form->_paymentProcessor);
+ if ($form->paymentInstrumentID) {
+ $form->_paymentObject->setPaymentInstrumentID($form->paymentInstrumentID);
+ }
+ $form->_paymentObject->setBackOffice($form->isBackOffice);
+ $form->assign('isBackOffice', $form->isBackOffice);
$form->assign('suppressSubmitButton', $form->_paymentObject->isSuppressSubmitButtons());
CRM_Core_Payment_Form::setPaymentFieldsByProcessor(
$form,
$form->_paymentProcessor,
- CRM_Utils_Request::retrieve('billing_profile_id', 'String')
+ CRM_Utils_Request::retrieve('billing_profile_id', 'String'),
+ $form->isBackOffice,
+ $form->paymentInstrumentID
);
$form->assign_by_ref('paymentProcessor', $form->_paymentProcessor);
*
* @param CRM_Core_Form $form
*/
- public static function buildQuickform(&$form) {
+ public static function buildQuickForm(&$form) {
//@todo document why this addHidden is here
//CRM-15743 - we should not set/create hidden element for pay later
// because payment processor is not selected
if (!empty($processorId)) {
$form->addElement('hidden', 'hidden_processor', 1);
}
- CRM_Core_Payment_Form::buildPaymentForm($form, $form->_paymentProcessor, $billing_profile_id, FALSE);
+ CRM_Core_Payment_Form::buildPaymentForm($form, $form->_paymentProcessor, $billing_profile_id, $form->isBackOffice, $form->paymentInstrumentID);
}
}
public $_forcePayement;
+ /**
+ * @deprecated
+ *
+ * @var
+ */
public $_isBillingAddressRequiredForPayLater;
+ /**
+ * Is this a back office form
+ *
+ * @var bool
+ */
+ public $isBackOffice = FALSE;
+
+ /**
+ * Payment instrument iD for the transaction.
+ *
+ * This will generally be drawn from the payment processor and is ignored for
+ * front end forms.
+ *
+ * @var int
+ */
+ public $paymentInstrumentID;
+
/**
* Set variables up before form is built.
*/
'name' => 'pay_later',
'billing_mode' => '',
'is_default' => 0,
- // This should ideally be retrieved from the DB but existing default is check so we'll code that for now.
- 'payment_instrument_id' => CRM_Core_OptionGroup::getValue('payment_instrument', 'Check', 'name'),
+ 'payment_instrument_id' => key(CRM_Core_OptionGroup::values('payment_instrument', FALSE, FALSE, FALSE, 'AND is_default = 1')),
// Making this optionally recur would give lots of options -but it should
// be a row in the payment processor table before we do that.
'is_recur' => FALSE,
+ 'is_test' => FALSE,
);
CRM_Utils_Cache::singleton()->set($cacheKey, $processors['values']);
* available processors
*/
public static function getPaymentProcessors($capabilities = array(), $ids = FALSE) {
- $mode = NULL;
$testProcessors = in_array('TestMode', $capabilities) ? self::getAllPaymentProcessors('test') : array();
if (is_array($ids)) {
$processors = self::getAllPaymentProcessors('all', TRUE, FALSE);
* @var array
*/
public $_paymentProcessor;
+
+ /**
+ * @var bool
+ */
+ public $isBackOffice = FALSE;
+
/**
* Set variables up before form is built.
*/
$this->currency = CRM_Utils_Request::retrieve('currency', 'String', CRM_Core_DAO::$_nullObject,
TRUE);
+ $this->paymentInstrumentID = CRM_Utils_Request::retrieve('payment_instrument_id', 'Integer');
+ $this->isBackOffice = CRM_Utils_Request::retrieve('is_back_office', 'Integer');
+
$this->assignBillingType();
$this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($this->_paymentProcessorID);
* @param int $paymentProcessorID
*/
public static function addCreditCardJs($paymentProcessorID = NULL) {
- $creditCards = array();
$creditCards = CRM_Financial_BAO_PaymentProcessor::getCreditCards($paymentProcessorID);
$creditCardTypes = CRM_Core_Payment_Form::getCreditCardCSSNames($creditCards);
CRM_Core_Resources::singleton()
);
CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE, TRUE);
}
+
// Build the form for auto renew. This is displayed when in credit card mode or update mode.
// The reason for showing it in update mode is not that clear.
if ($this->_mode || ($this->_action & CRM_Core_Action::UPDATE)) {
ts('Membership renewed automatically')
);
- $this->assignPaymentRelatedVariables();
}
$this->assign('autoRenewOptions', json_encode($this->membershipTypeRenewalStatus));
}
}
- if ($this->_mode) {
- $this->assignPaymentRelatedVariables();
- }
+ $this->assignPaymentRelatedVariables();
if ($this->_id) {
$this->_memType = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $this->_id, 'membership_type_id');
* @param array $formValues
*/
public function testSubmit($formValues) {
+ $this->setContextVariables($formValues);
$this->_memType = $formValues['membership_type_id'][1];
$this->_params = $formValues;
$this->submit();
$joinDate = $startDate = $endDate = NULL;
$membershipTypes = $membership = $calcDate = array();
$membershipType = NULL;
+ $paymentInstrumentID = $this->_paymentProcessor['object']->getPaymentInstrumentID();
$mailSend = FALSE;
$formValues = $this->setPriceSetParameters($formValues);
$config = CRM_Core_Config::singleton();
+ // @todo this is no longer required if we convert some date fields.
$this->convertDateFieldsToMySQL($formValues);
$membershipTypeValues = array();
$params['financial_type_id'] = CRM_Utils_Array::value('financial_type_id', $formValues);
}
- // @todo - test removing this line. The beginPostProcess Function should have done it for us.
- $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($formValues['payment_processor_id'],
- $this->_mode
- );
-
//get the payment processor id as per mode. Try removing in favour of beginPostProcess.
$params['payment_processor_id'] = $formValues['payment_processor_id'] = $this->_paymentProcessor['id'];
$params['register_date'] = date('YmdHis');
$financialType->id = $params['financial_type_id'];
$financialType->find(TRUE);
$this->_params = $formValues;
- $paymentParams['payment_instrument_id'] = $this->_paymentProcessor['payment_instrument_id'];
+
$contribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution($this,
$paymentParams,
NULL,
'contribution_page_id' => CRM_Utils_Array::value('contribution_page_id', $formValues),
'source' => CRM_Utils_Array::value('source', $paymentParams, CRM_Utils_Array::value('description', $paymentParams)),
'thankyou_date' => CRM_Utils_Array::value('thankyou_date', $paymentParams),
- 'payment_instrument_id' => $this->_paymentProcessor['payment_instrument_id'],
+ 'payment_instrument_id' => $paymentInstrumentID,
),
$financialType,
FALSE,
// of a single path!
unset($membershipParams['lineItems']);
}
-
+ $membershipParams['payment_instrument_id'] = $paymentInstrumentID;
$membership = CRM_Member_BAO_Membership::create($membershipParams, $ids);
$params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams);
unset($params['lineItems']);
{capture assign='profilePathVar'}{/capture}
{/if}
- var dataUrl = "{crmURL p='civicrm/payment/form' h=0 q="currency=`$currency`&`$urlPathVar``$profilePathVar``$contributionPageID``$preProfileID`processor_id="}" + type;
+ {capture assign='isBackOfficePathVar'}&is_back_office={$isBackOffice}&{/capture}
+
+ var payment_instrument_id = $('#payment_instrument_id').val();
+
+ var dataUrl = "{crmURL p='civicrm/payment/form' h=0 q="¤cy=`$currency`&`$urlPathVar``$isBackOfficePathVar``$profilePathVar``$contributionPageID``$preProfileID`processor_id="}" + type;
{literal}
if (typeof(CRM.vars) != "undefined") {
if (typeof(CRM.vars.coreForm) != "undefined") {
}
}
}
+ dataUrl = dataUrl + "&payment_instrument_id=" + payment_instrument_id;
// Processors like pp-express will hide the form submit buttons, so re-show them when switching
$('.crm-submit-buttons', $form).show().find('input').prop('disabled', true);
$('[name=payment_processor_id]').on('change.paymentBlock', function() {
buildPaymentBlock($(this).val());
});
+
$('#billing-payment-block').on('crmLoad', function() {
$('.crm-submit-buttons input').prop('disabled', false);
})
'billing_last_name' => 'Gruffier',
'selectMembership' => $this->_ids['membership_type'],
'email-Primary' => 'billy-goat@the-new-bridge.net',
+ 'payment_processor_id' => $this->params['payment_processor_id'],
);
$this->callAPISuccess('contribution_page', 'submit', $submitParams);