Merge pull request #10992 from seamuslee001/CRM-20907
[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-2017 |
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 protected $_membershipBlockAmount = 2;
45 /**
46 * Payment processor details.
47 * @var array
48 */
49 protected $_paymentProcessor = array();
50
51 /**
52 * @var array
53 * - contribution_page
54 * - price_set
55 * - price_field
56 * - price_field_value
57 */
58 protected $_ids = array();
59
60
61 public $DBResetRequired = TRUE;
62
63 public function setUp() {
64 parent::setUp();
65 $this->contactIds[] = $this->individualCreate();
66 $this->params = array(
67 'title' => "Test Contribution Page",
68 'financial_type_id' => 1,
69 'currency' => 'NZD',
70 'goal_amount' => $this->testAmount,
71 'is_pay_later' => 1,
72 'is_monetary' => TRUE,
73 'is_email_receipt' => TRUE,
74 'receipt_from_email' => 'yourconscience@donate.com',
75 'receipt_from_name' => 'Ego Freud',
76 );
77
78 $this->_priceSetParams = array(
79 'is_quick_config' => 1,
80 'extends' => 'CiviContribute',
81 'financial_type_id' => 'Donation',
82 'title' => 'my Page',
83 );
84 }
85
86 public function tearDown() {
87 foreach ($this->contactIds as $id) {
88 $this->callAPISuccess('contact', 'delete', array('id' => $id));
89 }
90 $this->quickCleanUpFinancialEntities();
91 }
92
93 public function testCreateContributionPage() {
94 $result = $this->callAPIAndDocument($this->_entity, 'create', $this->params, __FUNCTION__, __FILE__);
95 $this->assertEquals(1, $result['count']);
96 $this->assertNotNull($result['values'][$result['id']]['id']);
97 $this->getAndCheck($this->params, $result['id'], $this->_entity);
98 }
99
100 public function testGetBasicContributionPage() {
101 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
102 $this->id = $createResult['id'];
103 $getParams = array(
104 'currency' => 'NZD',
105 'financial_type_id' => 1,
106 );
107 $getResult = $this->callAPIAndDocument($this->_entity, 'get', $getParams, __FUNCTION__, __FILE__);
108 $this->assertEquals(1, $getResult['count']);
109 }
110
111 public function testGetContributionPageByAmount() {
112 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
113 $this->id = $createResult['id'];
114 $getParams = array(
115 'amount' => '' . $this->testAmount, // 3456
116 'currency' => 'NZD',
117 'financial_type_id' => 1,
118 );
119 $getResult = $this->callAPISuccess($this->_entity, 'get', $getParams);
120 $this->assertEquals(1, $getResult['count']);
121 }
122
123 public function testDeleteContributionPage() {
124 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
125 $deleteParams = array('id' => $createResult['id']);
126 $this->callAPIAndDocument($this->_entity, 'delete', $deleteParams, __FUNCTION__, __FILE__);
127 $checkDeleted = $this->callAPISuccess($this->_entity, 'get', array());
128 $this->assertEquals(0, $checkDeleted['count']);
129 }
130
131 public function testGetFieldsContributionPage() {
132 $result = $this->callAPISuccess($this->_entity, 'getfields', array('action' => 'create'));
133 $this->assertEquals(12, $result['values']['start_date']['type']);
134 }
135
136
137 /**
138 * Test form submission with basic price set.
139 */
140 public function testSubmit() {
141 $this->setUpContributionPage();
142 $priceFieldID = reset($this->_ids['price_field']);
143 $priceFieldValueID = reset($this->_ids['price_field_value']);
144 $submitParams = array(
145 'price_' . $priceFieldID => $priceFieldValueID,
146 'id' => (int) $this->_ids['contribution_page'],
147 'amount' => 10,
148 );
149
150 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
151 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
152 //assert non-deductible amount
153 $this->assertEquals(5.00, $contribution['non_deductible_amount']);
154 }
155
156 /**
157 * Test form submission with billing first & last name where the contact does NOT
158 * otherwise have one.
159 */
160 public function testSubmitNewBillingNameData() {
161 $this->setUpContributionPage();
162 $contact = $this->callAPISuccess('Contact', 'create', array('contact_type' => 'Individual', 'email' => 'wonderwoman@amazon.com'));
163 $priceFieldID = reset($this->_ids['price_field']);
164 $priceFieldValueID = reset($this->_ids['price_field_value']);
165 $submitParams = array(
166 'price_' . $priceFieldID => $priceFieldValueID,
167 'id' => (int) $this->_ids['contribution_page'],
168 'amount' => 10,
169 'billing_first_name' => 'Wonder',
170 'billing_last_name' => 'Woman',
171 'contactID' => $contact['id'],
172 'email' => 'wonderwoman@amazon.com',
173 );
174
175 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
176 $contact = $this->callAPISuccess('Contact', 'get', array(
177 'id' => $contact['id'],
178 'return' => array(
179 'first_name',
180 'last_name',
181 'sort_name',
182 'display_name',
183 ),
184 ));
185 $this->assertEquals(array(
186 'first_name' => 'Wonder',
187 'last_name' => 'Woman',
188 'display_name' => 'Wonder Woman',
189 'sort_name' => 'Woman, Wonder',
190 'id' => $contact['id'],
191 'contact_id' => $contact['id'],
192 ), $contact['values'][$contact['id']]);
193
194 }
195
196 /**
197 * Test form submission with billing first & last name where the contact does
198 * otherwise have one and should not be overwritten.
199 */
200 public function testSubmitNewBillingNameDoNotOverwrite() {
201 $this->setUpContributionPage();
202 $contact = $this->callAPISuccess('Contact', 'create', array(
203 'contact_type' => 'Individual',
204 'email' => 'wonderwoman@amazon.com',
205 'first_name' => 'Super',
206 'last_name' => 'Boy',
207 ));
208 $priceFieldID = reset($this->_ids['price_field']);
209 $priceFieldValueID = reset($this->_ids['price_field_value']);
210 $submitParams = array(
211 'price_' . $priceFieldID => $priceFieldValueID,
212 'id' => (int) $this->_ids['contribution_page'],
213 'amount' => 10,
214 'billing_first_name' => 'Wonder',
215 'billing_last_name' => 'Woman',
216 'contactID' => $contact['id'],
217 'email' => 'wonderwoman@amazon.com',
218 );
219
220 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
221 $contact = $this->callAPISuccess('Contact', 'get', array(
222 'id' => $contact['id'],
223 'return' => array(
224 'first_name',
225 'last_name',
226 'sort_name',
227 'display_name',
228 ),
229 ));
230 $this->assertEquals(array(
231 'first_name' => 'Super',
232 'last_name' => 'Boy',
233 'display_name' => 'Super Boy',
234 'sort_name' => 'Boy, Super',
235 'id' => $contact['id'],
236 'contact_id' => $contact['id'],
237 ), $contact['values'][$contact['id']]);
238
239 }
240
241 /**
242 * Test process with instant payment when more than one configured for the page.
243 *
244 * CRM-16923
245 */
246 public function testSubmitRecurMultiProcessorInstantPayment() {
247 $this->setUpContributionPage();
248 $this->setupPaymentProcessor();
249 $paymentProcessor2ID = $this->paymentProcessorCreate(array(
250 'payment_processor_type_id' => 'Dummy',
251 'name' => 'processor 2',
252 'class_name' => 'Payment_Dummy',
253 'billing_mode' => 1,
254 ));
255 $dummyPP = Civi\Payment\System::singleton()->getById($paymentProcessor2ID);
256 $dummyPP->setDoDirectPaymentResult(array(
257 'payment_status_id' => 1,
258 'trxn_id' => 'create_first_success',
259 'fee_amount' => .85,
260 ));
261 $processor = $dummyPP->getPaymentProcessor();
262 $this->callAPISuccess('ContributionPage', 'create', array(
263 'id' => $this->_ids['contribution_page'],
264 'payment_processor' => array($paymentProcessor2ID, $this->_ids['payment_processor']),
265 ));
266
267 $priceFieldID = reset($this->_ids['price_field']);
268 $priceFieldValueID = reset($this->_ids['price_field_value']);
269 $submitParams = array(
270 'price_' . $priceFieldID => $priceFieldValueID,
271 'id' => (int) $this->_ids['contribution_page'],
272 'amount' => 10,
273 'is_recur' => 1,
274 'frequency_interval' => 1,
275 'frequency_unit' => 'month',
276 'payment_processor_id' => $paymentProcessor2ID,
277 );
278
279 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
280 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
281 'contribution_page_id' => $this->_ids['contribution_page'],
282 'contribution_status_id' => 1,
283 ));
284 $this->assertEquals('create_first_success', $contribution['trxn_id']);
285 $this->assertEquals(10, $contribution['total_amount']);
286 $this->assertEquals(.85, $contribution['fee_amount']);
287 $this->assertEquals(9.15, $contribution['net_amount']);
288 $this->_checkFinancialRecords(array(
289 'id' => $contribution['id'],
290 'total_amount' => $contribution['total_amount'],
291 'payment_instrument_id' => $processor['payment_instrument_id'],
292 ), 'online');
293 }
294
295 /**
296 * Test submit with a membership block in place.
297 */
298 public function testSubmitMembershipBlockNotSeparatePayment() {
299 $this->setUpMembershipContributionPage();
300 $submitParams = array(
301 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
302 'id' => (int) $this->_ids['contribution_page'],
303 'amount' => 10,
304 'billing_first_name' => 'Billy',
305 'billing_middle_name' => 'Goat',
306 'billing_last_name' => 'Gruff',
307 'selectMembership' => $this->_ids['membership_type'],
308
309 );
310
311 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
312 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
313 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
314 $this->callAPISuccessGetSingle('LineItem', array('contribution_id' => $contribution['id'], 'entity_id' => $membershipPayment['id']));
315 }
316
317 /**
318 * Test submit with a membership block in place works with renewal.
319 */
320 public function testSubmitMembershipBlockNotSeparatePaymentProcessorInstantRenew() {
321 $this->setUpMembershipContributionPage();
322 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
323 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1));
324 $submitParams = array(
325 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
326 'id' => (int) $this->_ids['contribution_page'],
327 'amount' => 10,
328 'billing_first_name' => 'Billy',
329 'billing_middle_name' => 'Goat',
330 'billing_last_name' => 'Gruff',
331 'selectMembership' => $this->_ids['membership_type'],
332 'payment_processor_id' => 1,
333 'credit_card_number' => '4111111111111111',
334 'credit_card_type' => 'Visa',
335 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
336 'cvv2' => 123,
337 );
338
339 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
340 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
341 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
342 $this->callAPISuccessGetCount('LineItem', array(
343 'entity_table' => 'civicrm_membership',
344 'entity_id' => $membershipPayment['id'],
345 ), 1);
346
347 $submitParams['contact_id'] = $contribution['contact_id'];
348
349 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
350 $this->callAPISuccessGetCount('LineItem', array(
351 'entity_table' => 'civicrm_membership',
352 'entity_id' => $membershipPayment['id'],
353 ), 2);
354 $membership = $this->callAPISuccessGetSingle('Membership', array(
355 'id' => $membershipPayment['membership_id'],
356 'return' => array('end_date', 'join_date', 'start_date'),
357 ));
358 $this->assertEquals(date('Y-m-d'), $membership['start_date']);
359 $this->assertEquals(date('Y-m-d'), $membership['join_date']);
360 $this->assertEquals(date('Y-m-d', strtotime('+ 2 year - 1 day')), $membership['end_date']);
361 }
362
363 /**
364 * Test submit with a membership block in place.
365 */
366 public function testSubmitMembershipBlockNotSeparatePaymentWithEmail() {
367 $mut = new CiviMailUtils($this, TRUE);
368 $this->setUpMembershipContributionPage();
369 $this->addProfile('supporter_profile', $this->_ids['contribution_page']);
370
371 $submitParams = array(
372 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
373 'id' => (int) $this->_ids['contribution_page'],
374 'amount' => 10,
375 'billing_first_name' => 'Billy',
376 'billing_middle_name' => 'Goat',
377 'billing_last_name' => 'Gruff',
378 'selectMembership' => $this->_ids['membership_type'],
379 'email-Primary' => 'billy-goat@the-bridge.net',
380 'payment_processor_id' => $this->_paymentProcessor['id'],
381 'credit_card_number' => '4111111111111111',
382 'credit_card_type' => 'Visa',
383 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
384 'cvv2' => 123,
385 );
386
387 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
388 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
389 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
390 $mut->checkMailLog(array(
391 'Membership Type: General',
392 ));
393 $mut->stop();
394 $mut->clearMessages();
395 }
396
397 /**
398 * Test submit with a membership block in place.
399 */
400 public function testSubmitMembershipBlockNotSeparatePaymentZeroDollarsWithEmail() {
401 $mut = new CiviMailUtils($this, TRUE);
402 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 0)));
403 $this->setUpMembershipContributionPage();
404 $this->addProfile('supporter_profile', $this->_ids['contribution_page']);
405
406 $submitParams = array(
407 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
408 'id' => (int) $this->_ids['contribution_page'],
409 'amount' => 0,
410 'billing_first_name' => 'Billy',
411 'billing_middle_name' => 'Goat',
412 'billing_last_name' => 'Gruffier',
413 'selectMembership' => $this->_ids['membership_type'],
414 'email-Primary' => 'billy-goat@the-new-bridge.net',
415 'payment_processor_id' => $this->params['payment_processor_id'],
416 );
417
418 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
419 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
420 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
421 //Assert only one mail is being sent.
422 $msgs = $mut->getAllMessages();
423 $this->assertCount(1, $msgs);
424
425 $mut->checkMailLog(array(
426 'Membership Type: General',
427 'Gruffier',
428 ),
429 array(
430 'Amount',
431 )
432 );
433 $mut->stop();
434 $mut->clearMessages();
435 }
436
437 /**
438 * Test submit with a pay later abnd check line item in mails.
439 */
440 public function testSubmitMembershipBlockIsSeparatePaymentPayLaterWithEmail() {
441 $mut = new CiviMailUtils($this, TRUE);
442 $this->setUpMembershipContributionPage();
443 $submitParams = array(
444 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
445 'id' => (int) $this->_ids['contribution_page'],
446 'amount' => 10,
447 'billing_first_name' => 'Billy',
448 'billing_middle_name' => 'Goat',
449 'billing_last_name' => 'Gruff',
450 'is_pay_later' => 1,
451 'selectMembership' => $this->_ids['membership_type'],
452 'email-Primary' => 'billy-goat@the-bridge.net',
453 );
454
455 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
456 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
457 $this->callAPISuccess('membership_payment', 'getsingle', array('contribution_id' => $contribution['id']));
458 $mut->checkMailLog(array(
459 'Membership Amount -... $ 2.00',
460 ));
461 $mut->stop();
462 $mut->clearMessages();
463 }
464
465 /**
466 * Test submit with a membership block in place.
467 */
468 public function testSubmitMembershipBlockIsSeparatePayment() {
469 $this->setUpMembershipContributionPage(TRUE);
470 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
471 $submitParams = array(
472 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
473 'id' => (int) $this->_ids['contribution_page'],
474 'amount' => 10,
475 'billing_first_name' => 'Billy',
476 'billing_middle_name' => 'Goat',
477 'billing_last_name' => 'Gruff',
478 'selectMembership' => $this->_ids['membership_type'],
479 );
480
481 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
482 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
483 $this->assertCount(2, $contributions['values']);
484 $lines = $this->callAPISuccess('LineItem', 'get', array('sequential' => 1));
485 $this->assertEquals(10, $lines['values'][0]['line_total']);
486 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
487 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
488 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
489 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
490 }
491
492
493 /**
494 * Test submit with a membership block in place.
495 */
496 public function testSubmitMembershipBlockIsSeparatePaymentWithPayLater() {
497 $this->setUpMembershipContributionPage(TRUE);
498 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
499 //Pay later
500 $submitParams = array(
501 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
502 'id' => (int) $this->_ids['contribution_page'],
503 'amount' => 0,
504 'billing_first_name' => 'Billy',
505 'billing_middle_name' => 'Goat',
506 'billing_last_name' => 'Gruff',
507 'is_pay_later' => 1,
508 'selectMembership' => $this->_ids['membership_type'],
509 );
510
511 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
512 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
513 $this->assertCount(2, $contributions['values']);
514 foreach ($contributions['values'] as $val) {
515 $this->assertEquals('Pending', $val['contribution_status']);
516 }
517
518 //Membership should be in Pending state.
519 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
520 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
521 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
522 $pendingStatus = $this->callAPISuccessGetSingle('MembershipStatus', array(
523 'return' => array("id"),
524 'name' => "Pending",
525 ));
526 $this->assertEquals($membership['status_id'], $pendingStatus['id']);
527 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
528 }
529
530 /**
531 * Test submit with a membership block in place.
532 */
533 public function testSubmitMembershipBlockIsSeparatePaymentWithEmail() {
534 $mut = new CiviMailUtils($this, TRUE);
535 $this->setUpMembershipContributionPage(TRUE);
536 $this->addProfile('supporter_profile', $this->_ids['contribution_page']);
537
538 $submitParams = array(
539 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
540 'id' => (int) $this->_ids['contribution_page'],
541 'amount' => 10,
542 'billing_first_name' => 'Billy',
543 'billing_middle_name' => 'Goat',
544 'billing_last_name' => 'Gruff',
545 'selectMembership' => $this->_ids['membership_type'],
546 'email-Primary' => 'billy-goat@the-bridge.net',
547 'payment_processor_id' => $this->_paymentProcessor['id'],
548 'credit_card_number' => '4111111111111111',
549 'credit_card_type' => 'Visa',
550 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
551 'cvv2' => 123,
552 );
553
554 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
555 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
556 $this->assertCount(2, $contributions['values']);
557 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
558 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
559 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
560 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
561 // We should have two separate email messages, each with their own amount
562 // line and no total line.
563 $mut->checkAllMailLog(
564 array(
565 'Amount: $ 2.00',
566 'Amount: $ 10.00',
567 'Membership Fee',
568 ),
569 array(
570 'Total: $',
571 )
572 );
573 $mut->stop();
574 $mut->clearMessages();
575 }
576
577 /**
578 * Test submit with a membership block in place.
579 */
580 public function testSubmitMembershipBlockIsSeparatePaymentZeroDollarsPayLaterWithEmail() {
581 $mut = new CiviMailUtils($this, TRUE);
582 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 0)));
583 $this->setUpMembershipContributionPage(TRUE);
584 $this->addProfile('supporter_profile', $this->_ids['contribution_page']);
585
586 $submitParams = array(
587 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
588 'id' => (int) $this->_ids['contribution_page'],
589 'amount' => 0,
590 'billing_first_name' => 'Billy',
591 'billing_middle_name' => 'Goat',
592 'billing_last_name' => 'Gruffalo',
593 'selectMembership' => $this->_ids['membership_type'],
594 'payment_processor_id' => 0,
595 'email-Primary' => 'gruffalo@the-bridge.net',
596 );
597
598 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
599 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
600 $this->assertCount(2, $contributions['values']);
601 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
602 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
603 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
604 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
605 $mut->checkMailLog(array(
606 'Gruffalo',
607 'General Membership: $ 0.00',
608 'Membership Fee',
609 ));
610 $mut->stop();
611 $mut->clearMessages();
612 }
613
614 /**
615 * Test submit with a membership block in place.
616 */
617 public function testSubmitMembershipBlockTwoTypesIsSeparatePayment() {
618 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 6)));
619 $this->_ids['membership_type'][] = $this->membershipTypeCreate(array('name' => 'Student', 'minimum_fee' => 50));
620 $this->setUpMembershipContributionPage(TRUE);
621 $submitParams = array(
622 'price_' . $this->_ids['price_field'][0] => $this->_ids['price_field_value'][1],
623 'id' => (int) $this->_ids['contribution_page'],
624 'amount' => 10,
625 'billing_first_name' => 'Billy',
626 'billing_middle_name' => 'Goat',
627 'billing_last_name' => 'Gruff',
628 'selectMembership' => $this->_ids['membership_type'][1],
629 );
630
631 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
632 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
633 $this->assertCount(2, $contributions['values']);
634 $ids = array_keys($contributions['values']);
635 $this->assertEquals('10.00', $contributions['values'][$ids[0]]['total_amount']);
636 $this->assertEquals('50.00', $contributions['values'][$ids[1]]['total_amount']);
637 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
638 $this->assertArrayHasKey($membershipPayment['contribution_id'], $contributions['values']);
639 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
640 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
641 }
642
643 /**
644 * Test submit with a membership block in place.
645 *
646 * We are expecting a separate payment for the membership vs the contribution.
647 */
648 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNow() {
649 $mut = new CiviMailUtils($this, TRUE);
650 $this->setUpMembershipContributionPage(TRUE);
651 $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']);
652 $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
653 $submitParams = array(
654 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
655 'id' => (int) $this->_ids['contribution_page'],
656 'amount' => 10,
657 'billing_first_name' => 'Billy',
658 'billing_middle_name' => 'Goat',
659 'billing_last_name' => 'Gruff',
660 'email-Primary' => 'henry@8th.king',
661 'selectMembership' => $this->_ids['membership_type'],
662 'payment_processor_id' => $this->_paymentProcessor['id'],
663 'credit_card_number' => '4111111111111111',
664 'credit_card_type' => 'Visa',
665 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
666 'cvv2' => 123,
667 );
668
669 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
670 $contributions = $this->callAPISuccess('contribution', 'get', array(
671 'contribution_page_id' => $this->_ids['contribution_page'],
672 'contribution_status_id' => 1,
673 ));
674 $this->assertCount(2, $contributions['values']);
675 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
676 $this->assertTrue(in_array($membershipPayment['contribution_id'], array_keys($contributions['values'])));
677 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
678 $this->assertEquals($membership['contact_id'], $contributions['values'][$membershipPayment['contribution_id']]['contact_id']);
679 $lineItem = $this->callAPISuccessGetSingle('LineItem', array('entity_table' => 'civicrm_membership'));
680 $this->assertEquals($lineItem['entity_id'], $membership['id']);
681 $this->assertEquals($lineItem['contribution_id'], $membershipPayment['contribution_id']);
682 $this->assertEquals($lineItem['qty'], 1);
683 $this->assertEquals($lineItem['unit_price'], 2);
684 $this->assertEquals($lineItem['line_total'], 2);
685 foreach ($contributions['values'] as $contribution) {
686 $this->assertEquals(.72, $contribution['fee_amount']);
687 $this->assertEquals($contribution['total_amount'] - .72, $contribution['net_amount']);
688 }
689 // The total string is currently absent & it seems worse with - although at some point
690 // it may have been intended
691 $mut->checkAllMailLog(array('$ 2.00', 'Contribution Amount', '$ 10.00'), array('Total:'));
692 $mut->stop();
693 $mut->clearMessages();
694 }
695
696 /**
697 * Test submit with a membership block in place.
698 *
699 * Ensure a separate payment for the membership vs the contribution, with
700 * correct amounts.
701 */
702 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNowChargesCorrectAmounts() {
703 $this->setUpMembershipContributionPage(TRUE);
704 $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']);
705 $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
706 $test_uniq = uniqid();
707 $contributionPageAmount = 10;
708 $submitParams = array(
709 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
710 'id' => (int) $this->_ids['contribution_page'],
711 'amount' => $contributionPageAmount,
712 'billing_first_name' => 'Billy',
713 'billing_middle_name' => 'Goat',
714 'billing_last_name' => 'Gruff',
715 'email-Primary' => 'henry@8th.king',
716 'selectMembership' => $this->_ids['membership_type'],
717 'payment_processor_id' => $this->_paymentProcessor['id'],
718 'credit_card_number' => '4111111111111111',
719 'credit_card_type' => 'Visa',
720 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
721 'cvv2' => 123,
722 'TEST_UNIQ' => $test_uniq,
723 );
724
725 // set custom hook
726 $this->hookClass->setHook('civicrm_alterPaymentProcessorParams', array($this, 'hook_civicrm_alterPaymentProcessorParams'));
727
728 $this->callAPISuccess('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
729 $contributions = $this->callAPISuccess('contribution', 'get', array(
730 'contribution_page_id' => $this->_ids['contribution_page'],
731 'contribution_status_id' => 1,
732 ));
733
734 $result = civicrm_api3('SystemLog', 'get', array(
735 'sequential' => 1,
736 'message' => array('LIKE' => "%{$test_uniq}%"),
737 ));
738 $this->assertCount(2, $result['values'], "Expected exactly 2 log entries matching {$test_uniq}.");
739
740 // Examine logged entries to ensure correct values.
741 $contribution_ids = array();
742 $found_membership_amount = $found_contribution_amount = FALSE;
743 foreach ($result['values'] as $value) {
744 list($junk, $json) = explode("$test_uniq:", $value['message']);
745 $logged_contribution = json_decode($json, TRUE);
746 $contribution_ids[] = $logged_contribution['contributionID'];
747 if (!empty($logged_contribution['total_amount'])) {
748 $amount = $logged_contribution['total_amount'];
749 }
750 else {
751 $amount = $logged_contribution['amount'];
752 }
753
754 if ($amount == $this->_membershipBlockAmount) {
755 $found_membership_amount = TRUE;
756 }
757 if ($amount == $contributionPageAmount) {
758 $found_contribution_amount = TRUE;
759 }
760 }
761
762 $distinct_contribution_ids = array_unique($contribution_ids);
763 $this->assertCount(2, $distinct_contribution_ids, "Expected exactly 2 log contributions with distinct contributionIDs.");
764 $this->assertTrue($found_contribution_amount, "Expected one log contribution with amount '$contributionPageAmount' (the contribution page amount)");
765 $this->assertTrue($found_membership_amount, "Expected one log contribution with amount '$this->_membershipBlockAmount' (the membership amount)");
766
767 }
768
769 /**
770 * Test that when a transaction fails the pending contribution remains.
771 *
772 * An activity should also be created. CRM-16417.
773 */
774 public function testSubmitPaymentProcessorFailure() {
775 $this->setUpContributionPage();
776 $this->setupPaymentProcessor();
777 $this->createLoggedInUser();
778 $priceFieldID = reset($this->_ids['price_field']);
779 $priceFieldValueID = reset($this->_ids['price_field_value']);
780 $submitParams = array(
781 'price_' . $priceFieldID => $priceFieldValueID,
782 'id' => (int) $this->_ids['contribution_page'],
783 'amount' => 10,
784 'payment_processor_id' => 1,
785 'credit_card_number' => '4111111111111111',
786 'credit_card_type' => 'Visa',
787 'credit_card_exp_date' => array('M' => 9, 'Y' => 2008),
788 'cvv2' => 123,
789 );
790
791 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
792 $contribution = $this->callAPISuccessGetSingle('contribution', array(
793 'contribution_page_id' => $this->_ids['contribution_page'],
794 'contribution_status_id' => 'Failed',
795 ));
796
797 $this->callAPISuccessGetSingle('activity', array(
798 'source_record_id' => $contribution['id'],
799 'activity_type_id' => 'Failed Payment',
800 ));
801
802 }
803
804 /**
805 * Test submit recurring membership with immediate confirmation (IATS style).
806 *
807 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
808 * processor (IATS style - denoted by returning trxn_id)
809 * - the first creates a new membership, completed contribution, in progress recurring. Check these
810 * - create another - end date should be extended
811 */
812 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPayment() {
813 $this->params['is_recur'] = 1;
814 $this->params['recur_frequency_unit'] = 'month';
815 $this->setUpMembershipContributionPage();
816 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
817 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
818 $processor = $dummyPP->getPaymentProcessor();
819
820 $submitParams = array(
821 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
822 'id' => (int) $this->_ids['contribution_page'],
823 'amount' => 10,
824 'billing_first_name' => 'Billy',
825 'billing_middle_name' => 'Goat',
826 'billing_last_name' => 'Gruff',
827 'email' => 'billy@goat.gruff',
828 'selectMembership' => $this->_ids['membership_type'],
829 'payment_processor_id' => 1,
830 'credit_card_number' => '4111111111111111',
831 'credit_card_type' => 'Visa',
832 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
833 'cvv2' => 123,
834 'is_recur' => 1,
835 'frequency_interval' => 1,
836 'frequency_unit' => 'month',
837 );
838
839 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
840 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
841 'contribution_page_id' => $this->_ids['contribution_page'],
842 'contribution_status_id' => 1,
843 ));
844 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
845
846 $this->assertEquals('create_first_success', $contribution['trxn_id']);
847 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
848 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
849 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
850 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
851 $this->assertEquals(1, $membership['status_id']);
852 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
853
854 $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id'], 'entity_id' => $membership['id']));
855 //renew it with processor setting completed - should extend membership
856 $submitParams['contact_id'] = $contribution['contact_id'];
857 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
858 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
859 $this->callAPISuccess('contribution', 'getsingle', array(
860 'id' => array('NOT IN' => array($contribution['id'])),
861 'contribution_page_id' => $this->_ids['contribution_page'],
862 'contribution_status_id' => 1,
863 ));
864 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
865 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
866 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
867 $this->assertEquals($processor['payment_instrument_id'], $recurringContribution['payment_instrument_id']);
868 $this->assertEquals(5, $recurringContribution['contribution_status_id']);
869 }
870
871 /**
872 * Test submit recurring membership with immediate confirmation (IATS style).
873 *
874 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
875 * processor (IATS style - denoted by returning trxn_id)
876 * - the first creates a new membership, completed contribution, in progress recurring. Check these
877 * - create another - end date should be extended
878 */
879 public function testSubmitMembershipComplexNonPriceSetPaymentPaymentProcessorRecurInstantPayment() {
880 $this->params['is_recur'] = 1;
881 $this->params['recur_frequency_unit'] = 'month';
882 // Add a membership so membership & contribution are not both 1.
883 $preExistingMembershipID = $this->contactMembershipCreate(array('contact_id' => $this->contactIds[0]));
884 $this->setUpMembershipContributionPage();
885 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
886 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
887 $processor = $dummyPP->getPaymentProcessor();
888
889 $submitParams = array(
890 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
891 'price_' . $this->_ids['price_field']['cont'] => 88,
892 'id' => (int) $this->_ids['contribution_page'],
893 'amount' => 10,
894 'billing_first_name' => 'Billy',
895 'billing_middle_name' => 'Goat',
896 'billing_last_name' => 'Gruff',
897 'email' => 'billy@goat.gruff',
898 'selectMembership' => $this->_ids['membership_type'],
899 'payment_processor_id' => 1,
900 'credit_card_number' => '4111111111111111',
901 'credit_card_type' => 'Visa',
902 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
903 'cvv2' => 123,
904 'is_recur' => 1,
905 'frequency_interval' => 1,
906 'frequency_unit' => 'month',
907 );
908
909 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
910 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
911 'contribution_page_id' => $this->_ids['contribution_page'],
912 'contribution_status_id' => 1,
913 ));
914 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
915
916 $this->assertEquals('create_first_success', $contribution['trxn_id']);
917 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
918 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
919 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
920 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
921 $this->assertEquals(1, $membership['status_id']);
922 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
923
924 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $contribution['id']));
925 $this->assertEquals(2, $lines['count']);
926 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
927 $this->assertEquals($preExistingMembershipID + 1, $lines['values'][0]['entity_id']);
928 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
929 $this->assertEquals($contribution['id'], $lines['values'][1]['entity_id']);
930 $this->callAPISuccessGetSingle('MembershipPayment', array('contribution_id' => $contribution['id'], 'membership_id' => $preExistingMembershipID + 1));
931
932 //renew it with processor setting completed - should extend membership
933 $submitParams['contact_id'] = $contribution['contact_id'];
934 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
935 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
936 $renewContribution = $this->callAPISuccess('contribution', 'getsingle', array(
937 'id' => array('NOT IN' => array($contribution['id'])),
938 'contribution_page_id' => $this->_ids['contribution_page'],
939 'contribution_status_id' => 1,
940 ));
941 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $renewContribution['id']));
942 $this->assertEquals(2, $lines['count']);
943 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
944 $this->assertEquals($preExistingMembershipID + 1, $lines['values'][0]['entity_id']);
945 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
946 $this->assertEquals($renewContribution['id'], $lines['values'][1]['entity_id']);
947
948 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
949 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
950 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
951 $this->assertEquals($processor['payment_instrument_id'], $recurringContribution['payment_instrument_id']);
952 $this->assertEquals(5, $recurringContribution['contribution_status_id']);
953 }
954
955 /**
956 * Test submit recurring membership with immediate confirmation (IATS style).
957 *
958 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
959 * processor (IATS style - denoted by returning trxn_id)
960 * - the first creates a new membership, completed contribution, in progress recurring. Check these
961 * - create another - end date should be extended
962 */
963 public function testSubmitMembershipComplexPriceSetPaymentPaymentProcessorRecurInstantPayment() {
964 $this->params['is_recur'] = 1;
965 $this->params['recur_frequency_unit'] = 'month';
966 // Add a membership so membership & contribution are not both 1.
967 $preExistingMembershipID = $this->contactMembershipCreate(array('contact_id' => $this->contactIds[0]));
968 $this->createPriceSetWithPage();
969 $this->addSecondOrganizationMembershipToPriceSet();
970 $this->setupPaymentProcessor();
971
972 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
973 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
974 $processor = $dummyPP->getPaymentProcessor();
975
976 $submitParams = array(
977 'price_' . $this->_ids['price_field'][0] => $this->_ids['price_field_value']['cont'],
978 'price_' . $this->_ids['price_field']['org1'] => $this->_ids['price_field_value']['org1'],
979 'price_' . $this->_ids['price_field']['org2'] => $this->_ids['price_field_value']['org2'],
980 'id' => (int) $this->_ids['contribution_page'],
981 'amount' => 10,
982 'billing_first_name' => 'Billy',
983 'billing_middle_name' => 'Goat',
984 'billing_last_name' => 'Gruff',
985 'email' => 'billy@goat.gruff',
986 'selectMembership' => NULL,
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),
991 'cvv2' => 123,
992 'frequency_interval' => 1,
993 'frequency_unit' => 'month',
994 );
995
996 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
997 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
998 'contribution_page_id' => $this->_ids['contribution_page'],
999 'contribution_status_id' => 1,
1000 ));
1001 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
1002
1003 $this->assertEquals('create_first_success', $contribution['trxn_id']);
1004 $membershipPayments = $this->callAPISuccess('membership_payment', 'get', array(
1005 'sequential' => 1,
1006 'contribution_id' => $contribution['id'],
1007 ));
1008 $this->assertEquals(2, $membershipPayments['count']);
1009 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $contribution['id']));
1010 $this->assertEquals(3, $lines['count']);
1011 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
1012 $this->assertEquals($preExistingMembershipID + 1, $lines['values'][0]['entity_id']);
1013 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
1014 $this->assertEquals($contribution['id'], $lines['values'][1]['entity_id']);
1015 $this->assertEquals('civicrm_membership', $lines['values'][2]['entity_table']);
1016 $this->assertEquals($preExistingMembershipID + 2, $lines['values'][2]['entity_id']);
1017
1018 $this->callAPISuccessGetSingle('MembershipPayment', array('contribution_id' => $contribution['id'], 'membership_id' => $preExistingMembershipID + 1));
1019 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $preExistingMembershipID + 1));
1020
1021 //renew it with processor setting completed - should extend membership
1022 $submitParams['contact_id'] = $contribution['contact_id'];
1023 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
1024 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1025 $renewContribution = $this->callAPISuccess('contribution', 'getsingle', array(
1026 'id' => array('NOT IN' => array($contribution['id'])),
1027 'contribution_page_id' => $this->_ids['contribution_page'],
1028 'contribution_status_id' => 1,
1029 ));
1030 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $renewContribution['id']));
1031 $this->assertEquals(3, $lines['count']);
1032 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
1033 $this->assertEquals($preExistingMembershipID + 1, $lines['values'][0]['entity_id']);
1034 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
1035 $this->assertEquals($renewContribution['id'], $lines['values'][1]['entity_id']);
1036
1037 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $preExistingMembershipID + 1));
1038 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
1039 }
1040
1041 /**
1042 * Extend the price set with a second organisation's membership.
1043 */
1044 public function addSecondOrganizationMembershipToPriceSet() {
1045 $organization2ID = $this->organizationCreate();
1046 $membershipTypes = $this->callAPISuccess('MembershipType', 'get', array());
1047 $this->_ids['membership_type'] = array_keys($membershipTypes['values']);
1048 $this->_ids['membership_type']['org2'] = $this->membershipTypeCreate(array('contact_id' => $organization2ID, 'name' => 'Org 2'));
1049 $priceField = $this->callAPISuccess('PriceField', 'create', array(
1050 'price_set_id' => $this->_ids['price_set'],
1051 'html_type' => 'Radio',
1052 'name' => 'Org1 Price',
1053 'label' => 'Org1Price',
1054 ));
1055 $this->_ids['price_field']['org1'] = $priceField['id'];
1056
1057 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1058 'name' => 'org1 amount',
1059 'label' => 'org 1 Amount',
1060 'amount' => 2,
1061 'financial_type_id' => 'Member Dues',
1062 'format.only_id' => TRUE,
1063 'membership_type_id' => reset($this->_ids['membership_type']),
1064 'price_field_id' => $priceField['id'],
1065 ));
1066 $this->_ids['price_field_value']['org1'] = $priceFieldValue;
1067
1068 $priceField = $this->callAPISuccess('PriceField', 'create', array(
1069 'price_set_id' => $this->_ids['price_set'],
1070 'html_type' => 'Radio',
1071 'name' => 'Org2 Price',
1072 'label' => 'Org2Price',
1073 ));
1074 $this->_ids['price_field']['org2'] = $priceField['id'];
1075
1076 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1077 'name' => 'org2 amount',
1078 'label' => 'org 2 Amount',
1079 'amount' => 200,
1080 'financial_type_id' => 'Member Dues',
1081 'format.only_id' => TRUE,
1082 'membership_type_id' => $this->_ids['membership_type']['org2'],
1083 'price_field_id' => $priceField['id'],
1084 ));
1085 $this->_ids['price_field_value']['org2'] = $priceFieldValue;
1086
1087 }
1088
1089 /**
1090 * Test submit recurring membership with immediate confirmation (IATS style).
1091 *
1092 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
1093 * processor (IATS style - denoted by returning trxn_id)
1094 * - the first creates a new membership, completed contribution, in progress recurring. Check these
1095 * - create another - end date should be extended
1096 */
1097 public function testSubmitMembershipPriceSetPaymentPaymentProcessorSeparatePaymentRecurInstantPayment() {
1098
1099 $this->setUpMembershipContributionPage(TRUE);
1100 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
1101 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1102
1103 $submitParams = array(
1104 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1105 'id' => (int) $this->_ids['contribution_page'],
1106 'amount' => 10,
1107 'billing_first_name' => 'Billy',
1108 'billing_middle_name' => 'Goat',
1109 'billing_last_name' => 'Gruff',
1110 'email' => 'billy@goat.gruff',
1111 'selectMembership' => $this->_ids['membership_type'],
1112 'payment_processor_id' => 1,
1113 'credit_card_number' => '4111111111111111',
1114 'credit_card_type' => 'Visa',
1115 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1116 'cvv2' => 123,
1117 'is_recur' => 1,
1118 'auto_renew' => TRUE,
1119 'frequency_interval' => 1,
1120 'frequency_unit' => 'month',
1121 );
1122
1123 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
1124 $contribution = $this->callAPISuccess('contribution', 'get', array(
1125 'contribution_page_id' => $this->_ids['contribution_page'],
1126 'contribution_status_id' => 1,
1127 ));
1128
1129 $this->assertEquals(2, $contribution['count']);
1130 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
1131 $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1132 $this->assertNotEmpty($contribution['values'][$membershipPayment['contribution_id']]['contribution_recur_id']);
1133 $this->callAPISuccess('contribution_recur', 'getsingle', array());
1134 }
1135
1136 /**
1137 * Test submit recurring membership with delayed confirmation (Authorize.net style)
1138 * - we process 2 membership transactions against with a recurring contribution against a contribution page with a delayed
1139 * processor (Authorize.net style - denoted by NOT returning trxn_id)
1140 * - the first creates a pending membership, pending contribution, penging recurring. Check these
1141 * - complete the transaction
1142 * - create another - end date should NOT be extended
1143 */
1144 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurDelayed() {
1145 $this->params['is_recur'] = 1;
1146 $this->params['recur_frequency_unit'] = 'month';
1147 $this->setUpMembershipContributionPage();
1148 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
1149 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
1150 $this->membershipTypeCreate(array('name' => 'Student'));
1151
1152 // Add a contribution & a couple of memberships so the id will not be 1 & will differ from membership id.
1153 // This saves us from 'accidental success'.
1154 $this->contributionCreate(array('contact_id' => $this->contactIds[0]));
1155 $this->contactMembershipCreate(array('contact_id' => $this->contactIds[0]));
1156 $this->contactMembershipCreate(array('contact_id' => $this->contactIds[0], 'membership_type_id' => 'Student'));
1157
1158 $submitParams = array(
1159 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1160 'id' => (int) $this->_ids['contribution_page'],
1161 'amount' => 10,
1162 'billing_first_name' => 'Billy',
1163 'billing_middle_name' => 'Goat',
1164 'billing_last_name' => 'Gruff',
1165 'email' => 'billy@goat.gruff',
1166 'selectMembership' => $this->_ids['membership_type'],
1167 'payment_processor_id' => 1,
1168 'credit_card_number' => '4111111111111111',
1169 'credit_card_type' => 'Visa',
1170 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1171 'cvv2' => 123,
1172 'is_recur' => 1,
1173 'frequency_interval' => 1,
1174 'frequency_unit' => 'month',
1175 );
1176
1177 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
1178 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1179 'contribution_page_id' => $this->_ids['contribution_page'],
1180 'contribution_status_id' => 2,
1181 ));
1182
1183 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
1184 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
1185 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1186 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
1187 $this->assertEquals(5, $membership['status_id']);
1188
1189 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id']));
1190 $this->assertEquals('civicrm_membership', $line['entity_table']);
1191 $this->assertEquals($membership['id'], $line['entity_id']);
1192
1193 $this->callAPISuccess('contribution', 'completetransaction', array(
1194 'id' => $contribution['id'],
1195 'trxn_id' => 'ipn_called',
1196 'payment_processor_id' => $this->_paymentProcessor['id'],
1197 ));
1198 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id']));
1199 $this->assertEquals('civicrm_membership', $line['entity_table']);
1200 $this->assertEquals($membership['id'], $line['entity_id']);
1201
1202 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1203 //renew it with processor setting completed - should extend membership
1204 $submitParams = array_merge($submitParams, array(
1205 'contact_id' => $contribution['contact_id'],
1206 'is_recur' => 1,
1207 'frequency_interval' => 1,
1208 'frequency_unit' => 'month',
1209 )
1210 );
1211
1212 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
1213 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1214 $newContribution = $this->callAPISuccess('contribution', 'getsingle', array(
1215 'id' => array(
1216 'NOT IN' => array($contribution['id']),
1217 ),
1218 'contribution_page_id' => $this->_ids['contribution_page'],
1219 'contribution_status_id' => 2,
1220 )
1221 );
1222 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $newContribution['id']));
1223 $this->assertEquals('civicrm_membership', $line['entity_table']);
1224 $this->assertEquals($membership['id'], $line['entity_id']);
1225
1226 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1227 //no renewal as the date hasn't changed
1228 $this->assertEquals($membership['end_date'], $renewedMembership['end_date']);
1229 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $newContribution['contribution_recur_id']));
1230 $this->assertEquals(2, $recurringContribution['contribution_status_id']);
1231 }
1232
1233 /**
1234 * Test non-recur contribution with membership payment
1235 */
1236 public function testSubmitMembershipIsSeparatePaymentNotRecur() {
1237 //Create recur contribution page.
1238 $this->setUpMembershipContributionPage(TRUE, TRUE);
1239 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
1240 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1241
1242 //Sumbit payment with recur disabled.
1243 $submitParams = array(
1244 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1245 'id' => (int) $this->_ids['contribution_page'],
1246 'amount' => 10,
1247 'frequency_interval' => 1,
1248 'frequency_unit' => 'month',
1249 'billing_first_name' => 'Billy',
1250 'billing_middle_name' => 'Goat',
1251 'billing_last_name' => 'Gruff',
1252 'email' => 'billy@goat.gruff',
1253 'selectMembership' => $this->_ids['membership_type'],
1254 'payment_processor_id' => 1,
1255 'credit_card_number' => '4111111111111111',
1256 'credit_card_type' => 'Visa',
1257 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1258 'cvv2' => 123,
1259 );
1260
1261 //Assert if recur contribution is created.
1262 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1263 $recur = $this->callAPISuccess('contribution_recur', 'get', array());
1264 $this->assertEmpty($recur['count']);
1265 }
1266
1267
1268 /**
1269 * Set up membership contribution page.
1270 * @param bool $isSeparatePayment
1271 * @param bool $isRecur
1272 */
1273 public function setUpMembershipContributionPage($isSeparatePayment = FALSE, $isRecur = FALSE) {
1274 $this->setUpMembershipBlockPriceSet();
1275 $this->setupPaymentProcessor();
1276 $this->setUpContributionPage($isRecur);
1277
1278 $this->callAPISuccess('membership_block', 'create', array(
1279 'entity_id' => $this->_ids['contribution_page'],
1280 'entity_table' => 'civicrm_contribution_page',
1281 'is_required' => TRUE,
1282 'is_active' => TRUE,
1283 'is_separate_payment' => $isSeparatePayment,
1284 'membership_type_default' => $this->_ids['membership_type'],
1285 ));
1286 }
1287
1288 /**
1289 * Set up pledge block.
1290 */
1291 public function setUpPledgeBlock() {
1292 $params = array(
1293 'entity_table' => 'civicrm_contribution_page',
1294 'entity_id' => $this->_ids['contribution_page'],
1295 'pledge_frequency_unit' => 'week',
1296 'is_pledge_interval' => 0,
1297 'pledge_start_date' => json_encode(array('calendar_date' => date('Ymd', strtotime("+1 month")))),
1298 );
1299 $pledgeBlock = CRM_Pledge_BAO_PledgeBlock::create($params);
1300 $this->_ids['pledge_block_id'] = $pledgeBlock->id;
1301 }
1302
1303 /**
1304 * The default data set does not include a complete default membership price set - not quite sure why.
1305 *
1306 * This function ensures it exists & populates $this->_ids with it's data
1307 */
1308 public function setUpMembershipBlockPriceSet() {
1309 $this->_ids['price_set'][] = $this->callAPISuccess('price_set', 'getvalue', array(
1310 'name' => 'default_membership_type_amount',
1311 'return' => 'id',
1312 ));
1313 if (empty($this->_ids['membership_type'])) {
1314 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
1315 }
1316 $priceField = $this->callAPISuccess('price_field', 'create', array(
1317 'price_set_id' => reset($this->_ids['price_set']),
1318 'name' => 'membership_amount',
1319 'label' => 'Membership Amount',
1320 'html_type' => 'Radio',
1321 'sequential' => 1,
1322 ));
1323 $this->_ids['price_field'][] = $priceField['id'];
1324
1325 foreach ($this->_ids['membership_type'] as $membershipTypeID) {
1326 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1327 'name' => 'membership_amount',
1328 'label' => 'Membership Amount',
1329 'amount' => $this->_membershipBlockAmount,
1330 'financial_type_id' => 'Donation',
1331 'format.only_id' => TRUE,
1332 'membership_type_id' => $membershipTypeID,
1333 'price_field_id' => $priceField['id'],
1334 ));
1335 $this->_ids['price_field_value'][] = $priceFieldValue;
1336 }
1337 if (!empty($this->_ids['membership_type']['org2'])) {
1338 $priceField = $this->callAPISuccess('price_field', 'create', array(
1339 'price_set_id' => reset($this->_ids['price_set']),
1340 'name' => 'membership_org2',
1341 'label' => 'Membership Org2',
1342 'html_type' => 'Checkbox',
1343 'sequential' => 1,
1344 ));
1345 $this->_ids['price_field']['org2'] = $priceField['id'];
1346
1347 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1348 'name' => 'membership_org2',
1349 'label' => 'Membership org 2',
1350 'amount' => 55,
1351 'financial_type_id' => 'Member Dues',
1352 'format.only_id' => TRUE,
1353 'membership_type_id' => $this->_ids['membership_type']['org2'],
1354 'price_field_id' => $priceField['id'],
1355 ));
1356 $this->_ids['price_field_value']['org2'] = $priceFieldValue;
1357 }
1358 $priceField = $this->callAPISuccess('price_field', 'create', array(
1359 'price_set_id' => reset($this->_ids['price_set']),
1360 'name' => 'Contribution',
1361 'label' => 'Contribution',
1362 'html_type' => 'Text',
1363 'sequential' => 1,
1364 'is_enter_qty' => 1,
1365 ));
1366 $this->_ids['price_field']['cont'] = $priceField['id'];
1367 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1368 'name' => 'contribution',
1369 'label' => 'Give me money',
1370 'amount' => 88,
1371 'financial_type_id' => 'Donation',
1372 'format.only_id' => TRUE,
1373 'price_field_id' => $priceField['id'],
1374 ));
1375 $this->_ids['price_field_value'][] = $priceFieldValue;
1376 }
1377
1378 /**
1379 * Add text field other amount to the price set.
1380 */
1381 public function addOtherAmountFieldToMembershipPriceSet() {
1382 $this->_ids['price_field']['other_amount'] = $this->callAPISuccess('price_field', 'create', array(
1383 'price_set_id' => reset($this->_ids['price_set']),
1384 'name' => 'other_amount',
1385 'label' => 'Other Amount',
1386 'html_type' => 'Text',
1387 'format.only_id' => TRUE,
1388 'sequential' => 1,
1389 ));
1390 $this->_ids['price_field_value']['other_amount'] = $this->callAPISuccess('price_field_value', 'create', array(
1391 'financial_type_id' => 'Donation',
1392 'format.only_id' => TRUE,
1393 'label' => 'Other Amount',
1394 'amount' => 1,
1395 'price_field_id' => $this->_ids['price_field']['other_amount'],
1396 ));
1397 }
1398
1399 /**
1400 * Help function to set up contribution page with some defaults.
1401 * @param bool $isRecur
1402 */
1403 public function setUpContributionPage($isRecur = FALSE) {
1404 if ($isRecur) {
1405 $this->params['is_recur'] = 1;
1406 $this->params['recur_frequency_unit'] = 'month';
1407 }
1408 $contributionPageResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
1409 if (empty($this->_ids['price_set'])) {
1410 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams);
1411 $this->_ids['price_set'][] = $priceSet['id'];
1412 }
1413 $priceSetID = reset($this->_ids['price_set']);
1414 CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
1415
1416 if (empty($this->_ids['price_field'])) {
1417 $priceField = $this->callAPISuccess('price_field', 'create', array(
1418 'price_set_id' => $priceSetID,
1419 'label' => 'Goat Breed',
1420 'html_type' => 'Radio',
1421 ));
1422 $this->_ids['price_field'] = array($priceField['id']);
1423 }
1424 if (empty($this->_ids['price_field_value'])) {
1425 $this->callAPISuccess('price_field_value', 'create', array(
1426 'price_set_id' => $priceSetID,
1427 'price_field_id' => $priceField['id'],
1428 'label' => 'Long Haired Goat',
1429 'financial_type_id' => 'Donation',
1430 'amount' => 20,
1431 'non_deductible_amount' => 15,
1432 )
1433 );
1434 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1435 'price_set_id' => $priceSetID,
1436 'price_field_id' => $priceField['id'],
1437 'label' => 'Shoe-eating Goat',
1438 'financial_type_id' => 'Donation',
1439 'amount' => 10,
1440 'non_deductible_amount' => 5,
1441 )
1442 );
1443 $this->_ids['price_field_value'] = array($priceFieldValue['id']);
1444 }
1445 $this->_ids['contribution_page'] = $contributionPageResult['id'];
1446 }
1447
1448 public static function setUpBeforeClass() {
1449 // put stuff here that should happen before all tests in this unit
1450 }
1451
1452 public static function tearDownAfterClass() {
1453 $tablesToTruncate = array(
1454 'civicrm_contact',
1455 'civicrm_financial_type',
1456 'civicrm_contribution',
1457 'civicrm_contribution_page',
1458 );
1459 $unitTest = new CiviUnitTestCase();
1460 $unitTest->quickCleanup($tablesToTruncate);
1461 }
1462
1463 /**
1464 * Create a payment processor instance.
1465 */
1466 protected function setupPaymentProcessor() {
1467 $this->params['payment_processor_id'] = $this->_ids['payment_processor'] = $this->paymentProcessorCreate(array(
1468 'payment_processor_type_id' => 'Dummy',
1469 'class_name' => 'Payment_Dummy',
1470 'billing_mode' => 1,
1471 ));
1472 $this->_paymentProcessor = $this->callAPISuccess('payment_processor', 'getsingle', array('id' => $this->params['payment_processor_id']));
1473 }
1474
1475 /**
1476 * Test submit recurring pledge.
1477 *
1478 * - 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.
1479 */
1480 public function testSubmitPledgePaymentPaymentProcessorRecurFuturePayment() {
1481 $this->params['adjust_recur_start_date'] = TRUE;
1482 $this->params['is_pay_later'] = FALSE;
1483 $this->setUpContributionPage();
1484 $this->setUpPledgeBlock();
1485 $this->setupPaymentProcessor();
1486 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
1487 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1488
1489 $submitParams = array(
1490 'id' => (int) $this->_ids['contribution_page'],
1491 'amount' => 100,
1492 'billing_first_name' => 'Billy',
1493 'billing_middle_name' => 'Goat',
1494 'billing_last_name' => 'Gruff',
1495 'email' => 'billy@goat.gruff',
1496 'payment_processor_id' => 1,
1497 'credit_card_number' => '4111111111111111',
1498 'credit_card_type' => 'Visa',
1499 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1500 'cvv2' => 123,
1501 'pledge_frequency_interval' => 1,
1502 'pledge_frequency_unit' => 'week',
1503 'pledge_installments' => 3,
1504 'is_pledge' => TRUE,
1505 'pledge_block_id' => (int) $this->_ids['pledge_block_id'],
1506 );
1507
1508 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
1509
1510 // Check if contribution created.
1511 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1512 'contribution_page_id' => $this->_ids['contribution_page'],
1513 'contribution_status_id' => 'Completed', // Will be pending when actual payment processor is used (dummy processor does not support future payments).
1514 ));
1515
1516 $this->assertEquals('create_first_success', $contribution['trxn_id']);
1517
1518 // Check if pledge created.
1519 $pledge = $this->callAPISuccess('pledge', 'getsingle', array());
1520 $this->assertEquals(date('Ymd', strtotime($pledge['pledge_start_date'])), date('Ymd', strtotime("+1 month")));
1521 $this->assertEquals($pledge['pledge_amount'], 300.00);
1522
1523 // Check if pledge payments created.
1524 $params = array(
1525 'pledge_id' => $pledge['id'],
1526 );
1527 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1528 $this->assertEquals($pledgePayment['count'], 3);
1529 $this->assertEquals(date('Ymd', strtotime($pledgePayment['values'][1]['scheduled_date'])), date('Ymd', strtotime("+1 month")));
1530 $this->assertEquals($pledgePayment['values'][1]['scheduled_amount'], 100.00);
1531 $this->assertEquals($pledgePayment['values'][1]['status_id'], 1); // Will be pending when actual payment processor is used (dummy processor does not support future payments).
1532
1533 // Check contribution recur record.
1534 $recur = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
1535 $this->assertEquals(date('Ymd', strtotime($recur['start_date'])), date('Ymd', strtotime("+1 month")));
1536 $this->assertEquals($recur['amount'], 100.00);
1537 $this->assertEquals($recur['contribution_status_id'], 5); // In progress status.
1538 }
1539
1540 /**
1541 * Test submit pledge payment.
1542 *
1543 * - test submitting a pledge payment using contribution form.
1544 */
1545 public function testSubmitPledgePayment() {
1546 $this->testSubmitPledgePaymentPaymentProcessorRecurFuturePayment();
1547 $pledge = $this->callAPISuccess('pledge', 'getsingle', array());
1548 $params = array(
1549 'pledge_id' => $pledge['id'],
1550 );
1551 $submitParams = array(
1552 'id' => (int) $pledge['pledge_contribution_page_id'],
1553 'pledge_amount' => array(2 => 1),
1554 'billing_first_name' => 'Billy',
1555 'billing_middle_name' => 'Goat',
1556 'billing_last_name' => 'Gruff',
1557 'email' => 'billy@goat.gruff',
1558 'payment_processor_id' => 1,
1559 'credit_card_number' => '4111111111111111',
1560 'credit_card_type' => 'Visa',
1561 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1562 'cvv2' => 123,
1563 'pledge_id' => $pledge['id'],
1564 'cid' => $pledge['contact_id'],
1565 'contact_id' => $pledge['contact_id'],
1566 'amount' => 100.00,
1567 'is_pledge' => TRUE,
1568 'pledge_block_id' => $this->_ids['pledge_block_id'],
1569 );
1570 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1571 $this->assertEquals($pledgePayment['values'][2]['status_id'], 2);
1572
1573 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
1574
1575 // Check if contribution created.
1576 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1577 'contribution_page_id' => $pledge['pledge_contribution_page_id'],
1578 'contribution_status_id' => 'Completed',
1579 'contact_id' => $pledge['contact_id'],
1580 'contribution_recur_id' => array('IS NULL' => 1),
1581 ));
1582
1583 $this->assertEquals(100.00, $contribution['total_amount']);
1584 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1585 $this->assertEquals($pledgePayment['values'][2]['status_id'], 1, "This pledge payment should have been completed");
1586 $this->assertEquals($pledgePayment['values'][2]['contribution_id'], $contribution['id']);
1587 }
1588
1589 /**
1590 * Test form submission with multiple option price set.
1591 */
1592 public function testSubmitContributionPageWithPriceSet() {
1593 $this->_priceSetParams['is_quick_config'] = 0;
1594 $this->setUpContributionPage();
1595 $submitParams = array(
1596 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1597 'id' => (int) $this->_ids['contribution_page'],
1598 'amount' => 80,
1599 'first_name' => 'Billy',
1600 'last_name' => 'Gruff',
1601 'email' => 'billy@goat.gruff',
1602 'is_pay_later' => TRUE,
1603 );
1604 $this->addPriceFields($submitParams);
1605
1606 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1607 $contribution = $this->callAPISuccessGetSingle('contribution', array(
1608 'contribution_page_id' => $this->_ids['contribution_page'],
1609 'contribution_status_id' => 2,
1610 ));
1611 $this->callAPISuccessGetCount(
1612 'LineItem',
1613 array(
1614 'contribution_id' => $contribution['id'],
1615 ),
1616 3
1617 );
1618 }
1619
1620 /**
1621 * Function to add additional price fields to priceset.
1622 * @param array $params
1623 */
1624 public function addPriceFields(&$params) {
1625 $priceSetID = reset($this->_ids['price_set']);
1626 $priceField = $this->callAPISuccess('price_field', 'create', array(
1627 'price_set_id' => $priceSetID,
1628 'label' => 'Chicken Breed',
1629 'html_type' => 'CheckBox',
1630 ));
1631 $priceFieldValue1 = $this->callAPISuccess('price_field_value', 'create', array(
1632 'price_set_id' => $priceSetID,
1633 'price_field_id' => $priceField['id'],
1634 'label' => 'Shoe-eating chicken -1',
1635 'financial_type_id' => 'Donation',
1636 'amount' => 30,
1637 ));
1638 $priceFieldValue2 = $this->callAPISuccess('price_field_value', 'create', array(
1639 'price_set_id' => $priceSetID,
1640 'price_field_id' => $priceField['id'],
1641 'label' => 'Shoe-eating chicken -2',
1642 'financial_type_id' => 'Donation',
1643 'amount' => 40,
1644 ));
1645 $params['price_' . $priceField['id']] = array(
1646 $priceFieldValue1['id'] => 1,
1647 $priceFieldValue2['id'] => 1,
1648 );
1649 }
1650
1651 /**
1652 * Test Tax Amount is calculated properly when using PriceSet with Field Type = Text/Numeric Quantity
1653 */
1654 public function testSubmitContributionPageWithPriceSetQuantity() {
1655 $this->_priceSetParams['is_quick_config'] = 0;
1656 $this->enableTaxAndInvoicing();
1657 $financialType = $this->createFinancialType();
1658 $financialTypeId = $financialType['id'];
1659 // This function sets the Tax Rate at 10% - it currently has no way to pass Tax Rate into it - so let's work with 10%
1660 $financialAccount = $this->relationForFinancialTypeWithFinancialAccount($financialType['id'], 5);
1661
1662 $this->setUpContributionPage();
1663 $submitParams = array(
1664 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1665 'id' => (int) $this->_ids['contribution_page'],
1666 'first_name' => 'J',
1667 'last_name' => 'T',
1668 'email' => 'JT@ohcanada.ca',
1669 'is_pay_later' => TRUE,
1670 );
1671
1672 // Create PriceSet/PriceField
1673 $priceSetID = reset($this->_ids['price_set']);
1674 $priceField = $this->callAPISuccess('price_field', 'create', array(
1675 'price_set_id' => $priceSetID,
1676 'label' => 'Printing Rights',
1677 'html_type' => 'Text',
1678 ));
1679 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1680 'price_set_id' => $priceSetID,
1681 'price_field_id' => $priceField['id'],
1682 'label' => 'Printing Rights',
1683 'financial_type_id' => $financialTypeId,
1684 'amount' => '16.95',
1685 ));
1686 $priceFieldId = $priceField['id'];
1687
1688 // Set quantity for our test
1689 $submitParams['price_' . $priceFieldId] = 180;
1690
1691 // contribution_page submit requires amount and tax_amount - and that's ok we're not testing that - we're testing at the LineItem level
1692 $submitParams['amount'] = 180 * 16.95;
1693 // This is the correct Tax Amount - use it later to compare to what the CiviCRM Core came up with at the LineItem level
1694 $submitParams['tax_amount'] = 180 * 16.95 * 0.10;
1695
1696 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1697 $contribution = $this->callAPISuccessGetSingle('contribution', array(
1698 'contribution_page_id' => $this->_ids['contribution_page'],
1699 ));
1700
1701 // Retrieve the lineItem that belongs to the Printing Rights and check the tax_amount CiviCRM Core calculated for it
1702 $lineItem = $this->callAPISuccess('LineItem', 'get', array(
1703 'contribution_id' => $contribution['id'],
1704 'label' => 'Printing Rights',
1705 ));
1706 $lineItemId = $lineItem['id'];
1707 $lineItem_TaxAmount = round($lineItem['values'][$lineItemId]['tax_amount'], 2);
1708
1709 // Compare this to what it should be!
1710 $this->assertEquals($lineItem_TaxAmount, round($submitParams['tax_amount'], 2), 'Wrong Sales Tax Amount is calculated and stored.');
1711 }
1712
1713 public function hook_civicrm_alterPaymentProcessorParams($paymentObj, &$rawParams, &$cookedParams) {
1714 // Ensure total_amount are the same if they're both given.
1715 $total_amount = CRM_Utils_Array::value('total_amount', $rawParams);
1716 $amount = CRM_Utils_Array::value('amount', $rawParams);
1717 if (!empty($total_amount) && !empty($amount) && $total_amount != $amount) {
1718 throw new Exception("total_amount '$total_amount' and amount '$amount' differ.");
1719 }
1720
1721 // Log parameters for later debugging and testing.
1722 $message = __FUNCTION__ . ": {$rawParams['TEST_UNIQ']}:";
1723 $log_params = array_intersect_key($rawParams, array(
1724 'amount' => 1,
1725 'total_amount' => 1,
1726 'contributionID' => 1,
1727 ));
1728 $message .= json_encode($log_params);
1729 $log = new CRM_Utils_SystemLogger();
1730 $log->debug($message, $_REQUEST);
1731 }
1732
1733 }