/** * Process the Memberships. * * @param array $membershipParams * Array of membership fields. * @param int $contactID * Contact id. * @param CRM_Contribute_Form_Contribution_Confirm $form * Confirmation form object. * * @param array $premiumParams * @param null $customFieldsFormatted * @param null $includeFieldTypes * * @param array $membershipDetails * * @param array $membershipTypeIDs * * @param bool $isPaidMembership * @param array $membershipID * * @param bool $isProcessSeparateMembershipTransaction * * @param int $financialTypeID * @param array $membershipLineItems * Line items specific to membership payment that is separate to contribution. * @param bool $isPayLater * @param bool $isPending * * @throws \CRM_Core_Exception */ protected function postProcessMembership($membershipParams, $contactID, &$form, $premiumParams, $customFieldsFormatted = NULL, $includeFieldTypes = NULL, $membershipDetails, $membershipTypeIDs, $isPaidMembership, $membershipID, $isProcessSeparateMembershipTransaction, $financialTypeID, $membershipLineItems, $isPayLater, $isPending) { $membershipContribution = NULL; $isTest = CRM_Utils_Array::value('is_test', $membershipParams, FALSE); $errors = $createdMemberships = $paymentResults = array(); $form->_values['isMembership'] = TRUE; $isRecurForFirstTransaction = CRM_Utils_Array::value('is_recur', $form->_values, CRM_Utils_Array::value('is_recur', $membershipParams)); $totalAmount = $membershipParams['amount']; if ($isPaidMembership) { if ($isProcessSeparateMembershipTransaction) { // If we have 2 transactions only one can use the invoice id. $membershipParams['invoiceID'] .= '-2'; if (!empty($membershipParams['auto_renew'])) { $isRecurForFirstTransaction = FALSE; } } $paymentResult = CRM_Contribute_BAO_Contribution_Utils::processConfirm($form, $membershipParams, $contactID, $financialTypeID, 'membership', $isTest, $isRecurForFirstTransaction); if (!empty($paymentResult['contribution'])) { $this->postProcessPremium($premiumParams, $paymentResult['contribution']); //note that this will be over-written if we are using a separate membership transaction. Otherwise there is only one $membershipContribution = $paymentResult['contribution']; // Save the contribution ID so that I can be used in email receipts // For example, if you need to generate a tax receipt for the donation only. $form->_values['contribution_other_id'] = $membershipContribution->id; } } if ($isProcessSeparateMembershipTransaction) { try { $form->_lineItem = $membershipLineItems; if (empty($form->_params['auto_renew']) && !empty($membershipParams['is_recur'])) { unset($membershipParams['is_recur']); } list($membershipContribution, $secondPaymentResult) = $this->processSecondaryFinancialTransaction($contactID, $form, $membershipParams, $isTest, $membershipLineItems, CRM_Utils_Array::value('minimum_fee', $membershipDetails, 0), CRM_Utils_Array::value('financial_type_id', $membershipDetails)); $paymentResults[] = array('contribution_id' => $membershipContribution->id, 'result' => $secondPaymentResult); } catch (CRM_Core_Exception $e) { $errors[2] = $e->getMessage(); $membershipContribution = NULL; } } $membership = NULL; if (!empty($membershipContribution) && !is_a($membershipContribution, 'CRM_Core_Error')) { $membershipContributionID = $membershipContribution->id; } //@todo - why is this nested so deep? it seems like it could be just set on the calling function on the form layer if (isset($membershipParams['onbehalf']) && !empty($membershipParams['onbehalf']['member_campaign_id'])) { $form->_params['campaign_id'] = $membershipParams['onbehalf']['member_campaign_id']; } //@todo it should no longer be possible for it to get to this point & membership to not be an array if (is_array($membershipTypeIDs) && !empty($membershipContributionID)) { $typesTerms = CRM_Utils_Array::value('types_terms', $membershipParams, array()); foreach ($membershipTypeIDs as $memType) { $numTerms = CRM_Utils_Array::value($memType, $typesTerms, 1); if (!empty($membershipContribution)) { $pendingStatus = CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name'); $pending = $membershipContribution->contribution_status_id == $pendingStatus ? TRUE : FALSE; } else { $pending = $isPending; } $contributionRecurID = isset($form->_params['contributionRecurID']) ? $form->_params['contributionRecurID'] : NULL; $membershipSource = NULL; if (!empty($form->_params['membership_source'])) { $membershipSource = $form->_params['membership_source']; } elseif (isset($form->_values['title']) && !empty($form->_values['title'])) { $membershipSource = ts('Online Contribution:') . ' ' . $form->_values['title']; } $isPayLater = NULL; if (isset($form->_params)) { $isPayLater = CRM_Utils_Array::value('is_pay_later', $form->_params); } $campaignId = NULL; if (isset($form->_values) && is_array($form->_values) && !empty($form->_values)) { $campaignId = CRM_Utils_Array::value('campaign_id', $form->_params); if (!array_key_exists('campaign_id', $form->_params)) { $campaignId = CRM_Utils_Array::value('campaign_id', $form->_values); } } list($membership, $renewalMode, $dates) = CRM_Member_BAO_Membership::renewMembership($contactID, $memType, $isTest, date('YmdHis'), CRM_Utils_Array::value('cms_contactID', $membershipParams), $customFieldsFormatted, $numTerms, $membershipID, $pending, $contributionRecurID, $membershipSource, $isPayLater, $campaignId); $form->set('renewal_mode', $renewalMode); if (!empty($dates)) { $form->assign('mem_start_date', CRM_Utils_Date::customFormat($dates['start_date'], '%Y%m%d')); $form->assign('mem_end_date', CRM_Utils_Date::customFormat($dates['end_date'], '%Y%m%d')); } if (!empty($membershipContribution)) { // update recurring id for membership record CRM_Member_BAO_Membership::updateRecurMembership($membership, $membershipContribution); CRM_Member_BAO_Membership::linkMembershipPayment($membership, $membershipContribution); } } if ($form->_priceSetId && !empty($form->_useForMember) && !empty($form->_lineItem)) { foreach ($form->_lineItem[$form->_priceSetId] as &$priceFieldOp) { if (!empty($priceFieldOp['membership_type_id']) && isset($createdMemberships[$priceFieldOp['membership_type_id']])) { $membershipOb = $createdMemberships[$priceFieldOp['membership_type_id']]; $priceFieldOp['start_date'] = $membershipOb->start_date ? CRM_Utils_Date::customFormat($membershipOb->start_date, '%B %E%f, %Y') : '-'; $priceFieldOp['end_date'] = $membershipOb->end_date ? CRM_Utils_Date::customFormat($membershipOb->end_date, '%B %E%f, %Y') : '-'; } else { $priceFieldOp['start_date'] = $priceFieldOp['end_date'] = 'N/A'; } } $form->_values['lineItem'] = $form->_lineItem; $form->assign('lineItem', $form->_lineItem); } } if (!empty($errors)) { $message = $this->compileErrorMessage($errors); throw new CRM_Core_Exception($message); } $form->_params['createdMembershipIDs'] = array(); // CRM-7851 - Moved after processing Payment Errors //@todo - the reasoning for this being here seems a little outdated foreach ($createdMemberships as $createdMembership) { CRM_Core_BAO_CustomValueTable::postProcess($form->_params, 'civicrm_membership', $createdMembership->id, 'Membership'); $form->_params['createdMembershipIDs'][] = $createdMembership->id; } if (count($createdMemberships) == 1) { //presumably this is only relevant for exactly 1 membership $form->_params['membershipID'] = $createdMembership->id; } //CRM-15232: Check if membership is created and on the basis of it use //membership receipt template to send payment receipt if (count($createdMemberships)) { $form->_values['isMembership'] = TRUE; } if (isset($membershipContributionID)) { $form->_values['contribution_id'] = $membershipContributionID; } if ($form->_paymentProcessor) { // the is_monetary concept probably should be deprecated as it can be calculated from // the existence of 'amount' & seems fragile. if ($form->_values['is_monetary'] && $form->_amount > 0.0 && !$form->_params['is_pay_later']) { // call postProcess hook before leaving $form->postProcessHook(); } $payment = Civi\Payment\System::singleton()->getByProcessor($form->_paymentProcessor); // The contribution_other_id is effectively the ID for the only contribution or the non-membership contribution. // Since we have called the membership contribution (in a 2 contribution scenario) this is out // primary-contribution compared to that - but let's face it - it's all just too hard & confusing at the moment! $paymentParams = array_merge($form->_params, array('contributionID' => $form->_values['contribution_other_id'])); $paymentActionResult = $payment->doPayment($paymentParams, 'contribute'); $paymentResults[] = array('contribution_id' => $paymentResult['contribution']->id, 'result' => $paymentActionResult); // Do not send an email if Recurring transaction is done via Direct Mode // Email will we sent when the IPN is received. foreach ($paymentResults as $result) { $this->completeTransaction($result['result'], $result['contribution_id']); } return; } $emailValues = $form->_values; // Finally send an email receipt for pay-later scenario (although it might sometimes be caught above!) if ($totalAmount == 0) { // This feels like a bizarre hack as the variable name doesn't seem to be directly connected to it's use in the template. $emailValues['useForMember'] = 0; $emailValues['membership_assign'] = 1; $emailValues['amount'] = 0; } CRM_Contribute_BAO_ContributionPage::sendMail($contactID, $emailValues, $isTest, FALSE, $includeFieldTypes); }
/** * Process the Memberships. * * @param array $membershipParams * Array of membership fields. * @param int $contactID * Contact id. * @param CRM_Contribute_Form_Contribution_Confirm $form * Confirmation form object. * * @param array $premiumParams * @param null $customFieldsFormatted * @param null $includeFieldTypes * * @param array $membershipDetails * * @param array $membershipTypeIDs * * @param bool $isPaidMembership * @param array $membershipID * * @param bool $isProcessSeparateMembershipTransaction * * @param int $financialTypeID * @param array $membershipLineItems * Line items specific to membership payment that is separate to contribution. * @param bool $isPayLater * @param bool $isPending * * @throws \CRM_Core_Exception */ protected function postProcessMembership($membershipParams, $contactID, &$form, $premiumParams, $customFieldsFormatted = NULL, $includeFieldTypes = NULL, $membershipDetails, $membershipTypeIDs, $isPaidMembership, $membershipID, $isProcessSeparateMembershipTransaction, $financialTypeID, $membershipLineItems, $isPayLater, $isPending) { $membershipContribution = NULL; $isTest = CRM_Utils_Array::value('is_test', $membershipParams, FALSE); $errors = $createdMemberships = $paymentResult = array(); if ($isPaidMembership) { if ($isProcessSeparateMembershipTransaction) { // If we have 2 transactions only one can use the invoice id. $membershipParams['invoiceID'] .= '-2'; } $paymentResult = CRM_Contribute_BAO_Contribution_Utils::processConfirm($form, $membershipParams, $contactID, $financialTypeID, 'membership', array(), $isTest, $isPayLater); if (!empty($paymentResult['contribution'])) { $this->postProcessPremium($premiumParams, $paymentResult['contribution']); //note that this will be over-written if we are using a separate membership transaction. Otherwise there is only one $membershipContribution = $paymentResult['contribution']; // Save the contribution ID so that I can be used in email receipts // For example, if you need to generate a tax receipt for the donation only. $form->_values['contribution_other_id'] = $membershipContribution->id; } } if ($isProcessSeparateMembershipTransaction) { try { $form->_lineItem = $membershipLineItems; if (empty($form->_params['auto_renew']) && !empty($membershipParams['is_recur'])) { unset($membershipParams['is_recur']); } $membershipContribution = $this->processSecondaryFinancialTransaction($contactID, $form, $membershipParams, $isTest, $membershipLineItems, CRM_Utils_Array::value('minimum_fee', $membershipDetails, 0), CRM_Utils_Array::value('financial_type_id', $membershipDetails)); } catch (CRM_Core_Exception $e) { $errors[2] = $e->getMessage(); $membershipContribution = NULL; } } $membership = NULL; if (!empty($membershipContribution) && !is_a($membershipContribution, 'CRM_Core_Error')) { $membershipContributionID = $membershipContribution->id; } //@todo - why is this nested so deep? it seems like it could be just set on the calling function on the form layer if (isset($membershipParams['onbehalf']) && !empty($membershipParams['onbehalf']['member_campaign_id'])) { $form->_params['campaign_id'] = $membershipParams['onbehalf']['member_campaign_id']; } //@todo it should no longer be possible for it to get to this point & membership to not be an array if (is_array($membershipTypeIDs) && !empty($membershipContributionID)) { $typesTerms = CRM_Utils_Array::value('types_terms', $membershipParams, array()); foreach ($membershipTypeIDs as $memType) { $numTerms = CRM_Utils_Array::value($memType, $typesTerms, 1); if (!empty($membershipContribution)) { $pendingStatus = CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name'); $pending = $membershipContribution->contribution_status_id == $pendingStatus ? TRUE : FALSE; } else { $pending = $isPending; } $contributionRecurID = isset($form->_params['contributionRecurID']) ? $form->_params['contributionRecurID'] : NULL; $membershipSource = NULL; if (!empty($form->_params['membership_source'])) { $membershipSource = $form->_params['membership_source']; } elseif (isset($form->_values['title']) && !empty($form->_values['title'])) { $membershipSource = ts('Online Contribution:') . ' ' . $form->_values['title']; } $isPayLater = NULL; if (isset($form->_params)) { $isPayLater = CRM_Utils_Array::value('is_pay_later', $form->_params); } $campaignId = NULL; if (isset($form->_values) && is_array($form->_values) && !empty($form->_values)) { $campaignId = CRM_Utils_Array::value('campaign_id', $form->_params); if (!array_key_exists('campaign_id', $form->_params)) { $campaignId = CRM_Utils_Array::value('campaign_id', $form->_values); } } list($membership, $renewalMode, $dates) = CRM_Member_BAO_Membership::renewMembership($contactID, $memType, $isTest, date('YmdHis'), CRM_Utils_Array::value('cms_contactID', $membershipParams), $customFieldsFormatted, $numTerms, $membershipID, $pending, $contributionRecurID, $membershipSource, $isPayLater, $campaignId); $form->set('renewal_mode', $renewalMode); if (!empty($dates)) { $form->assign('mem_start_date', CRM_Utils_Date::customFormat($dates['start_date'], '%Y%m%d')); $form->assign('mem_end_date', CRM_Utils_Date::customFormat($dates['end_date'], '%Y%m%d')); } if (!empty($membershipContribution)) { // update recurring id for membership record CRM_Member_BAO_Membership::updateRecurMembership($membership, $membershipContribution); CRM_Member_BAO_Membership::linkMembershipPayment($membership, $membershipContribution); } } if ($form->_priceSetId && !empty($form->_useForMember) && !empty($form->_lineItem)) { foreach ($form->_lineItem[$form->_priceSetId] as &$priceFieldOp) { if (!empty($priceFieldOp['membership_type_id']) && isset($createdMemberships[$priceFieldOp['membership_type_id']])) { $membershipOb = $createdMemberships[$priceFieldOp['membership_type_id']]; $priceFieldOp['start_date'] = $membershipOb->start_date ? CRM_Utils_Date::customFormat($membershipOb->start_date, '%B %E%f, %Y') : '-'; $priceFieldOp['end_date'] = $membershipOb->end_date ? CRM_Utils_Date::customFormat($membershipOb->end_date, '%B %E%f, %Y') : '-'; } else { $priceFieldOp['start_date'] = $priceFieldOp['end_date'] = 'N/A'; } } $form->_values['lineItem'] = $form->_lineItem; $form->assign('lineItem', $form->_lineItem); } } if (!empty($errors)) { $message = $this->compileErrorMessage($errors); throw new CRM_Core_Exception($message); } $form->_params['createdMembershipIDs'] = array(); // CRM-7851 - Moved after processing Payment Errors //@todo - the reasoning for this being here seems a little outdated foreach ($createdMemberships as $createdMembership) { CRM_Core_BAO_CustomValueTable::postProcess($form->_params, 'civicrm_membership', $createdMembership->id, 'Membership'); $form->_params['createdMembershipIDs'][] = $createdMembership->id; } if (count($createdMemberships) == 1) { //presumably this is only relevant for exactly 1 membership $form->_params['membershipID'] = $createdMembership->id; } //CRM-15232: Check if membership is created and on the basis of it use //membership receipt template to send payment receipt if (count($createdMemberships)) { $form->_values['isMembership'] = TRUE; } if (isset($membershipContributionID)) { $form->_values['contribution_id'] = $membershipContributionID; } if ($form->_contributeMode) { if ($form->_values['is_monetary'] && $form->_amount > 0.0 && !$form->_params['is_pay_later']) { // call postProcess hook before leaving $form->postProcessHook(); } $payment = Civi\Payment\System::singleton()->getByProcessor($form->_paymentProcessor); $result = $payment->doPayment($form->_params, 'contribute'); if (CRM_Utils_Array::value('payment_status_id', $result) == 1) { // Refer to CRM-16737. Payment processors 'should' return payment_status_id // to denote the outcome of the transaction. try { civicrm_api3('contribution', 'completetransaction', array('id' => $paymentResult['contribution']->id, 'trxn_id' => $paymentResult['contribution']->trxn_id, 'is_transactional' => FALSE)); } catch (CiviCRM_API3_Exception $e) { // if for any reason it is already completed this will fail - e.g extensions hacking around core not completing transactions prior to CRM-15296 // so let's be gentle here CRM_Core_Error::debug_log_message('contribution ' . $membershipContribution->id . ' not completed with trxn_id ' . $membershipContribution->trxn_id . ' and message ' . $e->getMessage()); } } // Do not send an email if Recurring transaction is done via Direct Mode // Email will we sent when the IPN is received. return; } //finally send an email receipt CRM_Contribute_BAO_ContributionPage::sendMail($contactID, $form->_values, $isTest, FALSE, $includeFieldTypes); }