3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2016 |
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();
45 * Payment processor details.
48 protected $_paymentProcessor = array();
57 protected $_ids = array();
60 public $DBResetRequired = TRUE;
62 public function setUp() {
64 $this->contactIds
[] = $this->individualCreate();
65 $this->params
= array(
66 'title' => "Test Contribution Page",
67 'financial_type_id' => 1,
69 'goal_amount' => $this->testAmount
,
71 'is_monetary' => TRUE,
72 'is_email_receipt' => TRUE,
73 'receipt_from_email' => 'yourconscience@donate.com',
74 'receipt_from_name' => 'Ego Freud',
77 $this->_priceSetParams
= array(
78 'is_quick_config' => 1,
79 'extends' => 'CiviContribute',
80 'financial_type_id' => 'Donation',
85 public function tearDown() {
86 foreach ($this->contactIds
as $id) {
87 $this->callAPISuccess('contact', 'delete', array('id' => $id));
89 $this->quickCleanUpFinancialEntities();
92 public function testCreateContributionPage() {
93 $result = $this->callAPIAndDocument($this->_entity
, 'create', $this->params
, __FUNCTION__
, __FILE__
);
94 $this->assertEquals(1, $result['count']);
95 $this->assertNotNull($result['values'][$result['id']]['id']);
96 $this->getAndCheck($this->params
, $result['id'], $this->_entity
);
99 public function testGetBasicContributionPage() {
100 $createResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
101 $this->id
= $createResult['id'];
104 'financial_type_id' => 1,
106 $getResult = $this->callAPIAndDocument($this->_entity
, 'get', $getParams, __FUNCTION__
, __FILE__
);
107 $this->assertEquals(1, $getResult['count']);
110 public function testGetContributionPageByAmount() {
111 $createResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
112 $this->id
= $createResult['id'];
114 'amount' => '' . $this->testAmount
, // 3456
116 'financial_type_id' => 1,
118 $getResult = $this->callAPIAndDocument($this->_entity
, 'get', $getParams, __FUNCTION__
, __FILE__
);
119 $this->assertEquals(1, $getResult['count']);
122 public function testDeleteContributionPage() {
123 $createResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
124 $deleteParams = array('id' => $createResult['id']);
125 $this->callAPIAndDocument($this->_entity
, 'delete', $deleteParams, __FUNCTION__
, __FILE__
);
126 $checkDeleted = $this->callAPISuccess($this->_entity
, 'get', array());
127 $this->assertEquals(0, $checkDeleted['count']);
130 public function testGetFieldsContributionPage() {
131 $result = $this->callAPISuccess($this->_entity
, 'getfields', array('action' => 'create'));
132 $this->assertEquals(12, $result['values']['start_date']['type']);
137 * Test form submission with basic price set.
139 public function testSubmit() {
140 $this->setUpContributionPage();
141 $priceFieldID = reset($this->_ids
['price_field']);
142 $priceFieldValueID = reset($this->_ids
['price_field_value']);
143 $submitParams = array(
144 'price_' . $priceFieldID => $priceFieldValueID,
145 'id' => (int) $this->_ids
['contribution_page'],
149 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
150 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
151 //assert non-deductible amount
152 $this->assertEquals(5.00, $contribution['non_deductible_amount']);
156 * Test form submission with billing first & last name where the contact does NOT
157 * otherwise have one.
159 public function testSubmitNewBillingNameData() {
160 $this->setUpContributionPage();
161 $contact = $this->callAPISuccess('Contact', 'create', array('contact_type' => 'Individual', 'email' => 'wonderwoman@amazon.com'));
162 $priceFieldID = reset($this->_ids
['price_field']);
163 $priceFieldValueID = reset($this->_ids
['price_field_value']);
164 $submitParams = array(
165 'price_' . $priceFieldID => $priceFieldValueID,
166 'id' => (int) $this->_ids
['contribution_page'],
168 'billing_first_name' => 'Wonder',
169 'billing_last_name' => 'Woman',
170 'contactID' => $contact['id'],
171 'email' => 'wonderwoman@amazon.com',
174 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
175 $contact = $this->callAPISuccess('Contact', 'get', array(
176 'id' => $contact['id'],
184 $this->assertEquals(array(
185 'first_name' => 'Wonder',
186 'last_name' => 'Woman',
187 'display_name' => 'Wonder Woman',
188 'sort_name' => 'Woman, Wonder',
189 'id' => $contact['id'],
190 'contact_id' => $contact['id'],
191 ), $contact['values'][$contact['id']]);
196 * Test form submission with billing first & last name where the contact does
197 * otherwise have one and should not be overwritten.
199 public function testSubmitNewBillingNameDoNotOverwrite() {
200 $this->setUpContributionPage();
201 $contact = $this->callAPISuccess('Contact', 'create', array(
202 'contact_type' => 'Individual',
203 'email' => 'wonderwoman@amazon.com',
204 'first_name' => 'Super',
205 'last_name' => 'Boy',
207 $priceFieldID = reset($this->_ids
['price_field']);
208 $priceFieldValueID = reset($this->_ids
['price_field_value']);
209 $submitParams = array(
210 'price_' . $priceFieldID => $priceFieldValueID,
211 'id' => (int) $this->_ids
['contribution_page'],
213 'billing_first_name' => 'Wonder',
214 'billing_last_name' => 'Woman',
215 'contactID' => $contact['id'],
216 'email' => 'wonderwoman@amazon.com',
219 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
220 $contact = $this->callAPISuccess('Contact', 'get', array(
221 'id' => $contact['id'],
229 $this->assertEquals(array(
230 'first_name' => 'Super',
231 'last_name' => 'Boy',
232 'display_name' => 'Super Boy',
233 'sort_name' => 'Boy, Super',
234 'id' => $contact['id'],
235 'contact_id' => $contact['id'],
236 ), $contact['values'][$contact['id']]);
241 * Test process with instant payment when more than one configured for the page.
245 public function testSubmitRecurMultiProcessorInstantPayment() {
246 $this->setUpContributionPage();
247 $this->setupPaymentProcessor();
248 $paymentProcessor2ID = $this->paymentProcessorCreate(array(
249 'payment_processor_type_id' => 'Dummy',
250 'name' => 'processor 2',
251 'class_name' => 'Payment_Dummy',
254 $dummyPP = Civi\Payment\System
::singleton()->getById($paymentProcessor2ID);
255 $dummyPP->setDoDirectPaymentResult(array(
256 'payment_status_id' => 1,
257 'trxn_id' => 'create_first_success',
260 $processor = $dummyPP->getPaymentProcessor();
261 $this->callAPISuccess('ContributionPage', 'create', array(
262 'id' => $this->_ids
['contribution_page'],
263 'payment_processor' => array($paymentProcessor2ID, $this->_ids
['payment_processor']),
266 $priceFieldID = reset($this->_ids
['price_field']);
267 $priceFieldValueID = reset($this->_ids
['price_field_value']);
268 $submitParams = array(
269 'price_' . $priceFieldID => $priceFieldValueID,
270 'id' => (int) $this->_ids
['contribution_page'],
273 'frequency_interval' => 1,
274 'frequency_unit' => 'month',
275 'payment_processor_id' => $paymentProcessor2ID,
278 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
279 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
280 'contribution_page_id' => $this->_ids
['contribution_page'],
281 'contribution_status_id' => 1,
283 $this->assertEquals('create_first_success', $contribution['trxn_id']);
284 $this->assertEquals(10, $contribution['total_amount']);
285 $this->assertEquals(.85, $contribution['fee_amount']);
286 $this->assertEquals(9.15, $contribution['net_amount']);
287 $this->_checkFinancialRecords(array(
288 'id' => $contribution['id'],
289 'total_amount' => $contribution['total_amount'],
290 'payment_instrument_id' => $processor['payment_instrument_id'],
295 * Test submit with a membership block in place.
297 public function testSubmitMembershipBlockNotSeparatePayment() {
298 $this->setUpMembershipContributionPage();
299 $submitParams = array(
300 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
301 'id' => (int) $this->_ids
['contribution_page'],
303 'billing_first_name' => 'Billy',
304 'billing_middle_name' => 'Goat',
305 'billing_last_name' => 'Gruff',
306 'selectMembership' => $this->_ids
['membership_type'],
310 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
311 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
312 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
313 $this->callAPISuccessGetSingle('LineItem', array('contribution_id' => $contribution['id'], 'entity_id' => $membershipPayment['id']));
317 * Test submit with a membership block in place.
319 public function testSubmitMembershipBlockNotSeparatePaymentWithEmail() {
320 $mut = new CiviMailUtils($this, TRUE);
321 $this->setUpMembershipContributionPage();
322 $this->addProfile('supporter_profile', $this->_ids
['contribution_page']);
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 'email-Primary' => 'billy-goat@the-bridge.net',
333 'payment_processor_id' => $this->_paymentProcessor
['id'],
334 'credit_card_number' => '4111111111111111',
335 'credit_card_type' => 'Visa',
336 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
340 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
341 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
342 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
343 $mut->checkMailLog(array(
344 'Membership Type: General',
347 $mut->clearMessages();
351 * Test submit with a membership block in place.
353 public function testSubmitMembershipBlockNotSeparatePaymentZeroDollarsWithEmail() {
354 $mut = new CiviMailUtils($this, TRUE);
355 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 0)));
356 $this->setUpMembershipContributionPage();
357 $this->addProfile('supporter_profile', $this->_ids
['contribution_page']);
359 $submitParams = array(
360 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
361 'id' => (int) $this->_ids
['contribution_page'],
363 'billing_first_name' => 'Billy',
364 'billing_middle_name' => 'Goat',
365 'billing_last_name' => 'Gruffier',
366 'selectMembership' => $this->_ids
['membership_type'],
367 'email-Primary' => 'billy-goat@the-new-bridge.net',
370 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
371 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids
['contribution_page']));
372 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
373 $mut->checkMailLog(array(
374 'Membership Type: General',
382 $mut->clearMessages(999);
386 * Test submit with a membership block in place.
388 public function testSubmitMembershipBlockIsSeparatePayment() {
389 $this->setUpMembershipContributionPage(TRUE);
390 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
391 $submitParams = array(
392 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
393 'id' => (int) $this->_ids
['contribution_page'],
395 'billing_first_name' => 'Billy',
396 'billing_middle_name' => 'Goat',
397 'billing_last_name' => 'Gruff',
398 'selectMembership' => $this->_ids
['membership_type'],
401 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
402 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
403 $this->assertCount(2, $contributions['values']);
404 $this->callAPISuccessGetCount('LineItem', array(), 2);
405 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
406 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
407 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
408 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
412 * Test submit with a membership block in place.
414 public function testSubmitMembershipBlockIsSeparatePaymentWithEmail() {
415 $mut = new CiviMailUtils($this, TRUE);
416 $this->setUpMembershipContributionPage(TRUE);
417 $this->addProfile('supporter_profile', $this->_ids
['contribution_page']);
419 $submitParams = array(
420 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
421 'id' => (int) $this->_ids
['contribution_page'],
423 'billing_first_name' => 'Billy',
424 'billing_middle_name' => 'Goat',
425 'billing_last_name' => 'Gruff',
426 'selectMembership' => $this->_ids
['membership_type'],
427 'email-Primary' => 'billy-goat@the-bridge.net',
428 'payment_processor_id' => $this->_paymentProcessor
['id'],
429 'credit_card_number' => '4111111111111111',
430 'credit_card_type' => 'Visa',
431 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
435 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
436 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
437 $this->assertCount(2, $contributions['values']);
438 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
439 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
440 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
441 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
442 $mut->checkAllMailLog(array(
447 $mut->clearMessages(999);
451 * Test submit with a membership block in place.
453 public function testSubmitMembershipBlockIsSeparatePaymentZeroDollarsPayLaterWithEmail() {
454 $mut = new CiviMailUtils($this, TRUE);
455 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 0)));
456 $this->setUpMembershipContributionPage(TRUE);
457 $this->addProfile('supporter_profile', $this->_ids
['contribution_page']);
459 $submitParams = array(
460 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
461 'id' => (int) $this->_ids
['contribution_page'],
463 'billing_first_name' => 'Billy',
464 'billing_middle_name' => 'Goat',
465 'billing_last_name' => 'Gruffalo',
466 'selectMembership' => $this->_ids
['membership_type'],
467 'payment_processor_id' => 0,
468 'email-Primary' => 'gruffalo@the-bridge.net',
471 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
472 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
473 $this->assertCount(2, $contributions['values']);
474 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
475 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
476 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
477 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
478 $mut->checkMailLog(array(
480 'General Membership: $ 0.00',
484 $mut->clearMessages();
488 * Test submit with a membership block in place.
490 public function testSubmitMembershipBlockTwoTypesIsSeparatePayment() {
491 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 6)));
492 $this->_ids
['membership_type'][] = $this->membershipTypeCreate(array('name' => 'Student', 'minimum_fee' => 50));
493 $this->setUpMembershipContributionPage(TRUE);
494 $submitParams = array(
495 'price_' . $this->_ids
['price_field'][0] => $this->_ids
['price_field_value'][1],
496 'id' => (int) $this->_ids
['contribution_page'],
498 'billing_first_name' => 'Billy',
499 'billing_middle_name' => 'Goat',
500 'billing_last_name' => 'Gruff',
501 'selectMembership' => $this->_ids
['membership_type'][1],
504 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
505 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids
['contribution_page']));
506 $this->assertCount(2, $contributions['values']);
507 $ids = array_keys($contributions['values']);
508 $this->assertEquals('10.00', $contributions['values'][$ids[0]]['total_amount']);
509 $this->assertEquals('50.00', $contributions['values'][$ids[1]]['total_amount']);
510 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
511 $this->assertArrayHasKey($membershipPayment['contribution_id'], $contributions['values']);
512 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
513 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
517 * Test submit with a membership block in place.
519 * We are expecting a separate payment for the membership vs the contribution.
521 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNow() {
522 $this->setUpMembershipContributionPage(TRUE);
523 $processor = Civi\Payment\System
::singleton()->getById($this->_paymentProcessor
['id']);
524 $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
525 $submitParams = array(
526 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
527 'id' => (int) $this->_ids
['contribution_page'],
529 'billing_first_name' => 'Billy',
530 'billing_middle_name' => 'Goat',
531 'billing_last_name' => 'Gruff',
532 'selectMembership' => $this->_ids
['membership_type'],
533 'payment_processor_id' => $this->_paymentProcessor
['id'],
534 'credit_card_number' => '4111111111111111',
535 'credit_card_type' => 'Visa',
536 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
540 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
541 $contributions = $this->callAPISuccess('contribution', 'get', array(
542 'contribution_page_id' => $this->_ids
['contribution_page'],
543 'contribution_status_id' => 1,
545 $this->assertCount(2, $contributions['values']);
546 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
547 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
548 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
549 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
550 foreach ($contributions['values'] as $contribution) {
551 $this->assertEquals(.72, $contribution['fee_amount']);
552 $this->assertEquals($contribution['total_amount'] - .72, $contribution['net_amount']);
557 * Test that when a transaction fails the pending contribution remains.
559 * An activity should also be created. CRM-16417.
561 public function testSubmitPaymentProcessorFailure() {
562 $this->setUpContributionPage();
563 $this->setupPaymentProcessor();
564 $this->createLoggedInUser();
565 $priceFieldID = reset($this->_ids
['price_field']);
566 $priceFieldValueID = reset($this->_ids
['price_field_value']);
567 $submitParams = array(
568 'price_' . $priceFieldID => $priceFieldValueID,
569 'id' => (int) $this->_ids
['contribution_page'],
571 'payment_processor_id' => 1,
572 'credit_card_number' => '4111111111111111',
573 'credit_card_type' => 'Visa',
574 'credit_card_exp_date' => array('M' => 9, 'Y' => 2008),
578 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
579 $contribution = $this->callAPISuccessGetSingle('contribution', array(
580 'contribution_page_id' => $this->_ids
['contribution_page'],
581 'contribution_status_id' => 2,
584 $this->callAPISuccessGetSingle('activity', array(
585 'source_record_id' => $contribution['id'],
586 'activity_type_id' => 'Failed Payment',
592 * Test submit recurring membership with immediate confirmation (IATS style).
594 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
595 * processor (IATS style - denoted by returning trxn_id)
596 * - the first creates a new membership, completed contribution, in progress recurring. Check these
597 * - create another - end date should be extended
599 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPayment() {
600 $this->params
['is_recur'] = 1;
601 $this->params
['recur_frequency_unit'] = 'month';
602 $this->setUpMembershipContributionPage();
603 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
604 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
605 $processor = $dummyPP->getPaymentProcessor();
607 $submitParams = array(
608 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
609 'id' => (int) $this->_ids
['contribution_page'],
611 'billing_first_name' => 'Billy',
612 'billing_middle_name' => 'Goat',
613 'billing_last_name' => 'Gruff',
614 'email' => 'billy@goat.gruff',
615 'selectMembership' => $this->_ids
['membership_type'],
616 'payment_processor_id' => 1,
617 'credit_card_number' => '4111111111111111',
618 'credit_card_type' => 'Visa',
619 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
622 'frequency_interval' => 1,
623 'frequency_unit' => 'month',
626 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
627 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
628 'contribution_page_id' => $this->_ids
['contribution_page'],
629 'contribution_status_id' => 1,
631 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
633 $this->assertEquals('create_first_success', $contribution['trxn_id']);
634 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
635 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
636 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
637 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
638 $this->assertEquals(1, $membership['status_id']);
639 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
641 $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id'], 'entity_id' => $membership['id']));
642 //renew it with processor setting completed - should extend membership
643 $submitParams['contact_id'] = $contribution['contact_id'];
644 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
645 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
646 $this->callAPISuccess('contribution', 'getsingle', array(
647 'id' => array('NOT IN' => array($contribution['id'])),
648 'contribution_page_id' => $this->_ids
['contribution_page'],
649 'contribution_status_id' => 1,
651 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
652 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
653 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
654 $this->assertEquals($processor['payment_instrument_id'], $recurringContribution['payment_instrument_id']);
655 $this->assertEquals(5, $recurringContribution['contribution_status_id']);
659 * Test submit recurring membership with immediate confirmation (IATS style).
661 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
662 * processor (IATS style - denoted by returning trxn_id)
663 * - the first creates a new membership, completed contribution, in progress recurring. Check these
664 * - create another - end date should be extended
666 public function testSubmitMembershipPriceSetPaymentPaymentProcessorSeparatePaymentRecurInstantPayment() {
668 $this->setUpMembershipContributionPage(TRUE);
669 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
670 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
672 $submitParams = array(
673 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
674 'id' => (int) $this->_ids
['contribution_page'],
676 'billing_first_name' => 'Billy',
677 'billing_middle_name' => 'Goat',
678 'billing_last_name' => 'Gruff',
679 'email' => 'billy@goat.gruff',
680 'selectMembership' => $this->_ids
['membership_type'],
681 'payment_processor_id' => 1,
682 'credit_card_number' => '4111111111111111',
683 'credit_card_type' => 'Visa',
684 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
687 'auto_renew' => TRUE,
688 'frequency_interval' => 1,
689 'frequency_unit' => 'month',
692 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
693 $contribution = $this->callAPISuccess('contribution', 'get', array(
694 'contribution_page_id' => $this->_ids
['contribution_page'],
695 'contribution_status_id' => 1,
698 $this->assertEquals(2, $contribution['count']);
699 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
700 $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
701 $this->assertNotEmpty($contribution['values'][$membershipPayment['contribution_id']]['contribution_recur_id']);
702 $this->callAPISuccess('contribution_recur', 'getsingle', array());
706 * Test submit recurring membership with delayed confirmation (Authorize.net style)
707 * - we process 2 membership transactions against with a recurring contribution against a contribution page with a delayed
708 * processor (Authorize.net style - denoted by NOT returning trxn_id)
709 * - the first creates a pending membership, pending contribution, penging recurring. Check these
710 * - complete the transaction
711 * - create another - end date should NOT be extended
713 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurDelayed() {
714 $this->params
['is_recur'] = 1;
715 $this->params
['recur_frequency_unit'] = 'month';
716 $this->setUpMembershipContributionPage();
717 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
718 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
719 $this->membershipTypeCreate(array('name' => 'Student'));
721 // Add a contribution & a couple of memberships so the id will not be 1 & will differ from membership id.
722 // This saves us from 'accidental success'.
723 $this->contributionCreate(array('contact_id' => $this->contactIds
[0]));
724 $this->contactMembershipCreate(array('contact_id' => $this->contactIds
[0]));
725 $this->contactMembershipCreate(array('contact_id' => $this->contactIds
[0], 'membership_type_id' => 'Student'));
727 $submitParams = array(
728 'price_' . $this->_ids
['price_field'][0] => reset($this->_ids
['price_field_value']),
729 'id' => (int) $this->_ids
['contribution_page'],
731 'billing_first_name' => 'Billy',
732 'billing_middle_name' => 'Goat',
733 'billing_last_name' => 'Gruff',
734 'email' => 'billy@goat.gruff',
735 'selectMembership' => $this->_ids
['membership_type'],
736 'payment_processor_id' => 1,
737 'credit_card_number' => '4111111111111111',
738 'credit_card_type' => 'Visa',
739 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
742 'frequency_interval' => 1,
743 'frequency_unit' => 'month',
746 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
747 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
748 'contribution_page_id' => $this->_ids
['contribution_page'],
749 'contribution_status_id' => 2,
752 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
753 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
754 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
755 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
756 $this->assertEquals(5, $membership['status_id']);
758 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id']));
759 $this->assertEquals('civicrm_membership', $line['entity_table']);
760 $this->assertEquals($membership['id'], $line['entity_id']);
762 $this->callAPISuccess('contribution', 'completetransaction', array(
763 'id' => $contribution['id'],
764 'trxn_id' => 'ipn_called',
765 'payment_processor_id' => $this->_paymentProcessor
['id'],
767 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id']));
768 $this->assertEquals('civicrm_membership', $line['entity_table']);
769 $this->assertEquals($membership['id'], $line['entity_id']);
771 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
772 //renew it with processor setting completed - should extend membership
773 $submitParams = array_merge($submitParams, array(
774 'contact_id' => $contribution['contact_id'],
776 'frequency_interval' => 1,
777 'frequency_unit' => 'month',
781 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
782 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
783 $newContribution = $this->callAPISuccess('contribution', 'getsingle', array(
785 'NOT IN' => array($contribution['id']),
787 'contribution_page_id' => $this->_ids
['contribution_page'],
788 'contribution_status_id' => 2,
791 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $newContribution['id']));
792 $this->assertEquals('civicrm_membership', $line['entity_table']);
793 $this->assertEquals($membership['id'], $line['entity_id']);
795 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
796 //no renewal as the date hasn't changed
797 $this->assertEquals($membership['end_date'], $renewedMembership['end_date']);
798 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $newContribution['contribution_recur_id']));
799 $this->assertEquals(2, $recurringContribution['contribution_status_id']);
803 * Set up membership contribution page.
804 * @param bool $isSeparatePayment
806 public function setUpMembershipContributionPage($isSeparatePayment = FALSE) {
807 $this->setUpMembershipBlockPriceSet();
808 $this->setupPaymentProcessor();
809 $this->setUpContributionPage();
811 $this->callAPISuccess('membership_block', 'create', array(
812 'entity_id' => $this->_ids
['contribution_page'],
813 'entity_table' => 'civicrm_contribution_page',
814 'is_required' => TRUE,
816 'is_separate_payment' => $isSeparatePayment,
817 'membership_type_default' => $this->_ids
['membership_type'],
822 * Set up pledge block.
824 public function setUpPledgeBlock() {
826 'entity_table' => 'civicrm_contribution_page',
827 'entity_id' => $this->_ids
['contribution_page'],
828 'pledge_frequency_unit' => 'week',
829 'is_pledge_interval' => 0,
830 'pledge_start_date' => json_encode(array('calendar_date' => date('Ymd', strtotime("+1 month")))),
832 $pledgeBlock = CRM_Pledge_BAO_PledgeBlock
::create($params);
833 $this->_ids
['pledge_block_id'] = $pledgeBlock->id
;
837 * The default data set does not include a complete default membership price set - not quite sure why.
839 * This function ensures it exists & populates $this->_ids with it's data
841 public function setUpMembershipBlockPriceSet() {
842 $this->_ids
['price_set'][] = $this->callAPISuccess('price_set', 'getvalue', array(
843 'name' => 'default_membership_type_amount',
846 if (empty($this->_ids
['membership_type'])) {
847 $this->_ids
['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
849 $priceField = $this->callAPISuccess('price_field', 'create', array(
850 'price_set_id' => reset($this->_ids
['price_set']),
851 'name' => 'membership_amount',
852 'label' => 'Membership Amount',
853 'html_type' => 'Radio',
856 $this->_ids
['price_field'][] = $priceField['id'];
858 foreach ($this->_ids
['membership_type'] as $membershipTypeID) {
859 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
860 'name' => 'membership_amount',
861 'label' => 'Membership Amount',
863 'financial_type_id' => 'Donation',
864 'format.only_id' => TRUE,
865 'membership_type_id' => $membershipTypeID,
866 'price_field_id' => $priceField['id'],
868 $this->_ids
['price_field_value'][] = $priceFieldValue;
873 * Add text field other amount to the price set.
875 public function addOtherAmountFieldToMembershipPriceSet() {
876 $this->_ids
['price_field']['other_amount'] = $this->callAPISuccess('price_field', 'create', array(
877 'price_set_id' => reset($this->_ids
['price_set']),
878 'name' => 'other_amount',
879 'label' => 'Other Amount',
880 'html_type' => 'Text',
881 'format.only_id' => TRUE,
884 $this->_ids
['price_field_value']['other_amount'] = $this->callAPISuccess('price_field_value', 'create', array(
885 'financial_type_id' => 'Donation',
886 'format.only_id' => TRUE,
887 'label' => 'Other Amount',
889 'price_field_id' => $this->_ids
['price_field']['other_amount'],
894 * Help function to set up contribution page with some defaults.
896 public function setUpContributionPage() {
897 $contributionPageResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
898 if (empty($this->_ids
['price_set'])) {
899 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams
);
900 $this->_ids
['price_set'][] = $priceSet['id'];
902 $priceSetID = reset($this->_ids
['price_set']);
903 CRM_Price_BAO_PriceSet
::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
905 if (empty($this->_ids
['price_field'])) {
906 $priceField = $this->callAPISuccess('price_field', 'create', array(
907 'price_set_id' => $priceSetID,
908 'label' => 'Goat Breed',
909 'html_type' => 'Radio',
911 $this->_ids
['price_field'] = array($priceField['id']);
913 if (empty($this->_ids
['price_field_value'])) {
914 $this->callAPISuccess('price_field_value', 'create', array(
915 'price_set_id' => $priceSetID,
916 'price_field_id' => $priceField['id'],
917 'label' => 'Long Haired Goat',
918 'financial_type_id' => 'Donation',
920 'financial_type_id' => 'Donation',
921 'non_deductible_amount' => 15,
924 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
925 'price_set_id' => $priceSetID,
926 'price_field_id' => $priceField['id'],
927 'label' => 'Shoe-eating Goat',
928 'financial_type_id' => 'Donation',
930 'financial_type_id' => 'Donation',
931 'non_deductible_amount' => 5,
934 $this->_ids
['price_field_value'] = array($priceFieldValue['id']);
936 $this->_ids
['contribution_page'] = $contributionPageResult['id'];
939 public static function setUpBeforeClass() {
940 // put stuff here that should happen before all tests in this unit
943 public static function tearDownAfterClass() {
944 $tablesToTruncate = array(
946 'civicrm_financial_type',
947 'civicrm_contribution',
948 'civicrm_contribution_page',
950 $unitTest = new CiviUnitTestCase();
951 $unitTest->quickCleanup($tablesToTruncate);
955 * Create a payment processor instance.
957 protected function setupPaymentProcessor() {
958 $this->params
['payment_processor_id'] = $this->_ids
['payment_processor'] = $this->paymentProcessorCreate(array(
959 'payment_processor_type_id' => 'Dummy',
960 'class_name' => 'Payment_Dummy',
963 $this->_paymentProcessor
= $this->callAPISuccess('payment_processor', 'getsingle', array('id' => $this->params
['payment_processor_id']));
967 * Test submit recurring pledge.
969 * - 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.
971 public function testSubmitPledgePaymentPaymentProcessorRecurFuturePayment() {
972 $this->params
['adjust_recur_start_date'] = TRUE;
973 $this->params
['is_pay_later'] = FALSE;
974 $this->setUpContributionPage();
975 $this->setUpPledgeBlock();
976 $this->setupPaymentProcessor();
977 $dummyPP = Civi\Payment\System
::singleton()->getByProcessor($this->_paymentProcessor
);
978 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
980 $submitParams = array(
981 'id' => (int) $this->_ids
['contribution_page'],
983 'billing_first_name' => 'Billy',
984 'billing_middle_name' => 'Goat',
985 'billing_last_name' => 'Gruff',
986 'email' => 'billy@goat.gruff',
987 'payment_processor_id' => 1,
988 'credit_card_number' => '4111111111111111',
989 'credit_card_type' => 'Visa',
990 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
992 'pledge_frequency_interval' => 1,
993 'pledge_frequency_unit' => 'week',
994 'pledge_installments' => 3,
996 'pledge_block_id' => (int) $this->_ids
['pledge_block_id'],
999 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
1001 // Check if contribution created.
1002 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1003 'contribution_page_id' => $this->_ids
['contribution_page'],
1004 'contribution_status_id' => 'Completed', // Will be pending when actual payment processor is used (dummy processor does not support future payments).
1007 $this->assertEquals('create_first_success', $contribution['trxn_id']);
1009 // Check if pledge created.
1010 $pledge = $this->callAPISuccess('pledge', 'getsingle', array());
1011 $this->assertEquals(date('Ymd', strtotime($pledge['pledge_start_date'])), date('Ymd', strtotime("+1 month")));
1012 $this->assertEquals($pledge['pledge_amount'], 300.00);
1014 // Check if pledge payments created.
1016 'pledge_id' => $pledge['id'],
1018 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1019 $this->assertEquals($pledgePayment['count'], 3);
1020 $this->assertEquals(date('Ymd', strtotime($pledgePayment['values'][1]['scheduled_date'])), date('Ymd', strtotime("+1 month")));
1021 $this->assertEquals($pledgePayment['values'][1]['scheduled_amount'], 100.00);
1022 $this->assertEquals($pledgePayment['values'][1]['status_id'], 1); // Will be pending when actual payment processor is used (dummy processor does not support future payments).
1024 // Check contribution recur record.
1025 $recur = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
1026 $this->assertEquals(date('Ymd', strtotime($recur['start_date'])), date('Ymd', strtotime("+1 month")));
1027 $this->assertEquals($recur['amount'], 100.00);
1028 $this->assertEquals($recur['contribution_status_id'], 5); // In progress status.
1032 * Test submit pledge payment.
1034 * - test submitting a pledge payment using contribution form.
1036 public function testSubmitPledgePayment() {
1037 $this->testSubmitPledgePaymentPaymentProcessorRecurFuturePayment();
1038 $pledge = $this->callAPISuccess('pledge', 'getsingle', array());
1040 'pledge_id' => $pledge['id'],
1042 $submitParams = array(
1043 'id' => (int) $pledge['pledge_contribution_page_id'],
1044 'pledge_amount' => array(2 => 1),
1045 'billing_first_name' => 'Billy',
1046 'billing_middle_name' => 'Goat',
1047 'billing_last_name' => 'Gruff',
1048 'email' => 'billy@goat.gruff',
1049 'payment_processor_id' => 1,
1050 'credit_card_number' => '4111111111111111',
1051 'credit_card_type' => 'Visa',
1052 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1054 'pledge_id' => $pledge['id'],
1055 'cid' => $pledge['contact_id'],
1056 'contact_id' => $pledge['contact_id'],
1058 'is_pledge' => TRUE,
1059 'pledge_block_id' => $this->_ids
['pledge_block_id'],
1061 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1062 $this->assertEquals($pledgePayment['values'][2]['status_id'], 2);
1064 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__
, __FILE__
, 'submit contribution page', NULL);
1066 // Check if contribution created.
1067 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1068 'contribution_page_id' => $pledge['pledge_contribution_page_id'],
1069 'contribution_status_id' => 'Completed',
1070 'contact_id' => $pledge['contact_id'],
1071 'contribution_recur_id' => array('IS NULL' => 1),
1074 $this->assertEquals(100.00, $contribution['total_amount']);
1075 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1076 $this->assertEquals($pledgePayment['values'][2]['status_id'], 1, "This pledge payment should have been completed");
1077 $this->assertEquals($pledgePayment['values'][2]['contribution_id'], $contribution['id']);