Merge pull request #7797 from JKingsnorth/CRM-17977
[civicrm-core.git] / tests / phpunit / api / v3 / ContributionPageTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2016 |
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 /**
29 * Test APIv3 civicrm_contribute_recur* functions
30 *
31 * @package CiviCRM_APIv3
32 * @subpackage API_Contribution
33 * @group headless
34 */
35 class api_v3_ContributionPageTest extends CiviUnitTestCase {
36 protected $_apiversion = 3;
37 protected $testAmount = 34567;
38 protected $params;
39 protected $id = 0;
40 protected $contactIds = array();
41 protected $_entity = 'contribution_page';
42 protected $contribution_result = NULL;
43 protected $_priceSetParams = array();
44 /**
45 * Payment processor details.
46 * @var array
47 */
48 protected $_paymentProcessor = array();
49
50 /**
51 * @var array
52 * - contribution_page
53 * - price_set
54 * - price_field
55 * - price_field_value
56 */
57 protected $_ids = array();
58
59
60 public $DBResetRequired = TRUE;
61
62 public function setUp() {
63 parent::setUp();
64 $this->contactIds[] = $this->individualCreate();
65 $this->params = array(
66 'title' => "Test Contribution Page",
67 'financial_type_id' => 1,
68 'currency' => 'NZD',
69 'goal_amount' => $this->testAmount,
70 'is_pay_later' => 1,
71 'is_monetary' => TRUE,
72 'is_email_receipt' => TRUE,
73 'receipt_from_email' => 'yourconscience@donate.com',
74 'receipt_from_name' => 'Ego Freud',
75 );
76
77 $this->_priceSetParams = array(
78 'is_quick_config' => 1,
79 'extends' => 'CiviContribute',
80 'financial_type_id' => 'Donation',
81 'title' => 'my Page',
82 );
83 }
84
85 public function tearDown() {
86 foreach ($this->contactIds as $id) {
87 $this->callAPISuccess('contact', 'delete', array('id' => $id));
88 }
89 $this->quickCleanUpFinancialEntities();
90 }
91
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);
97 }
98
99 public function testGetBasicContributionPage() {
100 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
101 $this->id = $createResult['id'];
102 $getParams = array(
103 'currency' => 'NZD',
104 'financial_type_id' => 1,
105 );
106 $getResult = $this->callAPIAndDocument($this->_entity, 'get', $getParams, __FUNCTION__, __FILE__);
107 $this->assertEquals(1, $getResult['count']);
108 }
109
110 public function testGetContributionPageByAmount() {
111 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
112 $this->id = $createResult['id'];
113 $getParams = array(
114 'amount' => '' . $this->testAmount, // 3456
115 'currency' => 'NZD',
116 'financial_type_id' => 1,
117 );
118 $getResult = $this->callAPIAndDocument($this->_entity, 'get', $getParams, __FUNCTION__, __FILE__);
119 $this->assertEquals(1, $getResult['count']);
120 }
121
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']);
128 }
129
130 public function testGetFieldsContributionPage() {
131 $result = $this->callAPISuccess($this->_entity, 'getfields', array('action' => 'create'));
132 $this->assertEquals(12, $result['values']['start_date']['type']);
133 }
134
135
136 /**
137 * Test form submission with basic price set.
138 */
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'],
146 'amount' => 10,
147 );
148
149 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
150 $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
151 }
152
153 /**
154 * Test form submission with billing first & last name where the contact does NOT
155 * otherwise have one.
156 */
157 public function testSubmitNewBillingNameData() {
158 $this->setUpContributionPage();
159 $contact = $this->callAPISuccess('Contact', 'create', array('contact_type' => 'Individual', 'email' => 'wonderwoman@amazon.com'));
160 $priceFieldID = reset($this->_ids['price_field']);
161 $priceFieldValueID = reset($this->_ids['price_field_value']);
162 $submitParams = array(
163 'price_' . $priceFieldID => $priceFieldValueID,
164 'id' => (int) $this->_ids['contribution_page'],
165 'amount' => 10,
166 'billing_first_name' => 'Wonder',
167 'billing_last_name' => 'Woman',
168 'contactID' => $contact['id'],
169 'email' => 'wonderwoman@amazon.com',
170 );
171
172 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
173 $contact = $this->callAPISuccess('Contact', 'get', array(
174 'id' => $contact['id'],
175 'return' => array(
176 'first_name',
177 'last_name',
178 'sort_name',
179 'display_name',
180 ),
181 ));
182 $this->assertEquals(array(
183 'first_name' => 'Wonder',
184 'last_name' => 'Woman',
185 'display_name' => 'Wonder Woman',
186 'sort_name' => 'Woman, Wonder',
187 'id' => $contact['id'],
188 'contact_id' => $contact['id'],
189 ), $contact['values'][$contact['id']]);
190
191 }
192
193 /**
194 * Test form submission with billing first & last name where the contact does
195 * otherwise have one and should not be overwritten.
196 */
197 public function testSubmitNewBillingNameDoNotOverwrite() {
198 $this->setUpContributionPage();
199 $contact = $this->callAPISuccess('Contact', 'create', array(
200 'contact_type' => 'Individual',
201 'email' => 'wonderwoman@amazon.com',
202 'first_name' => 'Super',
203 'last_name' => 'Boy',
204 ));
205 $priceFieldID = reset($this->_ids['price_field']);
206 $priceFieldValueID = reset($this->_ids['price_field_value']);
207 $submitParams = array(
208 'price_' . $priceFieldID => $priceFieldValueID,
209 'id' => (int) $this->_ids['contribution_page'],
210 'amount' => 10,
211 'billing_first_name' => 'Wonder',
212 'billing_last_name' => 'Woman',
213 'contactID' => $contact['id'],
214 'email' => 'wonderwoman@amazon.com',
215 );
216
217 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
218 $contact = $this->callAPISuccess('Contact', 'get', array(
219 'id' => $contact['id'],
220 'return' => array(
221 'first_name',
222 'last_name',
223 'sort_name',
224 'display_name',
225 ),
226 ));
227 $this->assertEquals(array(
228 'first_name' => 'Super',
229 'last_name' => 'Boy',
230 'display_name' => 'Super Boy',
231 'sort_name' => 'Boy, Super',
232 'id' => $contact['id'],
233 'contact_id' => $contact['id'],
234 ), $contact['values'][$contact['id']]);
235
236 }
237
238 /**
239 * Test process with instant payment when more than one configured for the page.
240 *
241 * CRM-16923
242 */
243 public function testSubmitRecurMultiProcessorInstantPayment() {
244 $this->setUpContributionPage();
245 $this->setupPaymentProcessor();
246 $paymentProcessor2ID = $this->paymentProcessorCreate(array(
247 'payment_processor_type_id' => 'Dummy',
248 'name' => 'processor 2',
249 'class_name' => 'Payment_Dummy',
250 'billing_mode' => 1,
251 ));
252 $dummyPP = Civi\Payment\System::singleton()->getById($paymentProcessor2ID);
253 $dummyPP->setDoDirectPaymentResult(array(
254 'payment_status_id' => 1,
255 'trxn_id' => 'create_first_success',
256 'fee_amount' => .85,
257 ));
258 $this->callAPISuccess('ContributionPage', 'create', array(
259 'id' => $this->_ids['contribution_page'],
260 'payment_processor' => array($paymentProcessor2ID, $this->_ids['payment_processor']),
261 ));
262
263 $priceFieldID = reset($this->_ids['price_field']);
264 $priceFieldValueID = reset($this->_ids['price_field_value']);
265 $submitParams = array(
266 'price_' . $priceFieldID => $priceFieldValueID,
267 'id' => (int) $this->_ids['contribution_page'],
268 'amount' => 10,
269 'is_recur' => 1,
270 'frequency_interval' => 1,
271 'frequency_unit' => 'month',
272 'payment_processor_id' => $paymentProcessor2ID,
273 );
274
275 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
276 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
277 'contribution_page_id' => $this->_ids['contribution_page'],
278 'contribution_status_id' => 1,
279 ));
280 $this->assertEquals('create_first_success', $contribution['trxn_id']);
281 $this->assertEquals(10, $contribution['total_amount']);
282 $this->assertEquals(.85, $contribution['fee_amount']);
283 $this->assertEquals(9.15, $contribution['net_amount']);
284 }
285
286 /**
287 * Test submit with a membership block in place.
288 */
289 public function testSubmitMembershipBlockNotSeparatePayment() {
290 $this->setUpMembershipContributionPage();
291 $submitParams = array(
292 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
293 'id' => (int) $this->_ids['contribution_page'],
294 'amount' => 10,
295 'billing_first_name' => 'Billy',
296 'billing_middle_name' => 'Goat',
297 'billing_last_name' => 'Gruff',
298 'selectMembership' => $this->_ids['membership_type'],
299
300 );
301
302 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
303 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
304 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
305 }
306
307 /**
308 * Test submit with a membership block in place.
309 */
310 public function testSubmitMembershipBlockNotSeparatePaymentWithEmail() {
311 $mut = new CiviMailUtils($this, TRUE);
312 $this->setUpMembershipContributionPage();
313 $this->addProfile('supporter_profile', $this->_ids['contribution_page']);
314
315 $submitParams = array(
316 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
317 'id' => (int) $this->_ids['contribution_page'],
318 'amount' => 10,
319 'billing_first_name' => 'Billy',
320 'billing_middle_name' => 'Goat',
321 'billing_last_name' => 'Gruff',
322 'selectMembership' => $this->_ids['membership_type'],
323 'email-Primary' => 'billy-goat@the-bridge.net',
324 'payment_processor_id' => $this->_paymentProcessor['id'],
325 'credit_card_number' => '4111111111111111',
326 'credit_card_type' => 'Visa',
327 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
328 'cvv2' => 123,
329 );
330
331 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
332 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
333 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
334 $mut->checkMailLog(array(
335 'Membership Type: General',
336 ));
337 $mut->stop();
338 $mut->clearMessages();
339 }
340
341 /**
342 * Test submit with a membership block in place.
343 */
344 public function testSubmitMembershipBlockNotSeparatePaymentZeroDollarsWithEmail() {
345 $mut = new CiviMailUtils($this, TRUE);
346 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 0)));
347 $this->setUpMembershipContributionPage();
348 $this->addProfile('supporter_profile', $this->_ids['contribution_page']);
349
350 $submitParams = array(
351 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
352 'id' => (int) $this->_ids['contribution_page'],
353 'amount' => 0,
354 'billing_first_name' => 'Billy',
355 'billing_middle_name' => 'Goat',
356 'billing_last_name' => 'Gruffier',
357 'selectMembership' => $this->_ids['membership_type'],
358 'email-Primary' => 'billy-goat@the-new-bridge.net',
359 );
360
361 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
362 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
363 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
364 $mut->checkMailLog(array(
365 'Membership Type: General',
366 'Gruffier',
367 ),
368 array(
369 'Amount',
370 )
371 );
372 $mut->stop();
373 $mut->clearMessages(999);
374 }
375
376 /**
377 * Test submit with a membership block in place.
378 */
379 public function testSubmitMembershipBlockIsSeparatePayment() {
380 $this->setUpMembershipContributionPage(TRUE);
381 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
382 $submitParams = array(
383 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
384 'id' => (int) $this->_ids['contribution_page'],
385 'amount' => 10,
386 'billing_first_name' => 'Billy',
387 'billing_middle_name' => 'Goat',
388 'billing_last_name' => 'Gruff',
389 'selectMembership' => $this->_ids['membership_type'],
390 );
391
392 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
393 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
394 $this->assertCount(2, $contributions['values']);
395 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
396 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
397 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
398 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
399 }
400
401 /**
402 * Test submit with a membership block in place.
403 */
404 public function testSubmitMembershipBlockIsSeparatePaymentWithEmail() {
405 $mut = new CiviMailUtils($this, TRUE);
406 $this->setUpMembershipContributionPage(TRUE);
407 $this->addProfile('supporter_profile', $this->_ids['contribution_page']);
408
409 $submitParams = array(
410 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
411 'id' => (int) $this->_ids['contribution_page'],
412 'amount' => 10,
413 'billing_first_name' => 'Billy',
414 'billing_middle_name' => 'Goat',
415 'billing_last_name' => 'Gruff',
416 'selectMembership' => $this->_ids['membership_type'],
417 'email-Primary' => 'billy-goat@the-bridge.net',
418 'payment_processor_id' => $this->_paymentProcessor['id'],
419 'credit_card_number' => '4111111111111111',
420 'credit_card_type' => 'Visa',
421 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
422 'cvv2' => 123,
423 );
424
425 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
426 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
427 $this->assertCount(2, $contributions['values']);
428 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
429 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
430 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
431 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
432 $mut->checkAllMailLog(array(
433 '$ 2.00',
434 'Membership Fee',
435 ));
436 $mut->stop();
437 $mut->clearMessages(999);
438 }
439
440 /**
441 * Test submit with a membership block in place.
442 */
443 public function testSubmitMembershipBlockIsSeparatePaymentZeroDollarsPayLaterWithEmail() {
444 $mut = new CiviMailUtils($this, TRUE);
445 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 0)));
446 $this->setUpMembershipContributionPage(TRUE);
447 $this->addProfile('supporter_profile', $this->_ids['contribution_page']);
448
449 $submitParams = array(
450 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
451 'id' => (int) $this->_ids['contribution_page'],
452 'amount' => 0,
453 'billing_first_name' => 'Billy',
454 'billing_middle_name' => 'Goat',
455 'billing_last_name' => 'Gruffalo',
456 'selectMembership' => $this->_ids['membership_type'],
457 'payment_processor_id' => 0,
458 'email-Primary' => 'gruffalo@the-bridge.net',
459 );
460
461 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
462 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
463 $this->assertCount(2, $contributions['values']);
464 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
465 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
466 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
467 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
468 $mut->checkMailLog(array(
469 'Gruffalo',
470 'General Membership: $ 0.00',
471 'Membership Fee',
472 ));
473 $mut->stop();
474 $mut->clearMessages();
475 }
476
477 /**
478 * Test submit with a membership block in place.
479 */
480 public function testSubmitMembershipBlockTwoTypesIsSeparatePayment() {
481 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 6)));
482 $this->_ids['membership_type'][] = $this->membershipTypeCreate(array('name' => 'Student', 'minimum_fee' => 50));
483 $this->setUpMembershipContributionPage(TRUE);
484 $submitParams = array(
485 'price_' . $this->_ids['price_field'][0] => $this->_ids['price_field_value'][1],
486 'id' => (int) $this->_ids['contribution_page'],
487 'amount' => 10,
488 'billing_first_name' => 'Billy',
489 'billing_middle_name' => 'Goat',
490 'billing_last_name' => 'Gruff',
491 'selectMembership' => $this->_ids['membership_type'][1],
492 );
493
494 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
495 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
496 $this->assertCount(2, $contributions['values']);
497 $ids = array_keys($contributions['values']);
498 $this->assertEquals('10.00', $contributions['values'][$ids[0]]['total_amount']);
499 $this->assertEquals('50.00', $contributions['values'][$ids[1]]['total_amount']);
500 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
501 $this->assertArrayHasKey($membershipPayment['contribution_id'], $contributions['values']);
502 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
503 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
504 }
505
506 /**
507 * Test submit with a membership block in place.
508 *
509 * We are expecting a separate payment for the membership vs the contribution.
510 */
511 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNow() {
512 $this->setUpMembershipContributionPage(TRUE);
513 $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']);
514 $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
515 $submitParams = array(
516 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
517 'id' => (int) $this->_ids['contribution_page'],
518 'amount' => 10,
519 'billing_first_name' => 'Billy',
520 'billing_middle_name' => 'Goat',
521 'billing_last_name' => 'Gruff',
522 'selectMembership' => $this->_ids['membership_type'],
523 'payment_processor_id' => $this->_paymentProcessor['id'],
524 'credit_card_number' => '4111111111111111',
525 'credit_card_type' => 'Visa',
526 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
527 'cvv2' => 123,
528 );
529
530 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
531 $contributions = $this->callAPISuccess('contribution', 'get', array(
532 'contribution_page_id' => $this->_ids['contribution_page'],
533 'contribution_status_id' => 1,
534 ));
535 $this->assertCount(2, $contributions['values']);
536 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
537 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
538 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
539 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
540 foreach ($contributions['values'] as $contribution) {
541 $this->assertEquals(.72, $contribution['fee_amount']);
542 $this->assertEquals($contribution['total_amount'] - .72, $contribution['net_amount']);
543 }
544 }
545
546 /**
547 * Test that when a transaction fails the pending contribution remains.
548 *
549 * An activity should also be created. CRM-16417.
550 */
551 public function testSubmitPaymentProcessorFailure() {
552 $this->setUpContributionPage();
553 $this->setupPaymentProcessor();
554 $this->createLoggedInUser();
555 $priceFieldID = reset($this->_ids['price_field']);
556 $priceFieldValueID = reset($this->_ids['price_field_value']);
557 $submitParams = array(
558 'price_' . $priceFieldID => $priceFieldValueID,
559 'id' => (int) $this->_ids['contribution_page'],
560 'amount' => 10,
561 'payment_processor_id' => 1,
562 'credit_card_number' => '4111111111111111',
563 'credit_card_type' => 'Visa',
564 'credit_card_exp_date' => array('M' => 9, 'Y' => 2008),
565 'cvv2' => 123,
566 );
567
568 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
569 $contribution = $this->callAPISuccessGetSingle('contribution', array(
570 'contribution_page_id' => $this->_ids['contribution_page'],
571 'contribution_status_id' => 2,
572 ));
573
574 $this->callAPISuccessGetSingle('activity', array(
575 'source_record_id' => $contribution['id'],
576 'activity_type_id' => 'Failed Payment',
577 ));
578
579 }
580
581 /**
582 * Test submit recurring membership with immediate confirmation (IATS style).
583 *
584 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
585 * processor (IATS style - denoted by returning trxn_id)
586 * - the first creates a new membership, completed contribution, in progress recurring. Check these
587 * - create another - end date should be extended
588 */
589 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPayment() {
590 $this->params['is_recur'] = 1;
591 $this->params['recur_frequency_unit'] = 'month';
592 $this->setUpMembershipContributionPage();
593 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
594 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
595
596 $submitParams = array(
597 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
598 'id' => (int) $this->_ids['contribution_page'],
599 'amount' => 10,
600 'billing_first_name' => 'Billy',
601 'billing_middle_name' => 'Goat',
602 'billing_last_name' => 'Gruff',
603 'email' => 'billy@goat.gruff',
604 'selectMembership' => $this->_ids['membership_type'],
605 'payment_processor_id' => 1,
606 'credit_card_number' => '4111111111111111',
607 'credit_card_type' => 'Visa',
608 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
609 'cvv2' => 123,
610 'is_recur' => 1,
611 'frequency_interval' => 1,
612 'frequency_unit' => 'month',
613 );
614
615 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
616 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
617 'contribution_page_id' => $this->_ids['contribution_page'],
618 'contribution_status_id' => 1,
619 ));
620
621 $this->assertEquals('create_first_success', $contribution['trxn_id']);
622 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
623 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
624 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
625 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
626 $this->assertEquals(1, $membership['status_id']);
627 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
628 //@todo - check with Joe about these not existing
629 //$this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id'], 'entity_id' => $membership['id']));
630 //renew it with processor setting completed - should extend membership
631 $submitParams['contact_id'] = $contribution['contact_id'];
632 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
633 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
634 $this->callAPISuccess('contribution', 'getsingle', array(
635 'id' => array('NOT IN' => array($contribution['id'])),
636 'contribution_page_id' => $this->_ids['contribution_page'],
637 'contribution_status_id' => 1,
638 ));
639 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
640 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
641 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
642 $this->assertEquals(5, $recurringContribution['contribution_status_id']);
643 }
644
645 /**
646 * Test submit recurring membership with immediate confirmation (IATS style).
647 *
648 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
649 * processor (IATS style - denoted by returning trxn_id)
650 * - the first creates a new membership, completed contribution, in progress recurring. Check these
651 * - create another - end date should be extended
652 */
653 public function testSubmitMembershipPriceSetPaymentPaymentProcessorSeparatePaymentRecurInstantPayment() {
654
655 $this->setUpMembershipContributionPage(TRUE);
656 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
657 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
658
659 $submitParams = array(
660 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
661 'id' => (int) $this->_ids['contribution_page'],
662 'amount' => 10,
663 'billing_first_name' => 'Billy',
664 'billing_middle_name' => 'Goat',
665 'billing_last_name' => 'Gruff',
666 'email' => 'billy@goat.gruff',
667 'selectMembership' => $this->_ids['membership_type'],
668 'payment_processor_id' => 1,
669 'credit_card_number' => '4111111111111111',
670 'credit_card_type' => 'Visa',
671 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
672 'cvv2' => 123,
673 'is_recur' => 1,
674 'auto_renew' => TRUE,
675 'frequency_interval' => 1,
676 'frequency_unit' => 'month',
677 );
678
679 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
680 $contribution = $this->callAPISuccess('contribution', 'get', array(
681 'contribution_page_id' => $this->_ids['contribution_page'],
682 'contribution_status_id' => 1,
683 ));
684
685 $this->assertEquals(2, $contribution['count']);
686 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
687 $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
688 $this->assertNotEmpty($contribution['values'][$membershipPayment['contribution_id']]['contribution_recur_id']);
689 $this->callAPISuccess('contribution_recur', 'getsingle', array());
690 }
691
692 /**
693 * Test submit recurring membership with delayed confirmation (Authorize.net style)
694 * - we process 2 membership transactions against with a recurring contribution against a contribution page with a delayed
695 * processor (Authorize.net style - denoted by NOT returning trxn_id)
696 * - the first creates a pending membership, pending contribution, penging recurring. Check these
697 * - complete the transaction
698 * - create another - end date should NOT be extended
699 */
700 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurDelayed() {
701 $this->params['is_recur'] = 1;
702 $this->params['recur_frequency_unit'] = 'month';
703 $this->setUpMembershipContributionPage();
704 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
705 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
706
707 $submitParams = array(
708 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
709 'id' => (int) $this->_ids['contribution_page'],
710 'amount' => 10,
711 'billing_first_name' => 'Billy',
712 'billing_middle_name' => 'Goat',
713 'billing_last_name' => 'Gruff',
714 'email' => 'billy@goat.gruff',
715 'selectMembership' => $this->_ids['membership_type'],
716 'payment_processor_id' => 1,
717 'credit_card_number' => '4111111111111111',
718 'credit_card_type' => 'Visa',
719 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
720 'cvv2' => 123,
721 'is_recur' => 1,
722 'frequency_interval' => 1,
723 'frequency_unit' => 'month',
724 );
725
726 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
727 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
728 'contribution_page_id' => $this->_ids['contribution_page'],
729 'contribution_status_id' => 2,
730 ));
731 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
732 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
733 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
734 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
735 $this->assertEquals(5, $membership['status_id']);
736 //@todo - check with Joe about these not existing
737 //$this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id'], 'entity_id' => $membership['id']));
738 $this->callAPISuccess('contribution', 'completetransaction', array(
739 'id' => $contribution['id'],
740 'trxn_id' => 'ipn_called',
741 'payment_processor_id' => $this->_paymentProcessor['id'],
742 ));
743 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
744 //renew it with processor setting completed - should extend membership
745 $submitParams = array_merge($submitParams, array(
746 'contact_id' => $contribution['contact_id'],
747 'is_recur' => 1,
748 'frequency_interval' => 1,
749 'frequency_unit' => 'month',
750 )
751 );
752 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
753 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
754 $newContribution = $this->callAPISuccess('contribution', 'getsingle', array(
755 'id' => array(
756 'NOT IN' => array($contribution['id']),
757 ),
758 'contribution_page_id' => $this->_ids['contribution_page'],
759 'contribution_status_id' => 2,
760 )
761 );
762
763 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
764 //no renewal as the date hasn't changed
765 $this->assertEquals($membership['end_date'], $renewedMembership['end_date']);
766 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $newContribution['contribution_recur_id']));
767 $this->assertEquals(2, $recurringContribution['contribution_status_id']);
768 }
769
770 /**
771 * Set up membership contribution page.
772 * @param bool $isSeparatePayment
773 */
774 public function setUpMembershipContributionPage($isSeparatePayment = FALSE) {
775 $this->setUpMembershipBlockPriceSet();
776 $this->setupPaymentProcessor();
777 $this->setUpContributionPage();
778
779 $this->callAPISuccess('membership_block', 'create', array(
780 'entity_id' => $this->_ids['contribution_page'],
781 'entity_table' => 'civicrm_contribution_page',
782 'is_required' => TRUE,
783 'is_active' => TRUE,
784 'is_separate_payment' => $isSeparatePayment,
785 'membership_type_default' => $this->_ids['membership_type'],
786 ));
787 }
788
789 /**
790 * The default data set does not include a complete default membership price set - not quite sure why.
791 *
792 * This function ensures it exists & populates $this->_ids with it's data
793 */
794 public function setUpMembershipBlockPriceSet() {
795 $this->_ids['price_set'][] = $this->callAPISuccess('price_set', 'getvalue', array(
796 'name' => 'default_membership_type_amount',
797 'return' => 'id',
798 ));
799 if (empty($this->_ids['membership_type'])) {
800 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
801 }
802 $priceField = $this->callAPISuccess('price_field', 'create', array(
803 'price_set_id' => reset($this->_ids['price_set']),
804 'name' => 'membership_amount',
805 'label' => 'Membership Amount',
806 'html_type' => 'Radio',
807 'sequential' => 1,
808 ));
809 $this->_ids['price_field'][] = $priceField['id'];
810
811 foreach ($this->_ids['membership_type'] as $membershipTypeID) {
812 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
813 'name' => 'membership_amount',
814 'label' => 'Membership Amount',
815 'amount' => 1,
816 'financial_type_id' => 'Donation',
817 'format.only_id' => TRUE,
818 'membership_type_id' => $membershipTypeID,
819 'price_field_id' => $priceField['id'],
820 ));
821 $this->_ids['price_field_value'][] = $priceFieldValue['id'];
822 }
823 }
824
825 /**
826 * Add text field other amount to the price set.
827 */
828 public function addOtherAmountFieldToMembershipPriceSet() {
829 $this->_ids['price_field']['other_amount'] = $this->callAPISuccess('price_field', 'create', array(
830 'price_set_id' => reset($this->_ids['price_set']),
831 'name' => 'other_amount',
832 'label' => 'Other Amount',
833 'html_type' => 'Text',
834 'format.only_id' => TRUE,
835 'sequential' => 1,
836 ));
837 $this->_ids['price_field_value']['other_amount'] = $this->callAPISuccess('price_field_value', 'create', array(
838 'financial_type_id' => 'Donation',
839 'format.only_id' => TRUE,
840 'label' => 'Other Amount',
841 'amount' => 1,
842 'price_field_id' => $this->_ids['price_field']['other_amount'],
843 ));
844 }
845
846 /**
847 * Help function to set up contribution page with some defaults.
848 */
849 public function setUpContributionPage() {
850 $contributionPageResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
851 if (empty($this->_ids['price_set'])) {
852 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams);
853 $this->_ids['price_set'][] = $priceSet['id'];
854 }
855 $priceSetID = reset($this->_ids['price_set']);
856 CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
857
858 if (empty($this->_ids['price_field'])) {
859 $priceField = $this->callAPISuccess('price_field', 'create', array(
860 'price_set_id' => $priceSetID,
861 'label' => 'Goat Breed',
862 'html_type' => 'Radio',
863 ));
864 $this->_ids['price_field'] = array($priceField['id']);
865 }
866 if (empty($this->_ids['price_field_value'])) {
867 $this->callAPISuccess('price_field_value', 'create', array(
868 'price_set_id' => $priceSetID,
869 'price_field_id' => $priceField['id'],
870 'label' => 'Long Haired Goat',
871 'financial_type_id' => 'Donation',
872 'amount' => 20,
873 'financial_type_id' => 'Donation',
874 )
875 );
876 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
877 'price_set_id' => $priceSetID,
878 'price_field_id' => $priceField['id'],
879 'label' => 'Shoe-eating Goat',
880 'financial_type_id' => 'Donation',
881 'amount' => 10,
882 'financial_type_id' => 'Donation',
883 )
884 );
885 $this->_ids['price_field_value'] = array($priceFieldValue['id']);
886 }
887 $this->_ids['contribution_page'] = $contributionPageResult['id'];
888 }
889
890 public static function setUpBeforeClass() {
891 // put stuff here that should happen before all tests in this unit
892 }
893
894 public static function tearDownAfterClass() {
895 $tablesToTruncate = array(
896 'civicrm_contact',
897 'civicrm_financial_type',
898 'civicrm_contribution',
899 'civicrm_contribution_page',
900 );
901 $unitTest = new CiviUnitTestCase();
902 $unitTest->quickCleanup($tablesToTruncate);
903 }
904
905 /**
906 * Create a payment processor instance.
907 */
908 protected function setupPaymentProcessor() {
909 $this->params['payment_processor_id'] = $this->_ids['payment_processor'] = $this->paymentProcessorCreate(array(
910 'payment_processor_type_id' => 'Dummy',
911 'class_name' => 'Payment_Dummy',
912 'billing_mode' => 1,
913 ));
914 $this->_paymentProcessor = $this->callAPISuccess('payment_processor', 'getsingle', array('id' => $this->params['payment_processor_id']));
915 }
916
917 }