Merge pull request #15099 from christianwach/lab-1200
[civicrm-core.git] / CRM / Event / Cart / Form / Checkout / Payment.php
1 <?php
2
3 /**
4 * Class CRM_Event_Cart_Form_Checkout_Payment
5 */
6 class CRM_Event_Cart_Form_Checkout_Payment extends CRM_Event_Cart_Form_Cart {
7 public $all_participants;
8 public $financial_type_id;
9 public $description;
10 public $line_items;
11 public $_fields = [];
12 public $_paymentProcessor;
13 public $total;
14 public $sub_total;
15 public $payment_required = TRUE;
16 public $payer_contact_id;
17 public $is_pay_later = FALSE;
18 public $pay_later_receipt;
19
20 /**
21 * Register a participant.
22 *
23 * @param array $params
24 * @param CRM_Event_BAO_Participant $participant
25 * @param CRM_Event_BAO_Event $event
26 *
27 * @return mixed
28 */
29 public function registerParticipant($params, &$participant, $event) {
30 $transaction = new CRM_Core_Transaction();
31
32 // handle register date CRM-4320
33 $registerDate = date('YmdHis');
34 $participantParams = [
35 'id' => $participant->id,
36 'event_id' => $event->id,
37 'register_date' => $registerDate,
38 'source' => CRM_Utils_Array::value('participant_source', $params, $this->description),
39 //'fee_level' => $participant->fee_level,
40 'is_pay_later' => $this->is_pay_later,
41 'fee_amount' => CRM_Utils_Array::value('amount', $params, 0),
42 //XXX why is this a ref to participant and not contact?:
43 //'registered_by_id' => $this->payer_contact_id,
44 'fee_currency' => CRM_Utils_Array::value('currencyID', $params),
45 ];
46
47 if ($participant->must_wait) {
48 $participant_status = 'On waitlist';
49 }
50 elseif (!empty($params['is_pay_later'])) {
51 $participant_status = 'Pending from pay later';
52 }
53 else {
54 $participant_status = 'Registered';
55 }
56 $participant_statuses = CRM_Event_PseudoConstant::participantStatus();
57 $participantParams['status_id'] = array_search($participant_status, $participant_statuses);
58 $participant_status_label = CRM_Utils_Array::value($participantParams['status_id'], CRM_Event_PseudoConstant::participantStatus(NULL, NULL, 'label'));
59 $participantParams['participant_status'] = $participant_status_label;
60
61 $this->assign('isOnWaitlist', $participant->must_wait);
62
63 if ($this->_action & CRM_Core_Action::PREVIEW || CRM_Utils_Array::value('mode', $params) == 'test') {
64 $participantParams['is_test'] = 1;
65 }
66 else {
67 $participantParams['is_test'] = 0;
68 }
69
70 if (self::is_administrator()) {
71 if (!empty($params['note'])) {
72 $note_params = [
73 'participant_id' => $participant->id,
74 'contact_id' => self::getContactID(),
75 'note' => $params['note'],
76 ];
77 CRM_Event_BAO_Participant::update_note($note_params);
78 }
79 }
80
81 $participant->copyValues($participantParams);
82 $participant->save();
83
84 if (!empty($params['contributionID'])) {
85 $payment_params = [
86 'participant_id' => $participant->id,
87 'contribution_id' => $params['contributionID'],
88 ];
89 CRM_Event_BAO_ParticipantPayment::create($payment_params);
90 }
91
92 $transaction->commit();
93
94 $event_values = [];
95 CRM_Core_DAO::storeValues($event, $event_values);
96
97 $location = [];
98 if (CRM_Utils_Array::value('is_show_location', $event_values) == 1) {
99 $locationParams = [
100 'entity_id' => $participant->event_id,
101 'entity_table' => 'civicrm_event',
102 ];
103 $location = CRM_Core_BAO_Location::getValues($locationParams, TRUE);
104 CRM_Core_BAO_Address::fixAddress($location['address'][1]);
105 }
106
107 list($pre_id, $post_id) = CRM_Event_Cart_Form_MerParticipant::get_profile_groups($participant->event_id);
108 $payer_values = [
109 'email' => '',
110 'name' => '',
111 ];
112 if ($this->payer_contact_id) {
113 $payer_contact_details = CRM_Contact_BAO_Contact::getContactDetails($this->payer_contact_id);
114 $payer_values = [
115 'email' => $payer_contact_details[1],
116 'name' => $payer_contact_details[0],
117 ];
118 }
119 $values = [
120 'params' => [$participant->id => $participantParams],
121 'event' => $event_values,
122 'location' => $location,
123 'custom_pre_id' => $pre_id,
124 'custom_post_id' => $post_id,
125 'payer' => $payer_values,
126 ];
127 CRM_Event_BAO_Event::sendMail($participant->contact_id, $values, $participant->id);
128
129 return $participant;
130 }
131
132 /**
133 * Build payment fields.
134 */
135 public function buildPaymentFields() {
136 $payment_processor_id = NULL;
137 $can_pay_later = TRUE;
138 $pay_later_text = "";
139 $this->pay_later_receipt = "";
140 foreach ($this->cart->get_main_events_in_carts() as $event_in_cart) {
141 if ($payment_processor_id == NULL && $event_in_cart->event->payment_processor != NULL) {
142 $payment_processor_id = $event_in_cart->event->payment_processor;
143 $this->financial_type_id = $event_in_cart->event->financial_type_id;
144 }
145 else {
146 if ($event_in_cart->event->payment_processor != NULL && $event_in_cart->event->payment_processor != $payment_processor_id) {
147 CRM_Core_Error::statusBounce(ts('When registering for multiple events all events must use the same payment processor. '));
148 }
149 }
150 if (!$event_in_cart->event->is_pay_later) {
151 $can_pay_later = FALSE;
152 }
153 else {
154 //XXX
155 $pay_later_text = $event_in_cart->event->pay_later_text;
156 $this->pay_later_receipt = $event_in_cart->event->pay_later_receipt;
157 }
158 }
159
160 if ($payment_processor_id == NULL) {
161 CRM_Core_Error::statusBounce(ts('A payment processor must be selected for this event registration page, or the event must be configured to give users the option to pay later (contact the site administrator for assistance).'));
162 }
163
164 $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($payment_processor_id, $this->_mode);
165 $this->assign('paymentProcessor', $this->_paymentProcessor);
166
167 CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, FALSE, FALSE);
168
169 if ($can_pay_later || self::is_administrator()) {
170 $this->addElement('checkbox', 'is_pay_later',
171 $pay_later_text
172 );
173 $this->addElement('checkbox', 'payment_completed',
174 ts('Payment Completed')
175 );
176 $this->assign('pay_later_instructions', $this->pay_later_receipt);
177 }
178
179 // Event Cart does not support multiple payment processors
180 // so we cannot call $this->preProcessPaymentOptions();
181 CRM_Financial_Form_Payment::addCreditCardJs($this->_paymentProcessor['id']);
182 }
183
184 /**
185 * Build QuickForm.
186 */
187 public function buildQuickForm() {
188
189 $this->line_items = [];
190 $this->sub_total = 0;
191 $this->_price_values = $this->getValuesForPage('ParticipantsAndPrices');
192
193 // iterate over each event in cart
194 foreach ($this->cart->get_main_events_in_carts() as $event_in_cart) {
195 $this->process_event_line_item($event_in_cart);
196 foreach ($this->cart->get_events_in_carts_by_main_event_id($event_in_cart->event_id) as $subevent) {
197 $this->process_event_line_item($subevent, 'subevent');
198 }
199 }
200
201 $this->total = $this->sub_total;
202 $this->payment_required = ($this->total > 0);
203 $this->assign('payment_required', $this->payment_required);
204 $this->assign('line_items', $this->line_items);
205 $this->assign('sub_total', $this->sub_total);
206 $this->assign('total', $this->total);
207 $buttons = [];
208 $buttons[] = [
209 'name' => ts('Go Back'),
210 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp',
211 'type' => 'back',
212 ];
213 $buttons[] = [
214 'isDefault' => TRUE,
215 'name' => ts('Complete Transaction'),
216 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
217 'type' => 'next',
218 ];
219
220 if ($this->total) {
221 $this->add('text', 'billing_contact_email', 'Billing Email', '', TRUE);
222 $this->assign('collect_billing_email', TRUE);
223 }
224 if (self::is_administrator()) {
225 $this->add('textarea', 'note', 'Note');
226 $this->add('text', 'source', 'Source', ['size' => 80]);
227 $instruments = [];
228 CRM_Core_OptionGroup::getAssoc('payment_instrument', $instruments, TRUE);
229 $options = [];
230 foreach ($instruments as $type) {
231 $options[] = $this->createElement('radio', NULL, '', $type['label'], $type['value']);
232 }
233 $this->addGroup($options, 'payment_type', ts("Alternative Payment Type"));
234 $this->add('text', 'check_number', ts('Check No.'), ['size' => 20]);
235 $this->addElement('checkbox', 'is_pending', ts('Create a pending registration'));
236
237 $this->assign('administrator', TRUE);
238 }
239 $this->addButtons($buttons);
240
241 $this->addFormRule(['CRM_Event_Cart_Form_Checkout_Payment', 'formRule'], $this);
242
243 if ($this->payment_required) {
244 $this->buildPaymentFields();
245 }
246 }
247
248 /**
249 * Process line item for event.
250 *
251 * @param bool $event_in_cart
252 * @param string $class
253 */
254 public function process_event_line_item(&$event_in_cart, $class = NULL) {
255 $cost = 0;
256 $price_set_id = CRM_Price_BAO_PriceSet::getFor("civicrm_event", $event_in_cart->event_id);
257 $amount_level = NULL;
258 if ($price_set_id) {
259 $event_price_values = [];
260 foreach ($this->_price_values as $key => $value) {
261 if (preg_match("/event_{$event_in_cart->event_id}_(price.*)/", $key, $matches)) {
262 $event_price_values[$matches[1]] = $value;
263 }
264 }
265 $price_sets = CRM_Price_BAO_PriceSet::getSetDetail($price_set_id, TRUE);
266 $price_set = $price_sets[$price_set_id];
267 $price_set_amount = [];
268 CRM_Price_BAO_PriceSet::processAmount($price_set['fields'], $event_price_values, $price_set_amount);
269 $discountCode = $this->_price_values['discountcode'];
270 if (!empty($discountCode)) {
271 $ret = $this->apply_discount($discountCode, $price_set_amount, $cost, $event_in_cart->event_id);
272 if ($ret == FALSE) {
273 $cost = $event_price_values['amount'];
274 }
275 }
276 else {
277 $cost = $event_price_values['amount'];
278 }
279 // @todo - stop setting amount level in this function & call the CRM_Price_BAO_PriceSet::getAmountLevel
280 // function to get correct amount level consistently. Remove setting of the amount level in
281 // CRM_Price_BAO_PriceSet::processAmount. Extend the unit tests in CRM_Price_BAO_PriceSetTest
282 // to cover all variants.
283 $amount_level = $event_price_values['amount_level'];
284 $price_details[$price_set_id] = $price_set_amount;
285 }
286
287 // iterate over each participant in event
288 foreach ($event_in_cart->participants as & $participant) {
289 $participant->cost = $cost;
290 $participant->fee_level = $amount_level;
291 $participant->price_details = $price_details;
292 }
293
294 $this->add_line_item($event_in_cart, $class);
295 }
296
297 /**
298 * Add line item.
299 *
300 * @param CRM_Event_BAO_Event $event_in_cart
301 * @param string $class
302 */
303 public function add_line_item($event_in_cart, $class = NULL) {
304 $amount = 0;
305 $cost = 0;
306 $not_waiting_participants = [];
307 foreach ($event_in_cart->not_waiting_participants() as $participant) {
308 $amount += $participant->cost;
309 $cost = max($cost, $participant->cost);
310 $not_waiting_participants[] = [
311 'display_name' => CRM_Contact_BAO_Contact::displayName($participant->contact_id),
312 ];
313 }
314 $waiting_participants = [];
315 foreach ($event_in_cart->waiting_participants() as $participant) {
316 $waiting_participants[] = [
317 'display_name' => CRM_Contact_BAO_Contact::displayName($participant->contact_id),
318 ];
319 }
320 $this->line_items[] = [
321 'amount' => $amount,
322 'cost' => $cost,
323 'event' => $event_in_cart->event,
324 'participants' => $not_waiting_participants,
325 'num_participants' => count($not_waiting_participants),
326 'num_waiting_participants' => count($waiting_participants),
327 'waiting_participants' => $waiting_participants,
328 'class' => $class,
329 ];
330
331 $this->sub_total += $amount;
332 }
333
334 /**
335 * Send email receipt.
336 *
337 * @param array $events_in_cart
338 * @param array $params
339 */
340 public function emailReceipt($events_in_cart, $params) {
341 $contact_details = CRM_Contact_BAO_Contact::getContactDetails($this->payer_contact_id);
342 $state_province = new CRM_Core_DAO_StateProvince();
343 $state_province->id = $params["billing_state_province_id-{$this->_bltID}"];
344 $state_province->find();
345 $state_province->fetch();
346 $country = new CRM_Core_DAO_Country();
347 $country->id = $params["billing_country_id-{$this->_bltID}"];
348 $country->find();
349 $country->fetch();
350 foreach ($this->line_items as & $line_item) {
351 $location_params = ['entity_id' => $line_item['event']->id, 'entity_table' => 'civicrm_event'];
352 $line_item['location'] = CRM_Core_BAO_Location::getValues($location_params, TRUE);
353 CRM_Core_BAO_Address::fixAddress($line_item['location']['address'][1]);
354 }
355 $send_template_params = [
356 'table' => 'civicrm_msg_template',
357 'contactId' => $this->payer_contact_id,
358 'from' => current(CRM_Core_BAO_Domain::getNameAndEmail(TRUE, TRUE)),
359 'groupName' => 'msg_tpl_workflow_event',
360 'isTest' => FALSE,
361 'toEmail' => $contact_details[1],
362 'toName' => $contact_details[0],
363 'tplParams' => [
364 'billing_name' => "{$params['billing_first_name']} {$params['billing_last_name']}",
365 'billing_city' => $params["billing_city-{$this->_bltID}"],
366 'billing_country' => $country->name,
367 'billing_postal_code' => $params["billing_postal_code-{$this->_bltID}"],
368 'billing_state' => $state_province->abbreviation,
369 'billing_street_address' => $params["billing_street_address-{$this->_bltID}"],
370 'credit_card_exp_date' => $params['credit_card_exp_date'],
371 'credit_card_type' => $params['credit_card_type'],
372 'credit_card_number' => "************" . substr($params['credit_card_number'], -4, 4),
373 // XXX cart->get_discounts
374 'discounts' => $this->discounts,
375 'email' => $contact_details[1],
376 'events_in_cart' => $events_in_cart,
377 'line_items' => $this->line_items,
378 'name' => $contact_details[0],
379 'transaction_id' => $params['trxn_id'],
380 'transaction_date' => $params['trxn_date'],
381 'is_pay_later' => $this->is_pay_later,
382 'pay_later_receipt' => $this->pay_later_receipt,
383 ],
384 'valueName' => 'event_registration_receipt',
385 'PDFFilename' => ts('confirmation') . '.pdf',
386 ];
387 $template_params_to_copy = [
388 'billing_name',
389 'billing_city',
390 'billing_country',
391 'billing_postal_code',
392 'billing_state',
393 'billing_street_address',
394 'credit_card_exp_date',
395 'credit_card_type',
396 'credit_card_number',
397 ];
398 foreach ($template_params_to_copy as $template_param_to_copy) {
399 $this->set($template_param_to_copy, $send_template_params['tplParams'][$template_param_to_copy]);
400 }
401
402 CRM_Core_BAO_MessageTemplate::sendTemplate($send_template_params);
403 }
404
405 /**
406 * Apply form rules.
407 *
408 * @param array $fields
409 * @param array $files
410 * @param CRM_Core_Form $self
411 *
412 * @return array|bool
413 */
414 public static function formRule($fields, $files, $self) {
415 $errors = [];
416
417 if ($self->payment_required && empty($self->_submitValues['is_pay_later'])) {
418 CRM_Core_Form::validateMandatoryFields($self->_fields, $fields, $errors);
419
420 // validate payment instrument values (e.g. credit card number)
421 CRM_Core_Payment_Form::validatePaymentInstrument($self->_paymentProcessor['id'], $fields, $errors, NULL);
422 }
423
424 return empty($errors) ? TRUE : $errors;
425 }
426
427 /**
428 * Validate form.
429 *
430 * @todo this should surely go! Test & remove.
431 * @return bool
432 */
433 public function validate() {
434 if ($this->is_pay_later) {
435 $this->_fields['credit_card_number']['is_required'] = FALSE;
436 $this->_fields['cvv2']['is_required'] = FALSE;
437 $this->_fields['credit_card_exp_date']['is_required'] = FALSE;
438 $this->_fields['credit_card_type']['is_required'] = FALSE;
439 }
440 return parent::validate();
441 }
442
443 /**
444 * Pre-process form.
445 */
446 public function preProcess() {
447 $params = $this->_submitValues;
448 $this->is_pay_later = CRM_Utils_Array::value('is_pay_later', $params, FALSE) && !CRM_Utils_Array::value('payment_completed', $params);
449
450 parent::preProcess();
451 }
452
453 /**
454 * Post process form.
455 */
456 public function postProcess() {
457
458 $transaction = new CRM_Core_Transaction();
459 $trxnDetails = NULL;
460 $params = $this->_submitValues;
461
462 $main_participants = $this->cart->get_main_event_participants();
463 foreach ($main_participants as $participant) {
464 $defaults = [];
465 $ids = ['contact_id' => $participant->contact_id];
466 $contact = CRM_Contact_BAO_Contact::retrieve($ids, $defaults);
467 $contact->is_deleted = 0;
468 $contact->save();
469 }
470
471 $trxn_prefix = 'VR';
472 if (array_key_exists('billing_contact_email', $params)) {
473 $this->payer_contact_id = self::find_or_create_contact($this->getContactID(), [
474 'email' => $params['billing_contact_email'],
475 'first_name' => $params['billing_first_name'],
476 'last_name' => $params['billing_last_name'],
477 'is_deleted' => FALSE,
478 ]);
479
480 $ctype = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
481 $this->payer_contact_id,
482 'contact_type'
483 );
484 $billing_fields = [
485 "billing_first_name" => 1,
486 "billing_middle_name" => 1,
487 "billing_last_name" => 1,
488 "billing_street_address-{$this->_bltID}" => 1,
489 "billing_city-{$this->_bltID}" => 1,
490 "billing_state_province_id-{$this->_bltID}" => 1,
491 "billing_postal_code-{$this->_bltID}" => 1,
492 "billing_country_id-{$this->_bltID}" => 1,
493 "address_name-{$this->_bltID}" => 1,
494 "email-{$this->_bltID}" => 1,
495 ];
496
497 $params["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $params) . ' ' . CRM_Utils_Array::value('billing_middle_name', $params) . ' ' . CRM_Utils_Array::value('billing_last_name', $params);
498
499 $params["email-{$this->_bltID}"] = $params['billing_contact_email'];
500 CRM_Contact_BAO_Contact::createProfileContact(
501 $params,
502 $billing_fields,
503 $this->payer_contact_id,
504 NULL,
505 NULL,
506 $ctype,
507 TRUE
508 );
509
510 $params['contact_id'] = $this->payer_contact_id;
511 }
512
513 $params['now'] = date('YmdHis');
514 $params['invoiceID'] = md5(uniqid(rand(), TRUE));
515 $params['amount'] = $this->total;
516 $params['financial_type_id'] = $this->financial_type_id;
517 if ($this->payment_required && empty($params['is_pay_later'])) {
518 $trxnDetails = $this->make_payment($params);
519 $params['trxn_id'] = $trxnDetails['trxn_id'];
520 $params['trxn_date'] = $trxnDetails['trxn_date'];
521 $params['currencyID'] = $trxnDetails['currency'];
522 }
523 $this->cart->completed = TRUE;
524 $this->cart->save();
525 $this->set('last_event_cart_id', $this->cart->id);
526
527 $contribution_statuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
528 $params['payment_instrument_id'] = NULL;
529 if (!empty($params['is_pay_later'])) {
530 $params['payment_instrument_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Check');
531 $trxn_prefix = 'CK';
532 }
533 else {
534 $params['payment_instrument_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'payment_instrument_id', 'Credit Card');
535 }
536 if ($this->is_pay_later && empty($params['payment_completed'])) {
537 $params['contribution_status_id'] = array_search('Pending', $contribution_statuses);
538 }
539 else {
540 $params['contribution_status_id'] = array_search('Completed', $contribution_statuses);
541 $params['participant_status'] = 'Registered';
542 $params['is_pay_later'] = 0;
543 }
544 if ($trxnDetails == NULL) {
545 $params['trxn_id'] = $trxn_prefix . strftime("%Y%m%d%H%M%S");
546 $params['trxn_date'] = $params['now'];
547 }
548
549 if ($this->payment_required) {
550 $this->emailReceipt($this->cart->events_in_carts, $params);
551 }
552
553 // n.b. we need to process the subparticipants before main event
554 // participants so that session attendance can be included in the email
555 $main_participants = $this->cart->get_main_event_participants();
556 $this->all_participants = [];
557 foreach ($main_participants as $main_participant) {
558 $this->all_participants = array_merge($this->all_participants, $this->cart->get_subparticipants($main_participant));
559 }
560 $this->all_participants = array_merge($this->all_participants, $main_participants);
561
562 $this->sub_trxn_index = 0;
563 foreach ($this->all_participants as $mer_participant) {
564 $event_in_cart = $this->cart->get_event_in_cart_by_event_id($mer_participant->event_id);
565
566 $this->sub_trxn_index += 1;
567
568 unset($params['contributionID']);
569 if ($mer_participant->must_wait) {
570 $this->registerParticipant($params, $mer_participant, $event_in_cart->event);
571 }
572 else {
573 $params['amount'] = $mer_participant->cost - $mer_participant->discount_amount;
574
575 if ($event_in_cart->event->financial_type_id && $mer_participant->cost) {
576 $params['financial_type_id'] = $event_in_cart->event->financial_type_id;
577 $params['participant_contact_id'] = $mer_participant->contact_id;
578 $contribution = $this->record_contribution($mer_participant, $params, $event_in_cart->event);
579 // Record civicrm_line_item
580 CRM_Price_BAO_LineItem::processPriceSet($mer_participant->id, $mer_participant->price_details, $contribution, $entity_table = 'civicrm_participant');
581 }
582 $this->registerParticipant($params, $mer_participant, $event_in_cart->event);
583 }
584 }
585 $this->trxn_id = $params['trxn_id'];
586 $this->trxn_date = $params['trxn_date'];
587 $this->saveDataToSession();
588 $transaction->commit();
589 }
590
591 /**
592 * Make payment.
593 *
594 * @param array $params
595 *
596 * @return array
597 * @throws Exception
598 */
599 public function make_payment(&$params) {
600 $config = CRM_Core_Config::singleton();
601 if (isset($params["billing_state_province_id-{$this->_bltID}"]) && $params["billing_state_province_id-{$this->_bltID}"]) {
602 $params["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($params["billing_state_province_id-{$this->_bltID}"]);
603 }
604
605 if (isset($params["billing_country_id-{$this->_bltID}"]) && $params["billing_country_id-{$this->_bltID}"]) {
606 $params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($params["billing_country_id-{$this->_bltID}"]);
607 }
608 $params['ip_address'] = CRM_Utils_System::ipAddress();
609 $params['currencyID'] = $config->defaultCurrency;
610
611 $payment = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
612 CRM_Core_Payment_Form::mapParams($this->_bltID, $params, $params, TRUE);
613 $params['month'] = $params['credit_card_exp_date']['M'];
614 $params['year'] = $params['credit_card_exp_date']['Y'];
615 try {
616 $result = $payment->doPayment($params);
617 }
618 catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
619 CRM_Core_Error::displaySessionError($result);
620 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/event/cart_checkout', "_qf_Payment_display=1&qfKey={$this->controller->_key}", TRUE, NULL, FALSE));
621 }
622
623 $trxnDetails = [
624 'trxn_id' => $result['trxn_id'],
625 'trxn_date' => $result['now'],
626 'currency' => CRM_Utils_Array::value('currencyID', $result),
627 ];
628 return $trxnDetails;
629 }
630
631 /**
632 * Record contribution.
633 *
634 * @param CRM_Event_BAO_Participant $mer_participant
635 * @param array $params
636 * @param CRM_Event_BAO_Event $event
637 *
638 * @return object
639 * @throws Exception
640 */
641 public function record_contribution(&$mer_participant, &$params, $event) {
642 if (self::is_administrator() && !empty($params['payment_type'])) {
643 $params['payment_instrument_id'] = $params['payment_type'];
644 }
645
646 if ($this->payer_contact_id) {
647 $payer = $this->payer_contact_id;
648 }
649 elseif (self::getContactID()) {
650 $payer = self::getContactID();
651 }
652 else {
653 $payer = $params['participant_contact_id'];
654 }
655
656 $contribParams = [
657 'contact_id' => $payer,
658 'financial_type_id' => $params['financial_type_id'],
659 'receive_date' => $params['now'],
660 'total_amount' => $params['amount'],
661 'amount_level' => $mer_participant->fee_level,
662 'net_amount' => $params['amount'],
663 'invoice_id' => "{$params['invoiceID']}-{$this->sub_trxn_index}",
664 'trxn_id' => "{$params['trxn_id']}-{$this->sub_trxn_index}",
665 'currency' => CRM_Utils_Array::value('currencyID', $params),
666 'source' => $event->title,
667 'is_pay_later' => CRM_Utils_Array::value('is_pay_later', $params, 0),
668 'contribution_status_id' => $params['contribution_status_id'],
669 'payment_instrument_id' => $params['payment_instrument_id'],
670 'check_number' => CRM_Utils_Array::value('check_number', $params),
671 'skipLineItem' => 1,
672 ];
673
674 if (is_array($this->_paymentProcessor)) {
675 $contribParams['payment_processor'] = $this->_paymentProcessor['id'];
676 }
677
678 $contribution = CRM_Contribute_BAO_Contribution::add($contribParams);
679 $mer_participant->contribution_id = $contribution->id;
680 $params['contributionID'] = $contribution->id;
681
682 return $contribution;
683 }
684
685 /**
686 * Save data to session.
687 */
688 public function saveDataToSession() {
689 $session_line_items = [];
690 foreach ($this->line_items as $line_item) {
691 $session_line_item = [];
692 $session_line_item['amount'] = $line_item['amount'];
693 $session_line_item['cost'] = $line_item['cost'];
694 $session_line_item['event_id'] = $line_item['event']->id;
695 $session_line_items[] = $session_line_item;
696 }
697 $this->set('line_items', $session_line_items);
698 $this->set('payment_required', $this->payment_required);
699 $this->set('is_pay_later', $this->is_pay_later);
700 $this->set('pay_later_receipt', $this->pay_later_receipt);
701 $this->set('trxn_id', $this->trxn_id);
702 $this->set('trxn_date', $this->trxn_date);
703 $this->set('total', $this->total);
704 }
705
706 /**
707 * Set form default values.
708 *
709 * @return array
710 */
711 public function setDefaultValues() {
712
713 $defaults = parent::setDefaultValues();
714
715 $config = CRM_Core_Config::singleton();
716 $default_country = new CRM_Core_DAO_Country();
717 $default_country->iso_code = CRM_Core_BAO_Country::defaultContactCountry();
718 $default_country->find(TRUE);
719 $defaults["billing_country_id-{$this->_bltID}"] = $default_country->id;
720
721 if (self::getContactID() && !self::is_administrator()) {
722 $params = ['id' => self::getContactID()];
723 $contact = CRM_Contact_BAO_Contact::retrieve($params, $defaults);
724
725 foreach ($contact->email as $email) {
726 if ($email['is_billing']) {
727 $defaults["billing_contact_email"] = $email['email'];
728 }
729 }
730 if (empty($defaults['billing_contact_email'])) {
731 foreach ($contact->email as $email) {
732 if ($email['is_primary']) {
733 $defaults["billing_contact_email"] = $email['email'];
734 }
735 }
736 }
737
738 $defaults["billing_first_name"] = $contact->first_name;
739 $defaults["billing_middle_name"] = $contact->middle_name;
740 $defaults["billing_last_name"] = $contact->last_name;
741
742 $billing_address = CRM_Event_Cart_BAO_MerParticipant::billing_address_from_contact($contact);
743
744 if ($billing_address != NULL) {
745 $defaults["billing_street_address-{$this->_bltID}"] = $billing_address['street_address'];
746 $defaults["billing_city-{$this->_bltID}"] = $billing_address['city'];
747 $defaults["billing_postal_code-{$this->_bltID}"] = $billing_address['postal_code'];
748 $defaults["billing_state_province_id-{$this->_bltID}"] = $billing_address['state_province_id'];
749 $defaults["billing_country_id-{$this->_bltID}"] = $billing_address['country_id'];
750 }
751 }
752
753 $defaults["source"] = $this->description;
754
755 return $defaults;
756 }
757
758 /**
759 * Apply discount.
760 *
761 * @param string $discountCode
762 * @param array $price_set_amount
763 * @param float $cost
764 * @param int $event_id
765 *
766 * @return bool
767 */
768 protected function apply_discount($discountCode, &$price_set_amount, &$cost, $event_id) {
769 //need better way to determine if cividiscount installed
770 $autoDiscount = [];
771 $sql = "select is_active from civicrm_extension where name like 'CiviDiscount%'";
772 $dao = CRM_Core_DAO::executeQuery($sql, '');
773 while ($dao->fetch()) {
774 if ($dao->is_active != '1') {
775 return FALSE;
776 }
777 }
778 $discounted_priceset_ids = _cividiscount_get_discounted_priceset_ids();
779 $discounts = _cividiscount_get_discounts();
780
781 $stat = FALSE;
782 foreach ($discounts as $key => $discountValue) {
783 if ($key == $discountCode) {
784 $events = CRM_Utils_Array::value('events', $discountValue);
785 $evt_ids = implode(",", $events);
786 if ($evt_ids == "0" || strpos($evt_ids, $event_id)) {
787 $event_match = TRUE;
788 }
789 //check priceset is_active
790 if ($discountValue['active_on'] != NULL) {
791 $today = date('Y-m-d');
792 $diff1 = date_diff(date_create($today), date_create($discountValue['active_on']));
793 if ($diff1->days > 0) {
794 $active1 = TRUE;
795 }
796 }
797 else {
798 $active1 = TRUE;
799 }
800 if ($discountValue['expire_on'] != NULL) {
801 $diff2 = date_diff(date_create($today), date_create($discountValue['expire_on']));
802 if ($diff2->days > 0) {
803 $active2 = TRUE;
804 }
805 }
806 else {
807 $active2 = TRUE;
808 }
809 }
810 if ($discountValue['is_active'] == TRUE && ($discountValue['count_max'] == 0 || ($discountValue['count_max'] > $discountValue['count_use'])) && $active1 == TRUE && $active2 == TRUE && $event_match == TRUE) {
811 foreach ($price_set_amount as $key => $price) {
812 if (array_search($price['price_field_value_id'], $discounted_priceset_ids) != NULL) {
813 $discounted = _cividiscount_calc_discount($price['line_total'], $price['label'], $discountValue, $autoDiscount, "USD");
814 $price_set_amount[$key]['line_total'] = $discounted[0];
815 $cost += $discounted[0];
816 $price_set_amount[$key]['label'] = $discounted[1];
817 }
818 else {
819 $cost += $price['line_total'];
820 }
821 }
822 $stat = TRUE;
823 }
824 }
825 return $stat;
826 }
827
828 }