stylistic repair
[civicrm-core.git] / tests / phpunit / api / v3 / ContributionPageTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
26 */
27
28 require_once 'CiviTest/CiviUnitTestCase.php';
29
30
31 /**
32 * Test APIv3 civicrm_contribute_recur* functions
33 *
34 * @package CiviCRM_APIv3
35 * @subpackage API_Contribution
36 */
37 class api_v3_ContributionPageTest extends CiviUnitTestCase {
38 protected $_apiversion = 3;
39 protected $testAmount = 34567;
40 protected $params;
41 protected $id = 0;
42 protected $contactIds = array();
43 protected $_entity = 'contribution_page';
44 protected $contribution_result = NULL;
45 protected $_priceSetParams = array();
46 /**
47 * Payment processor details
48 * @var array
49 */
50 protected $_paymentProcessor = array();
51
52 /**
53 * @var array
54 * - contribution_page
55 * - price_set
56 * - price_field
57 * - price_field_value
58 */
59 protected $_ids = array();
60
61
62 public $DBResetRequired = TRUE;
63
64 public function setUp() {
65 parent::setUp();
66 $this->contactIds[] = $this->individualCreate();
67 $this->params = array(
68 'title' => "Test Contribution Page",
69 'financial_type_id' => 1,
70 'currency' => 'NZD',
71 'goal_amount' => $this->testAmount,
72 'is_pay_later' => 1,
73 'is_monetary' => TRUE,
74 );
75
76 $this->_priceSetParams = array(
77 'is_quick_config' => 1,
78 'extends' => 'CiviContribute',
79 'financial_type_id' => 'Donation',
80 'title' => 'my Page',
81 );
82 }
83
84 public function tearDown() {
85 foreach ($this->contactIds as $id) {
86 $this->callAPISuccess('contact', 'delete', array('id' => $id));
87 }
88 $this->quickCleanUpFinancialEntities();
89 }
90
91 public function testCreateContributionPage() {
92 $result = $this->callAPIAndDocument($this->_entity, 'create', $this->params, __FUNCTION__, __FILE__);
93 $this->assertEquals(1, $result['count']);
94 $this->assertNotNull($result['values'][$result['id']]['id']);
95 $this->getAndCheck($this->params, $result['id'], $this->_entity);
96 }
97
98 public function testGetBasicContributionPage() {
99 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
100 $this->id = $createResult['id'];
101 $getParams = array(
102 'currency' => 'NZD',
103 'financial_type_id' => 1,
104 );
105 $getResult = $this->callAPIAndDocument($this->_entity, 'get', $getParams, __FUNCTION__, __FILE__);
106 $this->assertEquals(1, $getResult['count']);
107 }
108
109 public function testGetContributionPageByAmount() {
110 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
111 $this->id = $createResult['id'];
112 $getParams = array(
113 'amount' => '' . $this->testAmount, // 3456
114 'currency' => 'NZD',
115 'financial_type_id' => 1,
116 );
117 $getResult = $this->callAPIAndDocument($this->_entity, 'get', $getParams, __FUNCTION__, __FILE__);
118 $this->assertEquals(1, $getResult['count']);
119 }
120
121 public function testDeleteContributionPage() {
122 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
123 $deleteParams = array('id' => $createResult['id']);
124 $this->callAPIAndDocument($this->_entity, 'delete', $deleteParams, __FUNCTION__, __FILE__);
125 $checkDeleted = $this->callAPISuccess($this->_entity, 'get', array());
126 $this->assertEquals(0, $checkDeleted['count']);
127 }
128
129 public function testGetFieldsContributionPage() {
130 $result = $this->callAPISuccess($this->_entity, 'getfields', array('action' => 'create'));
131 $this->assertEquals(12, $result['values']['start_date']['type']);
132 }
133
134
135 /**
136 * Test form submission with basic price set
137 */
138 public function testSubmit() {
139 $this->setUpContributionPage();
140 $priceFieldID = reset($this->_ids['price_field']);
141 $priceFieldValueID = reset($this->_ids['price_field_value']);
142 $submitParams = array(
143 'price_' . $priceFieldID => $priceFieldValueID,
144 'id' => (int) $this->_ids['contribution_page'],
145 'amount' => 10,
146 );
147
148 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
149 $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
150 }
151
152 /**
153 * Test submit with a membership block in place
154 */
155 public function testSubmitMembershipBlockNotSeparatePayment() {
156 $this->setUpMembershipContributionPage();
157 $submitParams = array(
158 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
159 'id' => (int) $this->_ids['contribution_page'],
160 'amount' => 10,
161 'billing_first_name' => 'Billy',
162 'billing_middle_name' => 'Goat',
163 'billing_last_name' => 'Gruff',
164 'selectMembership' => $this->_ids['membership_type'],
165
166 );
167
168 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL, 'Submit');
169 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
170 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
171 }
172
173 /**
174 * Test submit with a membership block in place
175 */
176 public function testSubmitMembershipBlockIsSeparatePayment() {
177 $this->setUpMembershipContributionPage(TRUE);
178 $submitParams = array(
179 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
180 'id' => (int) $this->_ids['contribution_page'],
181 'amount' => 10,
182 'billing_first_name' => 'Billy',
183 'billing_middle_name' => 'Goat',
184 'billing_last_name' => 'Gruff',
185 'selectMembership' => $this->_ids['membership_type'],
186 );
187
188 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL, 'Submit');
189 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
190 $this->assertCount(2, $contributions['values']);
191 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
192 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
193 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
194 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
195 }
196
197 /**
198 * Test submit with a membership block in place
199 */
200 public function testSubmitMembershipBlockTwoTypesIsSeparatePayment() {
201 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 6)));
202 $this->_ids['membership_type'][] = $this->membershipTypeCreate(array('name' => 'Student', 'minimum_fee' => 50));
203 $this->setUpMembershipContributionPage(TRUE);
204 $submitParams = array(
205 'price_' . $this->_ids['price_field'][0] => $this->_ids['price_field_value'][1],
206 'id' => (int) $this->_ids['contribution_page'],
207 'amount' => 10,
208 'billing_first_name' => 'Billy',
209 'billing_middle_name' => 'Goat',
210 'billing_last_name' => 'Gruff',
211 'selectMembership' => $this->_ids['membership_type'][1],
212 );
213
214 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL, 'Submit');
215 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
216 $this->assertCount(2, $contributions['values']);
217 $ids = array_keys($contributions['values']);
218 $this->assertEquals('10.00', $contributions['values'][$ids[0]]['total_amount']);
219 $this->assertEquals('50.00', $contributions['values'][$ids[1]]['total_amount']);
220 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
221 $this->assertArrayHasKey($membershipPayment['contribution_id'], $contributions['values']);
222 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
223 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
224 }
225
226 /**
227 * Test submit with a membership block in place
228 */
229 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessor() {
230 $this->setUpMembershipContributionPage(TRUE);
231 $submitParams = array(
232 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
233 'id' => (int) $this->_ids['contribution_page'],
234 'amount' => 10,
235 'billing_first_name' => 'Billy',
236 'billing_middle_name' => 'Goat',
237 'billing_last_name' => 'Gruff',
238 'selectMembership' => $this->_ids['membership_type'],
239 'payment_processor' => 1,
240 'credit_card_number' => '4111111111111111',
241 'credit_card_type' => 'Visa',
242 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
243 'cvv2' => 123,
244 );
245
246 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL, 'Submit');
247 $contributions = $this->callAPISuccess('contribution', 'get', array(
248 'contribution_page_id' => $this->_ids['contribution_page'],
249 'contribution_status_id' => 1,
250 ));
251 $this->assertCount(2, $contributions['values']);
252 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
253 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
254 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
255 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
256 }
257
258 /**
259 * Test submit recurring membership with immediate confirmation (IATS style)
260 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
261 * processor (IASTS style - denoted by returning trxn_id)
262 * - the first creates a new membership, completed contribution, in progress recurring. Check these
263 * - create another - end date should be extended
264 */
265 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecur() {
266 $this->params['is_recur'] = 1;
267 $var = array();
268 $this->params['recur_frequency_unit'] = 'month';
269 $this->setUpMembershipContributionPage();
270 $dummyPP = CRM_Core_Payment::singleton('live', $this->_paymentProcessor);
271 $dummyPP->setDoDirectPaymentResult(array('contribution_status_id' => 1, 'trxn_id' => 'create_first_success'));
272
273 $submitParams = array(
274 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
275 'id' => (int) $this->_ids['contribution_page'],
276 'amount' => 10,
277 'billing_first_name' => 'Billy',
278 'billing_middle_name' => 'Goat',
279 'billing_last_name' => 'Gruff',
280 'email' => 'billy@goat.gruff',
281 'selectMembership' => $this->_ids['membership_type'],
282 'payment_processor' => 1,
283 'credit_card_number' => '4111111111111111',
284 'credit_card_type' => 'Visa',
285 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
286 'cvv2' => 123,
287 'is_recur' => 1,
288 'frequency_interval' => 1,
289 'frequency_unit' => 'month',
290 );
291
292 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL, 'Submit');
293 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
294 'contribution_page_id' => $this->_ids['contribution_page'],
295 'contribution_status_id' => 1,
296 ));
297 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
298 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
299 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
300 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
301 $this->assertEquals(1, $membership['status_id']);
302 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
303 //@todo - check with Joe about these not existing
304 //$this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id'], 'entity_id' => $membership['id']));
305 //renew it with processor setting completed - should extend membership
306 $submitParams['contact_id'] = $contribution['contact_id'];
307 $dummyPP->setDoDirectPaymentResult(array('contribution_status_id' => 1, 'trxn_id' => 'create_second_success'));
308 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
309 $this->callAPISuccess('contribution', 'getsingle', array(
310 'id' => array('NOT IN' => array($contribution['id'])),
311 'contribution_page_id' => $this->_ids['contribution_page'],
312 'contribution_status_id' => 1,
313 ));
314 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
315 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
316 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
317 $this->assertEquals(2, $recurringContribution['contribution_status_id']);
318 }
319
320 /**
321 * Test submit recurring membership with delayed confirmation (Authorize.net style)
322 * - we process 2 membership transactions against with a recurring contribution against a contribution page with a delayed
323 * processor (Authorize.net style - denoted by NOT returning trxn_id)
324 * - the first creates a pending membership, pending contribution, penging recurring. Check these
325 * - complete the transaction
326 * - create another - end date should NOT be extended
327 */
328 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurDelayed() {
329 $this->params['is_recur'] = 1;
330 $this->params['recur_frequency_unit'] = 'month';
331 $this->setUpMembershipContributionPage();
332 $dummyPP = CRM_Core_Payment::singleton('live', $this->_paymentProcessor);
333 $dummyPP->setDoDirectPaymentResult(array('contribution_status_id' => 2));
334
335 $submitParams = array(
336 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
337 'id' => (int) $this->_ids['contribution_page'],
338 'amount' => 10,
339 'billing_first_name' => 'Billy',
340 'billing_middle_name' => 'Goat',
341 'billing_last_name' => 'Gruff',
342 'email' => 'billy@goat.gruff',
343 'selectMembership' => $this->_ids['membership_type'],
344 'payment_processor' => 1,
345 'credit_card_number' => '4111111111111111',
346 'credit_card_type' => 'Visa',
347 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
348 'cvv2' => 123,
349 'is_recur' => 1,
350 'frequency_interval' => 1,
351 'frequency_unit' => 'month',
352 );
353
354 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL, 'Submit');
355 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
356 'contribution_page_id' => $this->_ids['contribution_page'],
357 'contribution_status_id' => 2,
358 ));
359 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
360 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
361 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
362 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
363 $this->assertEquals(5, $membership['status_id']);
364 //@todo - check with Joe about these not existing
365 //$this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id'], 'entity_id' => $membership['id']));
366 $this->callAPISuccess('contribution', 'completetransaction', array(
367 'id' => $contribution['id'],
368 'trxn_id' => 'ipn_called',
369 ));
370 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
371 //renew it with processor setting completed - should extend membership
372 $submitParams = array_merge($submitParams, array(
373 'contact_id' => $contribution['contact_id'],
374 'is_recur' => 1,
375 'frequency_interval' => 1,
376 'frequency_unit' => 'month',
377 )
378 );
379 $dummyPP->setDoDirectPaymentResult(array('contribution_status_id' => 2));
380 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
381 $newContribution = $this->callAPISuccess('contribution', 'getsingle', array(
382 'id' => array(
383 'NOT IN' => array($contribution['id']),
384 ),
385 'contribution_page_id' => $this->_ids['contribution_page'],
386 'contribution_status_id' => 2,
387 )
388 );
389
390 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
391 //no renewal as the date hasn't changed
392 $this->assertEquals($membership['end_date'], $renewedMembership['end_date']);
393 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $newContribution['contribution_recur_id']));
394 $this->assertEquals(2, $recurringContribution['contribution_status_id']);
395 }
396
397 /**
398 * Set up membership contribution page
399 * @param bool $isSeparatePayment
400 */
401 public function setUpMembershipContributionPage($isSeparatePayment = FALSE) {
402 $this->setUpMembershipBlockPriceSet();
403 $this->params['payment_processor_id'] = $this->_ids['payment_processor'] = $this->paymentProcessorCreate(array(
404 'payment_processor_type_id' => 'Dummy',
405 'class_name' => 'Payment_Dummy',
406 'billing_mode' => 1,
407 ));
408 $this->_paymentProcessor = $this->callAPISuccess('payment_processor', 'getsingle', array('id' => $this->params['payment_processor_id']));
409 $this->setUpContributionPage();
410
411 $this->callAPISuccess('membership_block', 'create', array(
412 'entity_id' => $this->_ids['contribution_page'],
413 'entity_table' => 'civicrm_contribution_page',
414 'is_required' => TRUE,
415 'is_active' => TRUE,
416 'is_separate_payment' => $isSeparatePayment,
417 'membership_type_default' => $this->_ids['membership_type'],
418 ));
419 }
420
421 /**
422 * The default data set does not include a complete default membership price set - not quite sure why
423 * This function ensures it exists & populates $this->_ids with it's data
424 */
425 public function setUpMembershipBlockPriceSet() {
426 $this->_ids['price_set'][] = $this->callAPISuccess('price_set', 'getvalue', array(
427 'name' => 'default_membership_type_amount',
428 'return' => 'id',
429 ));
430 if (empty($this->_ids['membership_type'])) {
431 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
432 }
433 $priceField = $this->callAPISuccess('price_field', 'create', array(
434 'price_set_id' => reset($this->_ids['price_set']),
435 'name' => 'membership_amount',
436 'label' => 'Membership Amount',
437 'html_type' => 'Radio',
438 'sequential' => 1,
439 ));
440 $this->_ids['price_field'][] = $priceField['id'];
441 foreach ($this->_ids['membership_type'] as $membershipTypeID) {
442 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
443 'name' => 'membership_amount',
444 'label' => 'Membership Amount',
445 'amount' => 1,
446 'financial_type_id' => 1,
447 'format.only_id' => TRUE,
448 'membership_type_id' => $membershipTypeID,
449 'price_field_id' => $priceField['id'],
450 ));
451 $this->_ids['price_field_value'][] = $priceFieldValue['id'];
452 }
453 }
454
455 /**
456 * Help function to set up contribution page with some defaults
457 */
458 public function setUpContributionPage() {
459 $contributionPageResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
460 if (empty($this->_ids['price_set'])) {
461 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams);
462 $this->_ids['price_set'][] = $priceSet['id'];
463 }
464 $priceSetID = reset($this->_ids['price_set']);
465 CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
466
467 if (empty($this->_ids['price_field'])) {
468 $priceField = $this->callAPISuccess('price_field', 'create', array(
469 'price_set_id' => $priceSetID,
470 'label' => 'Goat Breed',
471 'html_type' => 'Radio',
472 ));
473 $this->_ids['price_field'] = array($priceField['id']);
474 }
475 if (empty($this->_ids['price_field_value'])) {
476 $this->callAPISuccess('price_field_value', 'create', array(
477 'price_set_id' => $priceSetID,
478 'price_field_id' => $priceField['id'],
479 'label' => 'Long Haired Goat',
480 'amount' => 20,
481 )
482 );
483 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
484 'price_set_id' => $priceSetID,
485 'price_field_id' => $priceField['id'],
486 'label' => 'Shoe-eating Goat',
487 'amount' => 10,
488 )
489 );
490 $this->_ids['price_field_value'] = array($priceFieldValue['id']);
491 }
492 $this->_ids['contribution_page'] = $contributionPageResult['id'];
493 }
494
495 public static function setUpBeforeClass() {
496 // put stuff here that should happen before all tests in this unit
497 }
498
499 public static function tearDownAfterClass() {
500 $tablesToTruncate = array(
501 'civicrm_contact',
502 'civicrm_financial_type',
503 'civicrm_contribution',
504 'civicrm_contribution_page',
505 );
506 $unitTest = new CiviUnitTestCase();
507 $unitTest->quickCleanup($tablesToTruncate);
508 }
509
510 }