3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2018 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
29 * Test APIv3 civicrm_contribute_recur* functions
31 * @package CiviCRM_APIv3
32 * @subpackage API_Contribution
35 class api_v3_ContributionPageTest
extends CiviUnitTestCase
{
36 protected $_apiversion = 3;
37 protected $testAmount = 34567;
40 protected $contactIds = array();
41 protected $_entity = 'contribution_page';
42 protected $contribution_result = NULL;
43 protected $_priceSetParams = array();
44 protected $_membershipBlockAmount = 2;
46 * Payment processor details.
49 protected $_paymentProcessor = array();
58 protected $_ids = array();
61 public $DBResetRequired = TRUE;
63 public function setUp() {
65 $this->contactIds
[] = $this->individualCreate();
66 $this->params
= array(
67 'title' => "Test Contribution Page",
68 'financial_type_id' => 1,
70 'goal_amount' => $this->testAmount
,
72 'is_monetary' => TRUE,
73 'is_email_receipt' => TRUE,
74 'receipt_from_email' => 'yourconscience@donate.com',
75 'receipt_from_name' => 'Ego Freud',
78 $this->_priceSetParams
= array(
79 'is_quick_config' => 1,
80 'extends' => 'CiviContribute',
81 'financial_type_id' => 'Donation',
86 public function tearDown() {
87 foreach ($this->contactIds
as $id) {
88 $this->callAPISuccess('contact', 'delete', array('id' => $id));
90 $this->quickCleanUpFinancialEntities();
93 public function testCreateContributionPage() {
94 $result = $this->callAPIAndDocument($this->_entity
, 'create', $this->params
, __FUNCTION__
, __FILE__
);
95 $this->assertEquals(1, $result['count']);
96 $this->assertNotNull($result['values'][$result['id']]['id']);
97 $this->getAndCheck($this->params
, $result['id'], $this->_entity
);
100 public function testGetBasicContributionPage() {
101 $createResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
102 $this->id
= $createResult['id'];
105 'financial_type_id' => 1,
107 $getResult = $this->callAPIAndDocument($this->_entity
, 'get', $getParams, __FUNCTION__
, __FILE__
);
108 $this->assertEquals(1, $getResult['count']);
111 public function testGetContributionPageByAmount() {
112 $createResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
113 $this->id
= $createResult['id'];
115 'amount' => '' . $this->testAmount
, // 3456
117 'financial_type_id' => 1,
119 $getResult = $this->callAPISuccess($this->_entity
, 'get', $getParams);
120 $this->assertEquals(1, $getResult['count']);
123 public function testDeleteContributionPage() {
124 $createResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
125 $deleteParams = array('id' => $createResult['id']);
126 $this->callAPIAndDocument($this->_entity
, 'delete', $deleteParams, __FUNCTION__
, __FILE__
);
127 $checkDeleted = $this->callAPISuccess($this->_entity
, 'get', array());
128 $this->assertEquals(0, $checkDeleted['count']);
131 public function testGetFieldsContributionPage() {
132 $result = $this->callAPISuccess($this->_entity
, 'getfields', array('action' => 'create'));
133 $this->assertEquals(12, $result['values']['start_date']['type']);
138 * Test form submission with basic price set.
140 public function testSubmit() {
141 $this->setUpContributionPage();
142 $priceFieldID = reset($this->_ids
['price_field']);
143 $priceFieldValueID = reset($this->_ids
['price_field_value']);
144 $submitParams = array(
145 'price_' . $priceFieldID => $priceFieldValueID,
146 'id' => (int) $this->_ids
['contribution_page'],
150 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
151 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
152 //assert non-deductible amount
153 $this->assertEquals(5.00, $contribution['non_deductible_amount']);
157 * Test form submission with billing first & last name where the contact does NOT
158 * otherwise have one.
160 public function testSubmitNewBillingNameData() {
161 $this->setUpContributionPage();
162 $contact = $this->callAPISuccess('Contact', 'create', array('contact_type' => 'Individual', 'email' => 'wonderwoman@amazon.com'));
163 $priceFieldID = reset($this->_ids
['price_field']);
164 $priceFieldValueID = reset($this->_ids
['price_field_value']);
165 $submitParams = array(
166 'price_' . $priceFieldID => $priceFieldValueID,
167 'id' => (int) $this->_ids
['contribution_page'],
169 'billing_first_name' => 'Wonder',
170 'billing_last_name' => 'Woman',
171 'contactID' => $contact['id'],
172 'email' => 'wonderwoman@amazon.com',
175 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
176 $contact = $this->callAPISuccess('Contact', 'get', array(
177 'id' => $contact['id'],
185 $this->assertEquals(array(
186 'first_name' => 'Wonder',
187 'last_name' => 'Woman',
188 'display_name' => 'Wonder Woman',
189 'sort_name' => 'Woman, Wonder',
190 'id' => $contact['id'],
191 'contact_id' => $contact['id'],
192 ), $contact['values'][$contact['id']]);
197 * Test form submission with billing first & last name where the contact does
198 * otherwise have one and should not be overwritten.
200 public function testSubmitNewBillingNameDoNotOverwrite() {
201 $this->setUpContributionPage();
202 $contact = $this->callAPISuccess('Contact', 'create', array(
203 'contact_type' => 'Individual',
204 'email' => 'wonderwoman@amazon.com',
205 'first_name' => 'Super',
206 'last_name' => 'Boy',
208 $priceFieldID = reset($this->_ids
['price_field']);
209 $priceFieldValueID = reset($this->_ids
['price_field_value']);
210 $submitParams = array(
211 'price_' . $priceFieldID => $priceFieldValueID,
212 'id' => (int) $this->_ids
['contribution_page'],
214 'billing_first_name' => 'Wonder',
215 'billing_last_name' => 'Woman',
216 'contactID' => $contact['id'],
217 'email' => 'wonderwoman@amazon.com',
220 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
221 $contact = $this->callAPISuccess('Contact', 'get', array(
222 'id' => $contact['id'],
230 $this->assertEquals(array(
231 'first_name' => 'Super',
232 'last_name' => 'Boy',
233 'display_name' => 'Super Boy',
234 'sort_name' => 'Boy, Super',
235 'id' => $contact['id'],
236 'contact_id' => $contact['id'],
237 ), $contact['values'][$contact['id']]);
242 * Test process with instant payment when more than one configured for the page.
246 public function testSubmitRecurMultiProcessorInstantPayment() {
247 $this->setUpContributionPage();
248 $this->setupPaymentProcessor();
249 $paymentProcessor2ID = $this->paymentProcessorCreate(array(
250 'payment_processor_type_id' => 'Dummy',
251 'name' => 'processor 2',
252 'class_name' => 'Payment_Dummy',
255 $dummyPP = Civi\Payment\System
::singleton()->getById($paymentProcessor2ID);
256 $dummyPP->setDoDirectPaymentResult(array(
257 'payment_status_id' => 1,
258 'trxn_id' => 'create_first_success',
261 $processor = $dummyPP->getPaymentProcessor();
262 $this->callAPISuccess('ContributionPage', 'create', array(
263 'id' => $this->_ids
['contribution_page'],
264 'payment_processor' => array($paymentProcessor2ID, $this->_ids
['payment_processor']),
267 $priceFieldID = reset($this->_ids
['price_field']);
268 $priceFieldValueID = reset($this->_ids
['price_field_value']);
269 $submitParams = array(
270 'price_' . $priceFieldID => $priceFieldValueID,
271 'id' => (int) $this->_ids
['contribution_page'],
274 'frequency_interval' => 1,
275 'frequency_unit' => 'month',
276 'payment_processor_id' => $paymentProcessor2ID,
279 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
280 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
281 'contribution_page_id' => $this->_ids
['contribution_page'],
282 'contribution_status_id' => 1,
284 $this->assertEquals('create_first_success', $contribution['trxn_id']);
285 $this->assertEquals(10, $contribution['total_amount']);
286 $this->assertEquals(.85, $contribution['fee_amount']);
287 $this->assertEquals(9.15, $contribution['net_amount']);
288 $this->_checkFinancialRecords(array(
289 'id' => $contribution['id'],
290 'total_amount' => $contribution['total_amount'],
291 'payment_instrument_id' => $processor['payment_instrument_id'],
296 * Test submit with a membership block in place.
298 public function testSubmitMembershipBlockNotSeparatePayment() {
299 $this->setUpMembershipContributionPage();
300 $submitParams = array(
301 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
302 'id' => (int) $this->_ids
['contribution_page'],
304 'billing_first_name' => 'Billy',
305 'billing_middle_name' => 'Goat',
306 'billing_last_name' => 'Gruff',
307 'selectMembership' => $this->_ids
['membership_type'],
311 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
312 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
313 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
314 $this->callAPISuccessGetSingle('LineItem', array('contribution_id' => $contribution['id'], 'entity_id' => $membershipPayment['id']));
318 * Test submit with a membership block in place works with renewal.
320 public function testSubmitMembershipBlockNotSeparatePaymentProcessorInstantRenew() {
321 $this->setUpMembershipContributionPage();
322 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
323 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1));
324 $submitParams = array(
325 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
326 'id' => (int) $this->_ids
['contribution_page'],
328 'billing_first_name' => 'Billy',
329 'billing_middle_name' => 'Goat',
330 'billing_last_name' => 'Gruff',
331 'selectMembership' => $this->_ids
['membership_type'],
332 'payment_processor_id' => 1,
333 'credit_card_number' => '4111111111111111',
334 'credit_card_type' => 'Visa',
335 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
339 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
340 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
341 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
342 $this->callAPISuccessGetCount('LineItem', array(
343 'entity_table' => 'civicrm_membership',
344 'entity_id' => $membershipPayment['id'],
347 $submitParams['contact_id'] = $contribution['contact_id'];
349 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
350 $this->callAPISuccessGetCount('LineItem', array(
351 'entity_table' => 'civicrm_membership',
352 'entity_id' => $membershipPayment['id'],
354 $membership = $this->callAPISuccessGetSingle('Membership', array(
355 'id' => $membershipPayment['membership_id'],
356 'return' => array('end_date', 'join_date', 'start_date'),
358 $this->assertEquals(date('Y-m-d'), $membership['start_date']);
359 $this->assertEquals(date('Y-m-d'), $membership['join_date']);
360 $this->assertEquals(date('Y-m-d', strtotime('+ 2 year - 1 day')), $membership['end_date']);
364 * Test submit with a membership block in place.
366 public function testSubmitMembershipBlockNotSeparatePaymentWithEmail() {
367 $mut = new CiviMailUtils($this, TRUE);
368 $this->setUpMembershipContributionPage();
369 $this->addProfile('supporter_profile', $this->_ids
['contribution_page']);
371 $submitParams = array(
372 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
373 'id' => (int) $this->_ids
['contribution_page'],
375 'billing_first_name' => 'Billy',
376 'billing_middle_name' => 'Goat',
377 'billing_last_name' => 'Gruff',
378 'selectMembership' => $this->_ids
['membership_type'],
379 'email-Primary' => 'billy-goat@the-bridge.net',
380 'payment_processor_id' => $this->_paymentProcessor
['id'],
381 'credit_card_number' => '4111111111111111',
382 'credit_card_type' => 'Visa',
383 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
387 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
388 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
389 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
390 $mut->checkMailLog(array(
391 'Membership Type: General',
394 $mut->clearMessages();
398 * Test submit with a membership block in place.
400 public function testSubmitMembershipBlockNotSeparatePaymentZeroDollarsWithEmail() {
401 $mut = new CiviMailUtils($this, TRUE);
402 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 0)));
403 $this->setUpMembershipContributionPage();
404 $this->addProfile('supporter_profile', $this->_ids
['contribution_page']);
406 $submitParams = array(
407 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
408 'id' => (int) $this->_ids
['contribution_page'],
410 'billing_first_name' => 'Billy',
411 'billing_middle_name' => 'Goat',
412 'billing_last_name' => 'Gruffier',
413 'selectMembership' => $this->_ids
['membership_type'],
414 'email-Primary' => 'billy-goat@the-new-bridge.net',
415 'payment_processor_id' => $this->params
['payment_processor_id'],
418 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
419 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
420 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
421 //Assert only one mail is being sent.
422 $msgs = $mut->getAllMessages();
423 $this->assertCount(1, $msgs);
425 $mut->checkMailLog(array(
426 'Membership Type: General',
434 $mut->clearMessages();
438 * Test submit with a pay later abnd check line item in mails.
440 public function testSubmitMembershipBlockIsSeparatePaymentPayLaterWithEmail() {
441 $mut = new CiviMailUtils($this, TRUE);
442 $this->setUpMembershipContributionPage();
443 $submitParams = array(
444 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
445 'id' => (int) $this->_ids
['contribution_page'],
447 'billing_first_name' => 'Billy',
448 'billing_middle_name' => 'Goat',
449 'billing_last_name' => 'Gruff',
451 'selectMembership' => $this->_ids
['membership_type'],
452 'email-Primary' => 'billy-goat@the-bridge.net',
455 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
456 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
457 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
458 $mut->checkMailLog(array(
459 'Membership Amount -... $ 2.00',
462 $mut->clearMessages();
466 * Test submit with a membership block in place.
468 public function testSubmitMembershipBlockIsSeparatePayment() {
469 $this->setUpMembershipContributionPage(TRUE);
470 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
471 $submitParams = array(
472 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
473 'id' => (int) $this->_ids
['contribution_page'],
475 'billing_first_name' => 'Billy',
476 'billing_middle_name' => 'Goat',
477 'billing_last_name' => 'Gruff',
478 'selectMembership' => $this->_ids
['membership_type'],
481 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
482 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
483 $this->assertCount(2, $contributions['values']);
484 $lines = $this->callAPISuccess('LineItem', 'get', array('sequential' => 1));
485 $this->assertEquals(10, $lines['values'][0]['line_total']);
486 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
487 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
488 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
489 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
494 * Test submit with a membership block in place.
496 public function testSubmitMembershipBlockIsSeparatePaymentWithPayLater() {
497 $this->setUpMembershipContributionPage(TRUE);
498 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
500 $submitParams = array(
501 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
502 'id' => (int) $this->_ids
['contribution_page'],
504 'billing_first_name' => 'Billy',
505 'billing_middle_name' => 'Goat',
506 'billing_last_name' => 'Gruff',
508 'selectMembership' => $this->_ids
['membership_type'],
511 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
512 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
513 $this->assertCount(2, $contributions['values']);
514 foreach ($contributions['values'] as $val) {
515 $this->assertEquals('Pending', $val['contribution_status']);
518 //Membership should be in Pending state.
519 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
520 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
521 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
522 $pendingStatus = $this->callAPISuccessGetSingle('MembershipStatus', array(
523 'return' => array("id"),
526 $this->assertEquals($membership['status_id'], $pendingStatus['id']);
527 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
531 * Test submit with a membership block in place.
533 public function testSubmitMembershipBlockIsSeparatePaymentWithEmail() {
534 $mut = new CiviMailUtils($this, TRUE);
535 $this->setUpMembershipContributionPage(TRUE);
536 $this->addProfile('supporter_profile', $this->_ids
['contribution_page']);
538 $submitParams = array(
539 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
540 'id' => (int) $this->_ids
['contribution_page'],
542 'billing_first_name' => 'Billy',
543 'billing_middle_name' => 'Goat',
544 'billing_last_name' => 'Gruff',
545 'selectMembership' => $this->_ids
['membership_type'],
546 'email-Primary' => 'billy-goat@the-bridge.net',
547 'payment_processor_id' => $this->_paymentProcessor
['id'],
548 'credit_card_number' => '4111111111111111',
549 'credit_card_type' => 'Visa',
550 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
554 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
555 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
556 $this->assertCount(2, $contributions['values']);
557 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
558 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
559 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
560 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
561 // We should have two separate email messages, each with their own amount
562 // line and no total line.
563 $mut->checkAllMailLog(
574 $mut->clearMessages();
578 * Test submit with a membership block in place.
580 public function testSubmitMembershipBlockIsSeparatePaymentZeroDollarsPayLaterWithEmail() {
581 $mut = new CiviMailUtils($this, TRUE);
582 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 0)));
583 $this->setUpMembershipContributionPage(TRUE);
584 $this->addProfile('supporter_profile', $this->_ids
['contribution_page']);
586 $submitParams = array(
587 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
588 'id' => (int) $this->_ids
['contribution_page'],
590 'billing_first_name' => 'Billy',
591 'billing_middle_name' => 'Goat',
592 'billing_last_name' => 'Gruffalo',
593 'selectMembership' => $this->_ids
['membership_type'],
594 'payment_processor_id' => 0,
595 'email-Primary' => 'gruffalo@the-bridge.net',
598 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
599 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
600 $this->assertCount(2, $contributions['values']);
601 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
602 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
603 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
604 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
605 $mut->checkMailLog(array(
607 'General Membership: $ 0.00',
611 $mut->clearMessages();
615 * Test submit with a membership block in place.
617 public function testSubmitMembershipBlockTwoTypesIsSeparatePayment() {
618 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 6)));
619 $this->_ids
['membership_type'][] = $this->membershipTypeCreate(array('name' => 'Student', 'minimum_fee' => 50));
620 $this->setUpMembershipContributionPage(TRUE);
621 $submitParams = array(
622 'price_' . $this->_ids
['price_field'][0] => $this->_ids
['price_field_value'][1],
623 'id' => (int) $this->_ids
['contribution_page'],
625 'billing_first_name' => 'Billy',
626 'billing_middle_name' => 'Goat',
627 'billing_last_name' => 'Gruff',
628 'selectMembership' => $this->_ids
['membership_type'][1],
631 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
632 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
633 $this->assertCount(2, $contributions['values']);
634 $ids = array_keys($contributions['values']);
635 $this->assertEquals('10.00', $contributions['values'][$ids[0]]['total_amount']);
636 $this->assertEquals('50.00', $contributions['values'][$ids[1]]['total_amount']);
637 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
638 $this->assertArrayHasKey($membershipPayment['contribution_id'], $contributions['values']);
639 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
640 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
644 * Test submit with a membership block in place.
646 * We are expecting a separate payment for the membership vs the contribution.
648 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNow() {
649 $mut = new CiviMailUtils($this, TRUE);
650 $this->setUpMembershipContributionPage(TRUE);
651 $processor = Civi\Payment\System
::singleton()->getById($this->_paymentProcessor
['id']);
652 $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
653 $submitParams = array(
654 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
655 'id' => (int) $this->_ids
['contribution_page'],
657 'billing_first_name' => 'Billy',
658 'billing_middle_name' => 'Goat',
659 'billing_last_name' => 'Gruff',
660 'email-Primary' => 'henry@8th.king',
661 'selectMembership' => $this->_ids
['membership_type'],
662 'payment_processor_id' => $this->_paymentProcessor
['id'],
663 'credit_card_number' => '4111111111111111',
664 'credit_card_type' => 'Visa',
665 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
669 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
670 $contributions = $this->callAPISuccess('contribution', 'get', array(
671 'contribution_page_id' => $this->_ids
['contribution_page'],
672 'contribution_status_id' => 1,
674 $this->assertCount(2, $contributions['values']);
675 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
676 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
677 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
678 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
679 $lineItem = $this->callAPISuccessGetSingle('LineItem', array('entity_table' => 'civicrm_membership'));
680 $this->assertEquals($lineItem['entity_id'], $membership['id']);
681 $this->assertEquals($lineItem['contribution_id'], $membershipPayment['contribution_id']);
682 $this->assertEquals($lineItem['qty'], 1);
683 $this->assertEquals($lineItem['unit_price'], 2);
684 $this->assertEquals($lineItem['line_total'], 2);
685 foreach ($contributions['values'] as $contribution) {
686 $this->assertEquals(.72, $contribution['fee_amount']);
687 $this->assertEquals($contribution['total_amount'] - .72, $contribution['net_amount']);
689 // The total string is currently absent & it seems worse with - although at some point
690 // it may have been intended
691 $mut->checkAllMailLog(array('$ 2.00', 'Contribution Amount', '$ 10.00'), array('Total:'));
693 $mut->clearMessages();
697 * Test submit with a membership block in place.
699 * Ensure a separate payment for the membership vs the contribution, with
702 * @param string $thousandSeparator
703 * punctuation used to refer to thousands.
705 * @dataProvider getThousandSeparators
707 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNowChargesCorrectAmounts($thousandSeparator) {
708 $this->setCurrencySeparators($thousandSeparator);
709 $this->setUpMembershipContributionPage(TRUE);
710 $processor = Civi\Payment\System
::singleton()->getById($this->_paymentProcessor
['id']);
711 $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
712 $test_uniq = uniqid();
713 $contributionPageAmount = 10;
714 $submitParams = array(
715 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
716 'id' => (int) $this->_ids
['contribution_page'],
717 'amount' => $contributionPageAmount,
718 'billing_first_name' => 'Billy',
719 'billing_middle_name' => 'Goat',
720 'billing_last_name' => 'Gruff',
721 'email-Primary' => 'henry@8th.king',
722 'selectMembership' => $this->_ids
['membership_type'],
723 'payment_processor_id' => $this->_paymentProcessor
['id'],
724 'credit_card_number' => '4111111111111111',
725 'credit_card_type' => 'Visa',
726 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
728 'TEST_UNIQ' => $test_uniq,
732 $this->hookClass
->setHook('civicrm_alterPaymentProcessorParams', array($this, 'hook_civicrm_alterPaymentProcessorParams'));
734 $this->callAPISuccess('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
735 $contributions = $this->callAPISuccess('contribution', 'get', array(
736 'contribution_page_id' => $this->_ids
['contribution_page'],
737 'contribution_status_id' => 1,
740 $result = civicrm_api3('SystemLog', 'get', array(
742 'message' => array('LIKE' => "%{$test_uniq}%"),
744 $this->assertCount(2, $result['values'], "Expected exactly 2 log entries matching {$test_uniq}.");
746 // Examine logged entries to ensure correct values.
747 $contribution_ids = array();
748 $found_membership_amount = $found_contribution_amount = FALSE;
749 foreach ($result['values'] as $value) {
750 list($junk, $json) = explode("$test_uniq:", $value['message']);
751 $logged_contribution = json_decode($json, TRUE);
752 $contribution_ids[] = $logged_contribution['contributionID'];
753 if (!empty($logged_contribution['total_amount'])) {
754 $amount = $logged_contribution['total_amount'];
757 $amount = $logged_contribution['amount'];
760 if ($amount == $this->_membershipBlockAmount
) {
761 $found_membership_amount = TRUE;
763 if ($amount == $contributionPageAmount) {
764 $found_contribution_amount = TRUE;
768 $distinct_contribution_ids = array_unique($contribution_ids);
769 $this->assertCount(2, $distinct_contribution_ids, "Expected exactly 2 log contributions with distinct contributionIDs.");
770 $this->assertTrue($found_contribution_amount, "Expected one log contribution with amount '$contributionPageAmount' (the contribution page amount)");
771 $this->assertTrue($found_membership_amount, "Expected one log contribution with amount '$this->_membershipBlockAmount' (the membership amount)");
775 * Test that when a transaction fails the pending contribution remains.
777 * An activity should also be created. CRM-16417.
779 public function testSubmitPaymentProcessorFailure() {
780 $this->setUpContributionPage();
781 $this->setupPaymentProcessor();
782 $this->createLoggedInUser();
783 $priceFieldID = reset($this->_ids
['price_field']);
784 $priceFieldValueID = reset($this->_ids
['price_field_value']);
785 $submitParams = array(
786 'price_' . $priceFieldID => $priceFieldValueID,
787 'id' => (int) $this->_ids
['contribution_page'],
789 'payment_processor_id' => 1,
790 'credit_card_number' => '4111111111111111',
791 'credit_card_type' => 'Visa',
792 'credit_card_exp_date' => array('M' => 9, 'Y' => 2008),
796 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
797 $contribution = $this->callAPISuccessGetSingle('contribution', array(
798 'contribution_page_id' => $this->_ids
['contribution_page'],
799 'contribution_status_id' => 'Failed',
802 $this->callAPISuccessGetSingle('activity', array(
803 'source_record_id' => $contribution['id'],
804 'activity_type_id' => 'Failed Payment',
810 * Test submit recurring (yearly) membership with immediate confirmation (IATS style).
812 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
813 * processor (IATS style - denoted by returning trxn_id)
814 * - the first creates a new membership, completed contribution, in progress recurring. Check these
815 * - create another - end date should be extended
817 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPaymentYear() {
818 $this->doSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPayment(array('duration_unit' => 'year', 'recur_frequency_unit' => 'year'));
822 * Test submit recurring (monthly) membership with immediate confirmation (IATS style).
824 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
825 * processor (IATS style - denoted by returning trxn_id)
826 * - the first creates a new membership, completed contribution, in progress recurring. Check these
827 * - create another - end date should be extended
829 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPaymentMonth() {
830 $this->doSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPayment(array('duration_unit' => 'month', 'recur_frequency_unit' => 'month'));
834 * Test submit recurring (mismatched frequency unit) membership with immediate confirmation (IATS style).
836 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
837 * processor (IATS style - denoted by returning trxn_id)
838 * - the first creates a new membership, completed contribution, in progress recurring. Check these
839 * - create another - end date should be extended
841 //public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPaymentDifferentFrequency() {
842 // $this->doSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPayment(array('duration_unit' => 'year', 'recur_frequency_unit' => 'month'));
846 * Helper function for testSubmitMembershipPriceSetPaymentProcessorRecurInstantPayment*
847 * @param array $params
849 * @throws \CRM_Core_Exception
852 public function doSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPayment($params = array()) {
853 $this->params
['is_recur'] = 1;
854 $this->params
['recur_frequency_unit'] = $params['recur_frequency_unit'];
855 $membershipTypeParams['duration_unit'] = $params['duration_unit'];
856 if ($params['recur_frequency_unit'] === $params['duration_unit']) {
857 $durationUnit = $params['duration_unit'];
860 $durationUnit = NULL;
862 $this->setUpMembershipContributionPage(FALSE, FALSE, $membershipTypeParams);
863 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
864 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
865 $processor = $dummyPP->getPaymentProcessor();
867 if ($params['recur_frequency_unit'] === $params['duration_unit']) {
868 // Membership will be in "New" state because it will get confirmed as payment matches
869 $expectedMembershipStatus = 1;
872 // Membership will still be in "Pending" state as it won't get confirmed as payment doesn't match
873 $expectedMembershipStatus = 5;
876 $submitParams = array(
877 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
878 'id' => (int) $this->_ids
['contribution_page'],
880 'billing_first_name' => 'Billy',
881 'billing_middle_name' => 'Goat',
882 'billing_last_name' => 'Gruff',
883 'email' => 'billy@goat.gruff',
884 'selectMembership' => $this->_ids
['membership_type'],
885 'payment_processor_id' => 1,
886 'credit_card_number' => '4111111111111111',
887 'credit_card_type' => 'Visa',
888 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
891 'frequency_interval' => 1,
892 'frequency_unit' => $this->params
['recur_frequency_unit'],
895 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
896 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
897 'contribution_page_id' => $this->_ids
['contribution_page'],
898 'contribution_status_id' => 1,
900 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
902 $this->assertEquals('create_first_success', $contribution['trxn_id']);
903 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
904 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
905 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
906 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
907 $this->assertEquals($expectedMembershipStatus, $membership['status_id']);
908 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
910 $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id'], 'entity_id' => $membership['id']));
911 //renew it with processor setting completed - should extend membership
912 $submitParams['contact_id'] = $contribution['contact_id'];
913 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
914 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
915 $this->callAPISuccess('contribution', 'getsingle', array(
916 'id' => array('NOT IN' => array($contribution['id'])),
917 'contribution_page_id' => $this->_ids
['contribution_page'],
918 'contribution_status_id' => 1,
920 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
922 // We only have an end_date if frequency units match, otherwise membership won't be autorenewed and dates won't be calculated.
923 $renewedMembershipEndDate = new DateTime($membership['end_date']);
924 switch ($durationUnit) {
926 $renewedMembershipEndDate->add(new DateInterval('P1Y'));
930 // We have to add 1 day first in case it's the end of the month, then subtract afterwards
931 // eg. 2018-02-28 should renew to 2018-03-31, if we just added 1 month we'd get 2018-03-28
932 $renewedMembershipEndDate->add(new DateInterval('P1D'));
933 $renewedMembershipEndDate->add(new DateInterval('P1M'));
934 $renewedMembershipEndDate->sub(new DateInterval('P1D'));
937 $this->assertEquals($renewedMembershipEndDate->format('Y-m-d'), $renewedMembership['end_date']);
939 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
940 $this->assertEquals($processor['payment_instrument_id'], $recurringContribution['payment_instrument_id']);
941 $this->assertEquals(5, $recurringContribution['contribution_status_id']);
945 * Test submit recurring membership with immediate confirmation (IATS style).
947 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
948 * processor (IATS style - denoted by returning trxn_id)
949 * - the first creates a new membership, completed contribution, in progress recurring. Check these
950 * - create another - end date should be extended
952 public function testSubmitMembershipComplexNonPriceSetPaymentPaymentProcessorRecurInstantPayment() {
953 $this->params
['is_recur'] = 1;
954 $this->params
['recur_frequency_unit'] = $membershipTypeParams['duration_unit'] = 'year';
955 // Add a membership so membership & contribution are not both 1.
956 $preExistingMembershipID = $this->contactMembershipCreate(array('contact_id' => $this->contactIds
[0]));
957 $this->setUpMembershipContributionPage(FALSE, FALSE, $membershipTypeParams);
958 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
959 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
960 $processor = $dummyPP->getPaymentProcessor();
962 $submitParams = array(
963 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
964 'price_' . $this->_ids
['price_field']['cont'] => 88,
965 'id' => (int) $this->_ids
['contribution_page'],
967 'billing_first_name' => 'Billy',
968 'billing_middle_name' => 'Goat',
969 'billing_last_name' => 'Gruff',
970 'email' => 'billy@goat.gruff',
971 'selectMembership' => $this->_ids
['membership_type'],
972 'payment_processor_id' => 1,
973 'credit_card_number' => '4111111111111111',
974 'credit_card_type' => 'Visa',
975 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
978 'frequency_interval' => 1,
979 'frequency_unit' => $this->params
['recur_frequency_unit'],
982 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
983 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
984 'contribution_page_id' => $this->_ids
['contribution_page'],
985 'contribution_status_id' => 1,
987 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
989 $this->assertEquals('create_first_success', $contribution['trxn_id']);
990 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
991 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
992 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
993 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
994 $this->assertEquals(1, $membership['status_id']);
995 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
997 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $contribution['id']));
998 $this->assertEquals(2, $lines['count']);
999 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
1000 $this->assertEquals($preExistingMembershipID +
1, $lines['values'][0]['entity_id']);
1001 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
1002 $this->assertEquals($contribution['id'], $lines['values'][1]['entity_id']);
1003 $this->callAPISuccessGetSingle('MembershipPayment', array('contribution_id' => $contribution['id'], 'membership_id' => $preExistingMembershipID +
1));
1005 //renew it with processor setting completed - should extend membership
1006 $submitParams['contact_id'] = $contribution['contact_id'];
1007 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
1008 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1009 $renewContribution = $this->callAPISuccess('contribution', 'getsingle', array(
1010 'id' => array('NOT IN' => array($contribution['id'])),
1011 'contribution_page_id' => $this->_ids
['contribution_page'],
1012 'contribution_status_id' => 1,
1014 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $renewContribution['id']));
1015 $this->assertEquals(2, $lines['count']);
1016 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
1017 $this->assertEquals($preExistingMembershipID +
1, $lines['values'][0]['entity_id']);
1018 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
1019 $this->assertEquals($renewContribution['id'], $lines['values'][1]['entity_id']);
1021 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1022 $this->assertEquals(date('Y-m-d', strtotime('+ 1 ' . $this->params
['recur_frequency_unit'], strtotime($membership['end_date']))), $renewedMembership['end_date']);
1023 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
1024 $this->assertEquals($processor['payment_instrument_id'], $recurringContribution['payment_instrument_id']);
1025 $this->assertEquals(5, $recurringContribution['contribution_status_id']);
1029 * Test submit recurring membership with immediate confirmation (IATS style).
1031 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
1032 * processor (IATS style - denoted by returning trxn_id)
1033 * - the first creates a new membership, completed contribution, in progress recurring. Check these
1034 * - create another - end date should be extended
1036 public function testSubmitMembershipComplexPriceSetPaymentPaymentProcessorRecurInstantPayment() {
1037 $this->params
['is_recur'] = 1;
1038 $this->params
['recur_frequency_unit'] = $membershipTypeParams['duration_unit'] = 'year';
1039 // Add a membership so membership & contribution are not both 1.
1040 $preExistingMembershipID = $this->contactMembershipCreate(array('contact_id' => $this->contactIds
[0]));
1041 $this->createPriceSetWithPage();
1042 $this->addSecondOrganizationMembershipToPriceSet();
1043 $this->setupPaymentProcessor();
1045 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
1046 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1047 $processor = $dummyPP->getPaymentProcessor();
1049 $submitParams = array(
1050 'price_' . $this->_ids
['price_field'][0] => $this->_ids
['price_field_value']['cont'],
1051 'price_' . $this->_ids
['price_field']['org1'] => $this->_ids
['price_field_value']['org1'],
1052 'price_' . $this->_ids
['price_field']['org2'] => $this->_ids
['price_field_value']['org2'],
1053 'id' => (int) $this->_ids
['contribution_page'],
1055 'billing_first_name' => 'Billy',
1056 'billing_middle_name' => 'Goat',
1057 'billing_last_name' => 'Gruff',
1058 'email' => 'billy@goat.gruff',
1059 'selectMembership' => NULL,
1060 'payment_processor_id' => 1,
1061 'credit_card_number' => '4111111111111111',
1062 'credit_card_type' => 'Visa',
1063 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1065 'frequency_interval' => 1,
1066 'frequency_unit' => $this->params
['recur_frequency_unit'],
1069 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
1070 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1071 'contribution_page_id' => $this->_ids
['contribution_page'],
1072 'contribution_status_id' => 1,
1074 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
1076 $this->assertEquals('create_first_success', $contribution['trxn_id']);
1077 $membershipPayments = $this->callAPISuccess('membership_payment', 'get', array(
1079 'contribution_id' => $contribution['id'],
1081 $this->assertEquals(2, $membershipPayments['count']);
1082 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $contribution['id']));
1083 $this->assertEquals(3, $lines['count']);
1084 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
1085 $this->assertEquals($preExistingMembershipID +
1, $lines['values'][0]['entity_id']);
1086 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
1087 $this->assertEquals($contribution['id'], $lines['values'][1]['entity_id']);
1088 $this->assertEquals('civicrm_membership', $lines['values'][2]['entity_table']);
1089 $this->assertEquals($preExistingMembershipID +
2, $lines['values'][2]['entity_id']);
1091 $this->callAPISuccessGetSingle('MembershipPayment', array('contribution_id' => $contribution['id'], 'membership_id' => $preExistingMembershipID +
1));
1092 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $preExistingMembershipID +
1));
1094 //renew it with processor setting completed - should extend membership
1095 $submitParams['contact_id'] = $contribution['contact_id'];
1096 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
1097 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1098 $renewContribution = $this->callAPISuccess('contribution', 'getsingle', array(
1099 'id' => array('NOT IN' => array($contribution['id'])),
1100 'contribution_page_id' => $this->_ids
['contribution_page'],
1101 'contribution_status_id' => 1,
1103 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $renewContribution['id']));
1104 $this->assertEquals(3, $lines['count']);
1105 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
1106 $this->assertEquals($preExistingMembershipID +
1, $lines['values'][0]['entity_id']);
1107 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
1108 $this->assertEquals($renewContribution['id'], $lines['values'][1]['entity_id']);
1110 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $preExistingMembershipID +
1));
1111 $this->assertEquals(date('Y-m-d', strtotime('+ 1 ' . $this->params
['recur_frequency_unit'], strtotime($membership['end_date']))), $renewedMembership['end_date']);
1115 * Extend the price set with a second organisation's membership.
1117 public function addSecondOrganizationMembershipToPriceSet() {
1118 $organization2ID = $this->organizationCreate();
1119 $membershipTypes = $this->callAPISuccess('MembershipType', 'get', array());
1120 $this->_ids
['membership_type'] = array_keys($membershipTypes['values']);
1121 $this->_ids
['membership_type']['org2'] = $this->membershipTypeCreate(array('contact_id' => $organization2ID, 'name' => 'Org 2'));
1122 $priceField = $this->callAPISuccess('PriceField', 'create', array(
1123 'price_set_id' => $this->_ids
['price_set'],
1124 'html_type' => 'Radio',
1125 'name' => 'Org1 Price',
1126 'label' => 'Org1Price',
1128 $this->_ids
['price_field']['org1'] = $priceField['id'];
1130 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1131 'name' => 'org1 amount',
1132 'label' => 'org 1 Amount',
1134 'financial_type_id' => 'Member Dues',
1135 'format.only_id' => TRUE,
1136 'membership_type_id' => reset($this->_ids
['membership_type']),
1137 'price_field_id' => $priceField['id'],
1139 $this->_ids
['price_field_value']['org1'] = $priceFieldValue;
1141 $priceField = $this->callAPISuccess('PriceField', 'create', array(
1142 'price_set_id' => $this->_ids
['price_set'],
1143 'html_type' => 'Radio',
1144 'name' => 'Org2 Price',
1145 'label' => 'Org2Price',
1147 $this->_ids
['price_field']['org2'] = $priceField['id'];
1149 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1150 'name' => 'org2 amount',
1151 'label' => 'org 2 Amount',
1153 'financial_type_id' => 'Member Dues',
1154 'format.only_id' => TRUE,
1155 'membership_type_id' => $this->_ids
['membership_type']['org2'],
1156 'price_field_id' => $priceField['id'],
1158 $this->_ids
['price_field_value']['org2'] = $priceFieldValue;
1163 * Test submit recurring membership with immediate confirmation (IATS style).
1165 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
1166 * processor (IATS style - denoted by returning trxn_id)
1167 * - the first creates a new membership, completed contribution, in progress recurring. Check these
1168 * - create another - end date should be extended
1170 public function testSubmitMembershipPriceSetPaymentPaymentProcessorSeparatePaymentRecurInstantPayment() {
1172 $this->setUpMembershipContributionPage(TRUE);
1173 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
1174 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1176 $submitParams = array(
1177 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
1178 'id' => (int) $this->_ids
['contribution_page'],
1180 'billing_first_name' => 'Billy',
1181 'billing_middle_name' => 'Goat',
1182 'billing_last_name' => 'Gruff',
1183 'email' => 'billy@goat.gruff',
1184 'selectMembership' => $this->_ids
['membership_type'],
1185 'payment_processor_id' => 1,
1186 'credit_card_number' => '4111111111111111',
1187 'credit_card_type' => 'Visa',
1188 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1191 'auto_renew' => TRUE,
1192 'frequency_interval' => 1,
1193 'frequency_unit' => 'month',
1196 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
1197 $contribution = $this->callAPISuccess('contribution', 'get', array(
1198 'contribution_page_id' => $this->_ids
['contribution_page'],
1199 'contribution_status_id' => 1,
1202 $this->assertEquals(2, $contribution['count']);
1203 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
1204 $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1205 $this->assertNotEmpty($contribution['values'][$membershipPayment['contribution_id']]['contribution_recur_id']);
1206 $this->callAPISuccess('contribution_recur', 'getsingle', array());
1210 * Test submit recurring membership with delayed confirmation (Authorize.net style)
1211 * - we process 2 membership transactions against with a recurring contribution against a contribution page with a delayed
1212 * processor (Authorize.net style - denoted by NOT returning trxn_id)
1213 * - the first creates a pending membership, pending contribution, penging recurring. Check these
1214 * - complete the transaction
1215 * - create another - end date should NOT be extended
1217 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurDelayed() {
1218 $this->params
['is_recur'] = 1;
1219 $this->params
['recur_frequency_unit'] = $membershipTypeParams['duration_unit'] = 'year';
1220 $this->setUpMembershipContributionPage();
1221 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
1222 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
1223 $this->membershipTypeCreate(array('name' => 'Student'));
1225 // Add a contribution & a couple of memberships so the id will not be 1 & will differ from membership id.
1226 // This saves us from 'accidental success'.
1227 $this->contributionCreate(array('contact_id' => $this->contactIds
[0]));
1228 $this->contactMembershipCreate(array('contact_id' => $this->contactIds
[0]));
1229 $this->contactMembershipCreate(array('contact_id' => $this->contactIds
[0], 'membership_type_id' => 'Student'));
1231 $submitParams = array(
1232 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
1233 'id' => (int) $this->_ids
['contribution_page'],
1235 'billing_first_name' => 'Billy',
1236 'billing_middle_name' => 'Goat',
1237 'billing_last_name' => 'Gruff',
1238 'email' => 'billy@goat.gruff',
1239 'selectMembership' => $this->_ids
['membership_type'],
1240 'payment_processor_id' => 1,
1241 'credit_card_number' => '4111111111111111',
1242 'credit_card_type' => 'Visa',
1243 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1246 'frequency_interval' => 1,
1247 'frequency_unit' => $this->params
['recur_frequency_unit'],
1250 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
1251 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1252 'contribution_page_id' => $this->_ids
['contribution_page'],
1253 'contribution_status_id' => 2,
1256 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
1257 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
1258 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1259 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
1260 $this->assertEquals(5, $membership['status_id']);
1262 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id']));
1263 $this->assertEquals('civicrm_membership', $line['entity_table']);
1264 $this->assertEquals($membership['id'], $line['entity_id']);
1266 $this->callAPISuccess('contribution', 'completetransaction', array(
1267 'id' => $contribution['id'],
1268 'trxn_id' => 'ipn_called',
1269 'payment_processor_id' => $this->_paymentProcessor
['id'],
1271 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id']));
1272 $this->assertEquals('civicrm_membership', $line['entity_table']);
1273 $this->assertEquals($membership['id'], $line['entity_id']);
1275 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1276 //renew it with processor setting completed - should extend membership
1277 $submitParams = array_merge($submitParams, array(
1278 'contact_id' => $contribution['contact_id'],
1280 'frequency_interval' => 1,
1281 'frequency_unit' => $this->params
['recur_frequency_unit'],
1285 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
1286 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1287 $newContribution = $this->callAPISuccess('contribution', 'getsingle', array(
1289 'NOT IN' => array($contribution['id']),
1291 'contribution_page_id' => $this->_ids
['contribution_page'],
1292 'contribution_status_id' => 2,
1295 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $newContribution['id']));
1296 $this->assertEquals('civicrm_membership', $line['entity_table']);
1297 $this->assertEquals($membership['id'], $line['entity_id']);
1299 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1300 //no renewal as the date hasn't changed
1301 $this->assertEquals($membership['end_date'], $renewedMembership['end_date']);
1302 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $newContribution['contribution_recur_id']));
1303 $this->assertEquals(2, $recurringContribution['contribution_status_id']);
1307 * Test non-recur contribution with membership payment
1309 public function testSubmitMembershipIsSeparatePaymentNotRecur() {
1310 //Create recur contribution page.
1311 $this->setUpMembershipContributionPage(TRUE, TRUE);
1312 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
1313 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1315 //Sumbit payment with recur disabled.
1316 $submitParams = array(
1317 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
1318 'id' => (int) $this->_ids
['contribution_page'],
1320 'frequency_interval' => 1,
1321 'frequency_unit' => 'month',
1322 'billing_first_name' => 'Billy',
1323 'billing_middle_name' => 'Goat',
1324 'billing_last_name' => 'Gruff',
1325 'email' => 'billy@goat.gruff',
1326 'selectMembership' => $this->_ids
['membership_type'],
1327 'payment_processor_id' => 1,
1328 'credit_card_number' => '4111111111111111',
1329 'credit_card_type' => 'Visa',
1330 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1334 //Assert if recur contribution is created.
1335 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1336 $recur = $this->callAPISuccess('contribution_recur', 'get', array());
1337 $this->assertEmpty($recur['count']);
1342 * Set up membership contribution page.
1343 * @param bool $isSeparatePayment
1344 * @param bool $isRecur
1345 * @param array $membershipTypeParams Parameters to pass to membershiptype.create API
1347 public function setUpMembershipContributionPage($isSeparatePayment = FALSE, $isRecur = FALSE, $membershipTypeParams = array()) {
1348 $this->setUpMembershipBlockPriceSet($membershipTypeParams);
1349 $this->setupPaymentProcessor();
1350 $this->setUpContributionPage($isRecur);
1352 $this->callAPISuccess('membership_block', 'create', array(
1353 'entity_id' => $this->_ids
['contribution_page'],
1354 'entity_table' => 'civicrm_contribution_page',
1355 'is_required' => TRUE,
1356 'is_active' => TRUE,
1357 'is_separate_payment' => $isSeparatePayment,
1358 'membership_type_default' => $this->_ids
['membership_type'],
1363 * Set up pledge block.
1365 public function setUpPledgeBlock() {
1367 'entity_table' => 'civicrm_contribution_page',
1368 'entity_id' => $this->_ids
['contribution_page'],
1369 'pledge_frequency_unit' => 'week',
1370 'is_pledge_interval' => 0,
1371 'pledge_start_date' => json_encode(array('calendar_date' => date('Ymd', strtotime("+1 month")))),
1373 $pledgeBlock = CRM_Pledge_BAO_PledgeBlock
::create($params);
1374 $this->_ids
['pledge_block_id'] = $pledgeBlock->id
;
1378 * The default data set does not include a complete default membership price set - not quite sure why.
1380 * This function ensures it exists & populates $this->_ids with it's data
1382 public function setUpMembershipBlockPriceSet($membershipTypeParams = array()) {
1383 $this->_ids
['price_set'][] = $this->callAPISuccess('price_set', 'getvalue', array(
1384 'name' => 'default_membership_type_amount',
1387 if (empty($this->_ids
['membership_type'])) {
1388 $membershipTypeParams = array_merge(array(
1390 ), $membershipTypeParams);
1391 $this->_ids
['membership_type'] = array($this->membershipTypeCreate($membershipTypeParams));
1393 $priceField = $this->callAPISuccess('price_field', 'create', array(
1394 'price_set_id' => reset($this->_ids
['price_set']),
1395 'name' => 'membership_amount',
1396 'label' => 'Membership Amount',
1397 'html_type' => 'Radio',
1400 $this->_ids
['price_field'][] = $priceField['id'];
1402 foreach ($this->_ids
['membership_type'] as $membershipTypeID) {
1403 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1404 'name' => 'membership_amount',
1405 'label' => 'Membership Amount',
1406 'amount' => $this->_membershipBlockAmount
,
1407 'financial_type_id' => 'Donation',
1408 'format.only_id' => TRUE,
1409 'membership_type_id' => $membershipTypeID,
1410 'price_field_id' => $priceField['id'],
1412 $this->_ids
['price_field_value'][] = $priceFieldValue;
1414 if (!empty($this->_ids
['membership_type']['org2'])) {
1415 $priceField = $this->callAPISuccess('price_field', 'create', array(
1416 'price_set_id' => reset($this->_ids
['price_set']),
1417 'name' => 'membership_org2',
1418 'label' => 'Membership Org2',
1419 'html_type' => 'Checkbox',
1422 $this->_ids
['price_field']['org2'] = $priceField['id'];
1424 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1425 'name' => 'membership_org2',
1426 'label' => 'Membership org 2',
1428 'financial_type_id' => 'Member Dues',
1429 'format.only_id' => TRUE,
1430 'membership_type_id' => $this->_ids
['membership_type']['org2'],
1431 'price_field_id' => $priceField['id'],
1433 $this->_ids
['price_field_value']['org2'] = $priceFieldValue;
1435 $priceField = $this->callAPISuccess('price_field', 'create', array(
1436 'price_set_id' => reset($this->_ids
['price_set']),
1437 'name' => 'Contribution',
1438 'label' => 'Contribution',
1439 'html_type' => 'Text',
1441 'is_enter_qty' => 1,
1443 $this->_ids
['price_field']['cont'] = $priceField['id'];
1444 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1445 'name' => 'contribution',
1446 'label' => 'Give me money',
1448 'financial_type_id' => 'Donation',
1449 'format.only_id' => TRUE,
1450 'price_field_id' => $priceField['id'],
1452 $this->_ids
['price_field_value'][] = $priceFieldValue;
1456 * Add text field other amount to the price set.
1458 public function addOtherAmountFieldToMembershipPriceSet() {
1459 $this->_ids
['price_field']['other_amount'] = $this->callAPISuccess('price_field', 'create', array(
1460 'price_set_id' => reset($this->_ids
['price_set']),
1461 'name' => 'other_amount',
1462 'label' => 'Other Amount',
1463 'html_type' => 'Text',
1464 'format.only_id' => TRUE,
1467 $this->_ids
['price_field_value']['other_amount'] = $this->callAPISuccess('price_field_value', 'create', array(
1468 'financial_type_id' => 'Donation',
1469 'format.only_id' => TRUE,
1470 'label' => 'Other Amount',
1472 'price_field_id' => $this->_ids
['price_field']['other_amount'],
1477 * Help function to set up contribution page with some defaults.
1478 * @param bool $isRecur
1480 public function setUpContributionPage($isRecur = FALSE) {
1482 $this->params
['is_recur'] = 1;
1483 $this->params
['recur_frequency_unit'] = 'month';
1485 $contributionPageResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
1486 if (empty($this->_ids
['price_set'])) {
1487 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams
);
1488 $this->_ids
['price_set'][] = $priceSet['id'];
1490 $priceSetID = reset($this->_ids
['price_set']);
1491 CRM_Price_BAO_PriceSet
::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
1493 if (empty($this->_ids
['price_field'])) {
1494 $priceField = $this->callAPISuccess('price_field', 'create', array(
1495 'price_set_id' => $priceSetID,
1496 'label' => 'Goat Breed',
1497 'html_type' => 'Radio',
1499 $this->_ids
['price_field'] = array($priceField['id']);
1501 if (empty($this->_ids
['price_field_value'])) {
1502 $this->callAPISuccess('price_field_value', 'create', array(
1503 'price_set_id' => $priceSetID,
1504 'price_field_id' => $priceField['id'],
1505 'label' => 'Long Haired Goat',
1506 'financial_type_id' => 'Donation',
1508 'non_deductible_amount' => 15,
1511 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1512 'price_set_id' => $priceSetID,
1513 'price_field_id' => $priceField['id'],
1514 'label' => 'Shoe-eating Goat',
1515 'financial_type_id' => 'Donation',
1517 'non_deductible_amount' => 5,
1520 $this->_ids
['price_field_value'] = array($priceFieldValue['id']);
1522 $this->_ids
['contribution_page'] = $contributionPageResult['id'];
1525 public static function setUpBeforeClass() {
1526 // put stuff here that should happen before all tests in this unit
1529 public static function tearDownAfterClass() {
1530 $tablesToTruncate = array(
1532 'civicrm_financial_type',
1533 'civicrm_contribution',
1534 'civicrm_contribution_page',
1536 $unitTest = new CiviUnitTestCase();
1537 $unitTest->quickCleanup($tablesToTruncate);
1541 * Create a payment processor instance.
1543 protected function setupPaymentProcessor() {
1544 $this->params
['payment_processor_id'] = $this->_ids
['payment_processor'] = $this->paymentProcessorCreate(array(
1545 'payment_processor_type_id' => 'Dummy',
1546 'class_name' => 'Payment_Dummy',
1547 'billing_mode' => 1,
1549 $this->_paymentProcessor
= $this->callAPISuccess('payment_processor', 'getsingle', array('id' => $this->params
['payment_processor_id']));
1553 * Test submit recurring pledge.
1555 * - we process 1 pledge with a future start date. A recur contribution and the pledge should be created with first payment date in the future.
1557 public function testSubmitPledgePaymentPaymentProcessorRecurFuturePayment() {
1558 $this->params
['adjust_recur_start_date'] = TRUE;
1559 $this->params
['is_pay_later'] = FALSE;
1560 $this->setUpContributionPage();
1561 $this->setUpPledgeBlock();
1562 $this->setupPaymentProcessor();
1563 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
1564 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1566 $submitParams = array(
1567 'id' => (int) $this->_ids
['contribution_page'],
1569 'billing_first_name' => 'Billy',
1570 'billing_middle_name' => 'Goat',
1571 'billing_last_name' => 'Gruff',
1572 'email' => 'billy@goat.gruff',
1573 'payment_processor_id' => 1,
1574 'credit_card_number' => '4111111111111111',
1575 'credit_card_type' => 'Visa',
1576 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1578 'pledge_frequency_interval' => 1,
1579 'pledge_frequency_unit' => 'week',
1580 'pledge_installments' => 3,
1581 'is_pledge' => TRUE,
1582 'pledge_block_id' => (int) $this->_ids
['pledge_block_id'],
1585 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
1587 // Check if contribution created.
1588 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1589 'contribution_page_id' => $this->_ids
['contribution_page'],
1590 'contribution_status_id' => 'Completed', // Will be pending when actual payment processor is used (dummy processor does not support future payments).
1593 $this->assertEquals('create_first_success', $contribution['trxn_id']);
1595 // Check if pledge created.
1596 $pledge = $this->callAPISuccess('pledge', 'getsingle', array());
1597 $this->assertEquals(date('Ymd', strtotime($pledge['pledge_start_date'])), date('Ymd', strtotime("+1 month")));
1598 $this->assertEquals($pledge['pledge_amount'], 300.00);
1600 // Check if pledge payments created.
1602 'pledge_id' => $pledge['id'],
1604 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1605 $this->assertEquals($pledgePayment['count'], 3);
1606 $this->assertEquals(date('Ymd', strtotime($pledgePayment['values'][1]['scheduled_date'])), date('Ymd', strtotime("+1 month")));
1607 $this->assertEquals($pledgePayment['values'][1]['scheduled_amount'], 100.00);
1608 $this->assertEquals($pledgePayment['values'][1]['status_id'], 1); // Will be pending when actual payment processor is used (dummy processor does not support future payments).
1610 // Check contribution recur record.
1611 $recur = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
1612 $this->assertEquals(date('Ymd', strtotime($recur['start_date'])), date('Ymd', strtotime("+1 month")));
1613 $this->assertEquals($recur['amount'], 100.00);
1614 $this->assertEquals($recur['contribution_status_id'], 5); // In progress status.
1618 * Test submit pledge payment.
1620 * - test submitting a pledge payment using contribution form.
1622 public function testSubmitPledgePayment() {
1623 $this->testSubmitPledgePaymentPaymentProcessorRecurFuturePayment();
1624 $pledge = $this->callAPISuccess('pledge', 'getsingle', array());
1626 'pledge_id' => $pledge['id'],
1628 $submitParams = array(
1629 'id' => (int) $pledge['pledge_contribution_page_id'],
1630 'pledge_amount' => array(2 => 1),
1631 'billing_first_name' => 'Billy',
1632 'billing_middle_name' => 'Goat',
1633 'billing_last_name' => 'Gruff',
1634 'email' => 'billy@goat.gruff',
1635 'payment_processor_id' => 1,
1636 'credit_card_number' => '4111111111111111',
1637 'credit_card_type' => 'Visa',
1638 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1640 'pledge_id' => $pledge['id'],
1641 'cid' => $pledge['contact_id'],
1642 'contact_id' => $pledge['contact_id'],
1644 'is_pledge' => TRUE,
1645 'pledge_block_id' => $this->_ids
['pledge_block_id'],
1647 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1648 $this->assertEquals($pledgePayment['values'][2]['status_id'], 2);
1650 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
1652 // Check if contribution created.
1653 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1654 'contribution_page_id' => $pledge['pledge_contribution_page_id'],
1655 'contribution_status_id' => 'Completed',
1656 'contact_id' => $pledge['contact_id'],
1657 'contribution_recur_id' => array('IS NULL' => 1),
1660 $this->assertEquals(100.00, $contribution['total_amount']);
1661 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1662 $this->assertEquals($pledgePayment['values'][2]['status_id'], 1, "This pledge payment should have been completed");
1663 $this->assertEquals($pledgePayment['values'][2]['contribution_id'], $contribution['id']);
1667 * Test form submission with multiple option price set.
1669 * @param string $thousandSeparator
1670 * punctuation used to refer to thousands.
1672 * @dataProvider getThousandSeparators
1674 public function testSubmitContributionPageWithPriceSet($thousandSeparator) {
1675 $this->setCurrencySeparators($thousandSeparator);
1676 $this->_priceSetParams
['is_quick_config'] = 0;
1677 $this->setUpContributionPage();
1678 $submitParams = array(
1679 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
1680 'id' => (int) $this->_ids
['contribution_page'],
1682 'first_name' => 'Billy',
1683 'last_name' => 'Gruff',
1684 'email' => 'billy@goat.gruff',
1685 'is_pay_later' => TRUE,
1687 $this->addPriceFields($submitParams);
1689 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1690 $contribution = $this->callAPISuccessGetSingle('contribution', array(
1691 'contribution_page_id' => $this->_ids
['contribution_page'],
1692 'contribution_status_id' => 2,
1694 $this->assertEquals(80, $contribution['total_amount']);
1695 $lineItems = $this->callAPISuccess('LineItem', 'get', array(
1696 'contribution_id' => $contribution['id'],
1698 $this->assertEquals(3, $lineItems['count']);
1699 $totalLineAmount = 0;
1700 foreach ($lineItems['values'] as $lineItem) {
1701 $totalLineAmount = $totalLineAmount +
$lineItem['line_total'];
1703 $this->assertEquals(80, $totalLineAmount);
1707 * Function to add additional price fields to priceset.
1708 * @param array $params
1710 public function addPriceFields(&$params) {
1711 $priceSetID = reset($this->_ids
['price_set']);
1712 $priceField = $this->callAPISuccess('price_field', 'create', array(
1713 'price_set_id' => $priceSetID,
1714 'label' => 'Chicken Breed',
1715 'html_type' => 'CheckBox',
1717 $priceFieldValue1 = $this->callAPISuccess('price_field_value', 'create', array(
1718 'price_set_id' => $priceSetID,
1719 'price_field_id' => $priceField['id'],
1720 'label' => 'Shoe-eating chicken -1',
1721 'financial_type_id' => 'Donation',
1724 $priceFieldValue2 = $this->callAPISuccess('price_field_value', 'create', array(
1725 'price_set_id' => $priceSetID,
1726 'price_field_id' => $priceField['id'],
1727 'label' => 'Shoe-eating chicken -2',
1728 'financial_type_id' => 'Donation',
1731 $params['price_' . $priceField['id']] = array(
1732 $priceFieldValue1['id'] => 1,
1733 $priceFieldValue2['id'] => 1,
1738 * Test Tax Amount is calculated properly when using PriceSet with Field Type = Text/Numeric Quantity
1740 * @param string $thousandSeparator
1741 * punctuation used to refer to thousands.
1743 * @dataProvider getThousandSeparators
1745 public function testSubmitContributionPageWithPriceSetQuantity($thousandSeparator) {
1746 $this->setCurrencySeparators($thousandSeparator);
1747 $this->_priceSetParams
['is_quick_config'] = 0;
1748 $this->enableTaxAndInvoicing();
1749 $financialType = $this->createFinancialType();
1750 $financialTypeId = $financialType['id'];
1751 // This function sets the Tax Rate at 10% - it currently has no way to pass Tax Rate into it - so let's work with 10%
1752 $this->relationForFinancialTypeWithFinancialAccount($financialType['id'], 5);
1754 $this->setUpContributionPage();
1755 $submitParams = array(
1756 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
1757 'id' => (int) $this->_ids
['contribution_page'],
1758 'first_name' => 'J',
1760 'email' => 'JT@ohcanada.ca',
1761 'is_pay_later' => TRUE,
1762 'receive_date' => date('Y-m-d H:i:s'),
1765 // Create PriceSet/PriceField
1766 $priceSetID = reset($this->_ids
['price_set']);
1767 $priceField = $this->callAPISuccess('price_field', 'create', array(
1768 'price_set_id' => $priceSetID,
1769 'label' => 'Printing Rights',
1770 'html_type' => 'Text',
1772 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1773 'price_set_id' => $priceSetID,
1774 'price_field_id' => $priceField['id'],
1775 'label' => 'Printing Rights',
1776 'financial_type_id' => $financialTypeId,
1777 'amount' => '16.95',
1779 $priceFieldId = $priceField['id'];
1781 // Set quantity for our test
1782 $submitParams['price_' . $priceFieldId] = 180;
1784 // contribution_page submit requires amount and tax_amount - and that's ok we're not testing that - we're testing at the LineItem level
1785 $submitParams['amount'] = $this->formatMoneyInput(180 * 16.95);
1786 // This is the correct Tax Amount - use it later to compare to what the CiviCRM Core came up with at the LineItem level
1787 $submitParams['tax_amount'] = $this->formatMoneyInput(180 * 16.95 * 0.10);
1789 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1790 $contribution = $this->callAPISuccessGetSingle('contribution', array(
1791 'contribution_page_id' => $this->_ids
['contribution_page'],
1794 // Retrieve the lineItem that belongs to the Printing Rights and check the tax_amount CiviCRM Core calculated for it
1795 $lineItem = $this->callAPISuccessGetSingle('LineItem', array(
1796 'contribution_id' => $contribution['id'],
1797 'label' => 'Printing Rights',
1800 $lineItem_TaxAmount = round($lineItem['tax_amount'], 2);
1802 $this->assertEquals($lineItem['line_total'], $contribution['total_amount'], 'Contribution total should match line total');
1803 $this->assertEquals($lineItem_TaxAmount, round(180 * 16.95 * 0.10, 2), 'Wrong Sales Tax Amount is calculated and stored.');
1806 public function hook_civicrm_alterPaymentProcessorParams($paymentObj, &$rawParams, &$cookedParams) {
1807 // Ensure total_amount are the same if they're both given.
1808 $total_amount = CRM_Utils_Array
::value('total_amount', $rawParams);
1809 $amount = CRM_Utils_Array
::value('amount', $rawParams);
1810 if (!empty($total_amount) && !empty($amount) && $total_amount != $amount) {
1811 throw new Exception("total_amount '$total_amount' and amount '$amount' differ.");
1814 // Log parameters for later debugging and testing.
1815 $message = __FUNCTION__
. ": {$rawParams['TEST_UNIQ']}:";
1816 $log_params = array_intersect_key($rawParams, array(
1818 'total_amount' => 1,
1819 'contributionID' => 1,
1821 $message .= json_encode($log_params);
1822 $log = new CRM_Utils_SystemLogger();
1823 $log->debug($message, $_REQUEST);