Merge pull request #11642 from JKingsnorth/CRM-21739
[civicrm-core.git] / tests / phpunit / api / v3 / ContributionPageTest.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
81621fee 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
8c9251b3 6 | Copyright CiviCRM LLC (c) 2004-2018 |
6a488035
TO
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035 27
6a488035
TO
28/**
29 * Test APIv3 civicrm_contribute_recur* functions
30 *
6c6e6187
TO
31 * @package CiviCRM_APIv3
32 * @subpackage API_Contribution
acb109b7 33 * @group headless
6a488035 34 */
6a488035
TO
35class 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';
6c6e6187 42 protected $contribution_result = NULL;
f64a217a 43 protected $_priceSetParams = array();
15816cb4 44 protected $_membershipBlockAmount = 2;
f8fe0df6 45 /**
eceb18cc 46 * Payment processor details.
f8fe0df6 47 * @var array
48 */
49 protected $_paymentProcessor = array();
f64a217a
EM
50
51 /**
52 * @var array
16b10e64
CW
53 * - contribution_page
54 * - price_set
55 * - price_field
56 * - price_field_value
f64a217a
EM
57 */
58 protected $_ids = array();
59
b7c9bc4c 60
6a488035 61 public $DBResetRequired = TRUE;
5896d037 62
6a488035
TO
63 public function setUp() {
64 parent::setUp();
65 $this->contactIds[] = $this->individualCreate();
66 $this->params = array(
6a488035
TO
67 'title' => "Test Contribution Page",
68 'financial_type_id' => 1,
69 'currency' => 'NZD',
70 'goal_amount' => $this->testAmount,
6cdac50b 71 'is_pay_later' => 1,
f64a217a 72 'is_monetary' => TRUE,
dd1b539a 73 'is_email_receipt' => TRUE,
74 'receipt_from_email' => 'yourconscience@donate.com',
75 'receipt_from_name' => 'Ego Freud',
f64a217a
EM
76 );
77
78 $this->_priceSetParams = array(
79 'is_quick_config' => 1,
80 'extends' => 'CiviContribute',
81 'financial_type_id' => 'Donation',
21dfd5f5 82 'title' => 'my Page',
6a488035
TO
83 );
84 }
85
00be9182 86 public function tearDown() {
6a488035 87 foreach ($this->contactIds as $id) {
fc928539 88 $this->callAPISuccess('contact', 'delete', array('id' => $id));
611c7ece 89 }
f9342903 90 $this->quickCleanUpFinancialEntities();
6a488035
TO
91 }
92
93 public function testCreateContributionPage() {
fc928539 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']);
6a488035
TO
97 $this->getAndCheck($this->params, $result['id'], $this->_entity);
98 }
99
100 public function testGetBasicContributionPage() {
fc928539 101 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
6a488035 102 $this->id = $createResult['id'];
6a488035 103 $getParams = array(
6a488035
TO
104 'currency' => 'NZD',
105 'financial_type_id' => 1,
106 );
fc928539 107 $getResult = $this->callAPIAndDocument($this->_entity, 'get', $getParams, __FUNCTION__, __FILE__);
108 $this->assertEquals(1, $getResult['count']);
6a488035
TO
109 }
110
111 public function testGetContributionPageByAmount() {
fc928539 112 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
6a488035 113 $this->id = $createResult['id'];
6a488035 114 $getParams = array(
5896d037 115 'amount' => '' . $this->testAmount, // 3456
6a488035
TO
116 'currency' => 'NZD',
117 'financial_type_id' => 1,
118 );
5be22f39 119 $getResult = $this->callAPISuccess($this->_entity, 'get', $getParams);
fc928539 120 $this->assertEquals(1, $getResult['count']);
6a488035
TO
121 }
122
123 public function testDeleteContributionPage() {
fc928539 124 $createResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
125 $deleteParams = array('id' => $createResult['id']);
611c7ece 126 $this->callAPIAndDocument($this->_entity, 'delete', $deleteParams, __FUNCTION__, __FILE__);
fc928539 127 $checkDeleted = $this->callAPISuccess($this->_entity, 'get', array());
128 $this->assertEquals(0, $checkDeleted['count']);
6a488035
TO
129 }
130
131 public function testGetFieldsContributionPage() {
fc928539 132 $result = $this->callAPISuccess($this->_entity, 'getfields', array('action' => 'create'));
6a488035
TO
133 $this->assertEquals(12, $result['values']['start_date']['type']);
134 }
135
be26f3e0 136
d58b453e 137 /**
eceb18cc 138 * Test form submission with basic price set.
d58b453e 139 */
be26f3e0 140 public function testSubmit() {
f64a217a
EM
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'],
21dfd5f5 147 'amount' => 10,
be26f3e0
EM
148 );
149
f64a217a 150 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
05465712 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']);
f64a217a 154 }
5896d037 155
870156d6 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
7bc789a5 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();
d27635dc 249 $paymentProcessor2ID = $this->paymentProcessorCreate(array(
7bc789a5 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);
70cc8754 256 $dummyPP->setDoDirectPaymentResult(array(
257 'payment_status_id' => 1,
258 'trxn_id' => 'create_first_success',
259 'fee_amount' => .85,
260 ));
f69a9ac3 261 $processor = $dummyPP->getPaymentProcessor();
7bc789a5 262 $this->callAPISuccess('ContributionPage', 'create', array(
d27635dc 263 'id' => $this->_ids['contribution_page'],
264 'payment_processor' => array($paymentProcessor2ID, $this->_ids['payment_processor']),
7bc789a5 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);
70cc8754 280 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
7bc789a5 281 'contribution_page_id' => $this->_ids['contribution_page'],
282 'contribution_status_id' => 1,
283 ));
70cc8754 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']);
bf722049 288 $this->_checkFinancialRecords(array(
289 'id' => $contribution['id'],
290 'total_amount' => $contribution['total_amount'],
f69a9ac3 291 'payment_instrument_id' => $processor['payment_instrument_id'],
bf722049 292 ), 'online');
7bc789a5 293 }
70cc8754 294
f64a217a 295 /**
eceb18cc 296 * Test submit with a membership block in place.
f64a217a 297 */
f9342903
EM
298 public function testSubmitMembershipBlockNotSeparatePayment() {
299 $this->setUpMembershipContributionPage();
f64a217a 300 $submitParams = array(
3e28c791 301 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
f9342903 302 'id' => (int) $this->_ids['contribution_page'],
f64a217a
EM
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
a828d7b8 311 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
f9342903 312 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('contribution_page_id' => $this->_ids['contribution_page']));
840e3907 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']));
f9342903
EM
315 }
316
e04004fa 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
2f59d010 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',
9daadfce 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,
2f59d010 385 );
386
5be22f39 387 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
2f59d010 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',
18135422 415 'payment_processor_id' => $this->params['payment_processor_id'],
2f59d010 416 );
417
5be22f39 418 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
2f59d010 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']));
e2e2be86
JP
421 //Assert only one mail is being sent.
422 $msgs = $mut->getAllMessages();
423 $this->assertCount(1, $msgs);
424
2f59d010 425 $mut->checkMailLog(array(
426 'Membership Type: General',
427 'Gruffier',
428 ),
429 array(
430 'Amount',
431 )
432 );
433 $mut->stop();
b13d4a61 434 $mut->clearMessages();
2f59d010 435 }
436
337be0d2
JP
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
f9342903 465 /**
eceb18cc 466 * Test submit with a membership block in place.
f9342903
EM
467 */
468 public function testSubmitMembershipBlockIsSeparatePayment() {
469 $this->setUpMembershipContributionPage(TRUE);
9daadfce 470 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
f9342903 471 $submitParams = array(
3e28c791 472 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
f9342903
EM
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
5be22f39 481 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
f9342903
EM
482 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
483 $this->assertCount(2, $contributions['values']);
8e8d287f 484 $lines = $this->callAPISuccess('LineItem', 'get', array('sequential' => 1));
485 $this->assertEquals(10, $lines['values'][0]['line_total']);
f9342903
EM
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 }
f64a217a 491
50d7b4ea
JP
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
2f59d010 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',
9daadfce 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,
2f59d010 552 );
553
5be22f39 554 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
2f59d010 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']);
de6a9861
JM
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(
c2ce0c2a 570 'Total: $',
461ae02f 571 )
de6a9861 572 );
2f59d010 573 $mut->stop();
b13d4a61 574 $mut->clearMessages();
2f59d010 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
797c4f88 614 /**
eceb18cc 615 * Test submit with a membership block in place.
797c4f88
EM
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
a828d7b8 631 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
6c6e6187 632 $contributions = $this->callAPISuccess('contribution', 'get', array('contribution_page_id' => $this->_ids['contribution_page']));
797c4f88
EM
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
8ca611b7 643 /**
eceb18cc 644 * Test submit with a membership block in place.
329f3f66
EM
645 *
646 * We are expecting a separate payment for the membership vs the contribution.
8ca611b7 647 */
329f3f66 648 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNow() {
58d8c7e2 649 $mut = new CiviMailUtils($this, TRUE);
8ca611b7 650 $this->setUpMembershipContributionPage(TRUE);
0193ebdb 651 $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']);
652 $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
8ca611b7
EM
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',
58d8c7e2 660 'email-Primary' => 'henry@8th.king',
8ca611b7 661 'selectMembership' => $this->_ids['membership_type'],
7bc789a5 662 'payment_processor_id' => $this->_paymentProcessor['id'],
8ca611b7
EM
663 'credit_card_number' => '4111111111111111',
664 'credit_card_type' => 'Visa',
5896d037 665 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
8ca611b7
EM
666 'cvv2' => 123,
667 );
05990684 668
a828d7b8 669 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
5896d037 670 $contributions = $this->callAPISuccess('contribution', 'get', array(
92915c55
TO
671 'contribution_page_id' => $this->_ids['contribution_page'],
672 'contribution_status_id' => 1,
673 ));
8ca611b7
EM
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']);
f2bca231 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);
0193ebdb 685 foreach ($contributions['values'] as $contribution) {
686 $this->assertEquals(.72, $contribution['fee_amount']);
687 $this->assertEquals($contribution['total_amount'] - .72, $contribution['net_amount']);
688 }
58d8c7e2 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();
8ca611b7 694 }
f8fe0df6 695
15816cb4
AS
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.
83644f47 701 *
702 * @param string $thousandSeparator
703 * punctuation used to refer to thousands.
704 *
705 * @dataProvider getThousandSeparators
15816cb4 706 */
83644f47 707 public function testSubmitMembershipBlockIsSeparatePaymentPaymentProcessorNowChargesCorrectAmounts($thousandSeparator) {
708 $this->setCurrencySeparators($thousandSeparator);
15816cb4
AS
709 $this->setUpMembershipContributionPage(TRUE);
710 $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']);
711 $processor->setDoDirectPaymentResult(array('fee_amount' => .72));
712 $test_uniq = uniqid();
713 $contributionPageAmount = 10;
714 $submitParams = array(
715 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
716 'id' => (int) $this->_ids['contribution_page'],
717 'amount' => $contributionPageAmount,
718 'billing_first_name' => 'Billy',
719 'billing_middle_name' => 'Goat',
720 'billing_last_name' => 'Gruff',
721 'email-Primary' => 'henry@8th.king',
722 'selectMembership' => $this->_ids['membership_type'],
723 'payment_processor_id' => $this->_paymentProcessor['id'],
724 'credit_card_number' => '4111111111111111',
725 'credit_card_type' => 'Visa',
726 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
727 'cvv2' => 123,
728 'TEST_UNIQ' => $test_uniq,
729 );
730
731 // set custom hook
732 $this->hookClass->setHook('civicrm_alterPaymentProcessorParams', array($this, 'hook_civicrm_alterPaymentProcessorParams'));
733
734 $this->callAPISuccess('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
735 $contributions = $this->callAPISuccess('contribution', 'get', array(
736 'contribution_page_id' => $this->_ids['contribution_page'],
737 'contribution_status_id' => 1,
738 ));
739
740 $result = civicrm_api3('SystemLog', 'get', array(
741 'sequential' => 1,
742 'message' => array('LIKE' => "%{$test_uniq}%"),
743 ));
744 $this->assertCount(2, $result['values'], "Expected exactly 2 log entries matching {$test_uniq}.");
745
746 // Examine logged entries to ensure correct values.
747 $contribution_ids = array();
748 $found_membership_amount = $found_contribution_amount = FALSE;
749 foreach ($result['values'] as $value) {
750 list($junk, $json) = explode("$test_uniq:", $value['message']);
751 $logged_contribution = json_decode($json, TRUE);
752 $contribution_ids[] = $logged_contribution['contributionID'];
753 if (!empty($logged_contribution['total_amount'])) {
754 $amount = $logged_contribution['total_amount'];
755 }
756 else {
757 $amount = $logged_contribution['amount'];
758 }
759
760 if ($amount == $this->_membershipBlockAmount) {
761 $found_membership_amount = TRUE;
762 }
763 if ($amount == $contributionPageAmount) {
764 $found_contribution_amount = TRUE;
765 }
766 }
767
768 $distinct_contribution_ids = array_unique($contribution_ids);
769 $this->assertCount(2, $distinct_contribution_ids, "Expected exactly 2 log contributions with distinct contributionIDs.");
770 $this->assertTrue($found_contribution_amount, "Expected one log contribution with amount '$contributionPageAmount' (the contribution page amount)");
771 $this->assertTrue($found_membership_amount, "Expected one log contribution with amount '$this->_membershipBlockAmount' (the membership amount)");
15816cb4
AS
772 }
773
5961bd47
EM
774 /**
775 * Test that when a transaction fails the pending contribution remains.
776 *
777 * An activity should also be created. CRM-16417.
778 */
779 public function testSubmitPaymentProcessorFailure() {
780 $this->setUpContributionPage();
781 $this->setupPaymentProcessor();
782 $this->createLoggedInUser();
783 $priceFieldID = reset($this->_ids['price_field']);
784 $priceFieldValueID = reset($this->_ids['price_field_value']);
785 $submitParams = array(
786 'price_' . $priceFieldID => $priceFieldValueID,
787 'id' => (int) $this->_ids['contribution_page'],
788 'amount' => 10,
789 'payment_processor_id' => 1,
790 'credit_card_number' => '4111111111111111',
791 'credit_card_type' => 'Visa',
792 'credit_card_exp_date' => array('M' => 9, 'Y' => 2008),
793 'cvv2' => 123,
794 );
795
796 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
797 $contribution = $this->callAPISuccessGetSingle('contribution', array(
798 'contribution_page_id' => $this->_ids['contribution_page'],
382c5680 799 'contribution_status_id' => 'Failed',
5961bd47
EM
800 ));
801
802 $this->callAPISuccessGetSingle('activity', array(
803 'source_record_id' => $contribution['id'],
804 'activity_type_id' => 'Failed Payment',
805 ));
806
5961bd47
EM
807 }
808
f8fe0df6 809 /**
dd1b539a 810 * Test submit recurring membership with immediate confirmation (IATS style).
811 *
e6fa4056 812 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
1fee3ad2 813 * processor (IATS style - denoted by returning trxn_id)
e6fa4056
EM
814 * - the first creates a new membership, completed contribution, in progress recurring. Check these
815 * - create another - end date should be extended
f8fe0df6 816 */
8bef2e38 817 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurInstantPayment() {
f8fe0df6 818 $this->params['is_recur'] = 1;
f8fe0df6 819 $this->params['recur_frequency_unit'] = 'month';
820 $this->setUpMembershipContributionPage();
7c85dc65 821 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
8bef2e38 822 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
f69a9ac3 823 $processor = $dummyPP->getPaymentProcessor();
f8fe0df6 824
825 $submitParams = array(
826 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
827 'id' => (int) $this->_ids['contribution_page'],
828 'amount' => 10,
829 'billing_first_name' => 'Billy',
830 'billing_middle_name' => 'Goat',
831 'billing_last_name' => 'Gruff',
832 'email' => 'billy@goat.gruff',
833 'selectMembership' => $this->_ids['membership_type'],
579a1fb7 834 'payment_processor_id' => 1,
f8fe0df6 835 'credit_card_number' => '4111111111111111',
836 'credit_card_type' => 'Visa',
5896d037 837 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
f8fe0df6 838 'cvv2' => 123,
839 'is_recur' => 1,
840 'frequency_interval' => 1,
841 'frequency_unit' => 'month',
842 );
843
a828d7b8 844 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
5896d037 845 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
92915c55
TO
846 'contribution_page_id' => $this->_ids['contribution_page'],
847 'contribution_status_id' => 1,
848 ));
f69a9ac3 849 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
0739d6cf 850
851 $this->assertEquals('create_first_success', $contribution['trxn_id']);
f8fe0df6 852 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
853 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
854 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
855 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
856 $this->assertEquals(1, $membership['status_id']);
857 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
356bfcaf 858
859 $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id'], 'entity_id' => $membership['id']));
f8fe0df6 860 //renew it with processor setting completed - should extend membership
861 $submitParams['contact_id'] = $contribution['contact_id'];
8bef2e38 862 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
f8fe0df6 863 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
5896d037 864 $this->callAPISuccess('contribution', 'getsingle', array(
92915c55
TO
865 'id' => array('NOT IN' => array($contribution['id'])),
866 'contribution_page_id' => $this->_ids['contribution_page'],
867 'contribution_status_id' => 1,
868 ));
f8fe0df6 869 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
870 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
871 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
f69a9ac3 872 $this->assertEquals($processor['payment_instrument_id'], $recurringContribution['payment_instrument_id']);
91259407 873 $this->assertEquals(5, $recurringContribution['contribution_status_id']);
f8fe0df6 874 }
875
13a16f43 876 /**
877 * Test submit recurring membership with immediate confirmation (IATS style).
878 *
879 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
880 * processor (IATS style - denoted by returning trxn_id)
881 * - the first creates a new membership, completed contribution, in progress recurring. Check these
882 * - create another - end date should be extended
883 */
65e172a3 884 public function testSubmitMembershipComplexNonPriceSetPaymentPaymentProcessorRecurInstantPayment() {
13a16f43 885 $this->params['is_recur'] = 1;
886 $this->params['recur_frequency_unit'] = 'month';
887 // Add a membership so membership & contribution are not both 1.
888 $preExistingMembershipID = $this->contactMembershipCreate(array('contact_id' => $this->contactIds[0]));
889 $this->setUpMembershipContributionPage();
890 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
891 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
892 $processor = $dummyPP->getPaymentProcessor();
893
894 $submitParams = array(
895 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
896 'price_' . $this->_ids['price_field']['cont'] => 88,
897 'id' => (int) $this->_ids['contribution_page'],
898 'amount' => 10,
899 'billing_first_name' => 'Billy',
900 'billing_middle_name' => 'Goat',
901 'billing_last_name' => 'Gruff',
902 'email' => 'billy@goat.gruff',
903 'selectMembership' => $this->_ids['membership_type'],
904 'payment_processor_id' => 1,
905 'credit_card_number' => '4111111111111111',
906 'credit_card_type' => 'Visa',
907 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
908 'cvv2' => 123,
909 'is_recur' => 1,
910 'frequency_interval' => 1,
911 'frequency_unit' => 'month',
912 );
913
914 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
915 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
916 'contribution_page_id' => $this->_ids['contribution_page'],
917 'contribution_status_id' => 1,
918 ));
919 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
920
921 $this->assertEquals('create_first_success', $contribution['trxn_id']);
922 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
923 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
924 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
925 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
926 $this->assertEquals(1, $membership['status_id']);
927 $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
928
929 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $contribution['id']));
930 $this->assertEquals(2, $lines['count']);
931 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
932 $this->assertEquals($preExistingMembershipID + 1, $lines['values'][0]['entity_id']);
933 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
934 $this->assertEquals($contribution['id'], $lines['values'][1]['entity_id']);
935 $this->callAPISuccessGetSingle('MembershipPayment', array('contribution_id' => $contribution['id'], 'membership_id' => $preExistingMembershipID + 1));
936
937 //renew it with processor setting completed - should extend membership
938 $submitParams['contact_id'] = $contribution['contact_id'];
939 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
940 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
941 $renewContribution = $this->callAPISuccess('contribution', 'getsingle', array(
942 'id' => array('NOT IN' => array($contribution['id'])),
943 'contribution_page_id' => $this->_ids['contribution_page'],
944 'contribution_status_id' => 1,
945 ));
946 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $renewContribution['id']));
947 $this->assertEquals(2, $lines['count']);
948 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
949 $this->assertEquals($preExistingMembershipID + 1, $lines['values'][0]['entity_id']);
950 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
951 $this->assertEquals($renewContribution['id'], $lines['values'][1]['entity_id']);
952
953 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
954 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
955 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
956 $this->assertEquals($processor['payment_instrument_id'], $recurringContribution['payment_instrument_id']);
957 $this->assertEquals(5, $recurringContribution['contribution_status_id']);
958 }
959
65e172a3 960 /**
961 * Test submit recurring membership with immediate confirmation (IATS style).
962 *
963 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
964 * processor (IATS style - denoted by returning trxn_id)
965 * - the first creates a new membership, completed contribution, in progress recurring. Check these
966 * - create another - end date should be extended
967 */
968 public function testSubmitMembershipComplexPriceSetPaymentPaymentProcessorRecurInstantPayment() {
969 $this->params['is_recur'] = 1;
970 $this->params['recur_frequency_unit'] = 'month';
971 // Add a membership so membership & contribution are not both 1.
972 $preExistingMembershipID = $this->contactMembershipCreate(array('contact_id' => $this->contactIds[0]));
973 $this->createPriceSetWithPage();
974 $this->addSecondOrganizationMembershipToPriceSet();
975 $this->setupPaymentProcessor();
976
977 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
978 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
979 $processor = $dummyPP->getPaymentProcessor();
980
981 $submitParams = array(
982 'price_' . $this->_ids['price_field'][0] => $this->_ids['price_field_value']['cont'],
983 'price_' . $this->_ids['price_field']['org1'] => $this->_ids['price_field_value']['org1'],
984 'price_' . $this->_ids['price_field']['org2'] => $this->_ids['price_field_value']['org2'],
985 'id' => (int) $this->_ids['contribution_page'],
986 'amount' => 10,
987 'billing_first_name' => 'Billy',
988 'billing_middle_name' => 'Goat',
989 'billing_last_name' => 'Gruff',
990 'email' => 'billy@goat.gruff',
991 'selectMembership' => NULL,
992 'payment_processor_id' => 1,
993 'credit_card_number' => '4111111111111111',
994 'credit_card_type' => 'Visa',
995 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
996 'cvv2' => 123,
997 'frequency_interval' => 1,
998 'frequency_unit' => 'month',
999 );
1000
1001 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
1002 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1003 'contribution_page_id' => $this->_ids['contribution_page'],
1004 'contribution_status_id' => 1,
1005 ));
1006 $this->assertEquals($processor['payment_instrument_id'], $contribution['payment_instrument_id']);
1007
1008 $this->assertEquals('create_first_success', $contribution['trxn_id']);
1009 $membershipPayments = $this->callAPISuccess('membership_payment', 'get', array(
1010 'sequential' => 1,
1011 'contribution_id' => $contribution['id'],
1012 ));
1013 $this->assertEquals(2, $membershipPayments['count']);
1014 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $contribution['id']));
1015 $this->assertEquals(3, $lines['count']);
1016 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
1017 $this->assertEquals($preExistingMembershipID + 1, $lines['values'][0]['entity_id']);
1018 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
1019 $this->assertEquals($contribution['id'], $lines['values'][1]['entity_id']);
1020 $this->assertEquals('civicrm_membership', $lines['values'][2]['entity_table']);
1021 $this->assertEquals($preExistingMembershipID + 2, $lines['values'][2]['entity_id']);
1022
1023 $this->callAPISuccessGetSingle('MembershipPayment', array('contribution_id' => $contribution['id'], 'membership_id' => $preExistingMembershipID + 1));
1024 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $preExistingMembershipID + 1));
1025
1026 //renew it with processor setting completed - should extend membership
1027 $submitParams['contact_id'] = $contribution['contact_id'];
1028 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_second_success'));
1029 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1030 $renewContribution = $this->callAPISuccess('contribution', 'getsingle', array(
1031 'id' => array('NOT IN' => array($contribution['id'])),
1032 'contribution_page_id' => $this->_ids['contribution_page'],
1033 'contribution_status_id' => 1,
1034 ));
1035 $lines = $this->callAPISuccess('line_item', 'get', array('sequential' => 1, 'contribution_id' => $renewContribution['id']));
1036 $this->assertEquals(3, $lines['count']);
1037 $this->assertEquals('civicrm_membership', $lines['values'][0]['entity_table']);
1038 $this->assertEquals($preExistingMembershipID + 1, $lines['values'][0]['entity_id']);
1039 $this->assertEquals('civicrm_contribution', $lines['values'][1]['entity_table']);
1040 $this->assertEquals($renewContribution['id'], $lines['values'][1]['entity_id']);
1041
1042 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $preExistingMembershipID + 1));
1043 $this->assertEquals(date('Y-m-d', strtotime('+ 1 year', strtotime($membership['end_date']))), $renewedMembership['end_date']);
1044 }
1045
1046 /**
1047 * Extend the price set with a second organisation's membership.
1048 */
1049 public function addSecondOrganizationMembershipToPriceSet() {
1050 $organization2ID = $this->organizationCreate();
1051 $membershipTypes = $this->callAPISuccess('MembershipType', 'get', array());
1052 $this->_ids['membership_type'] = array_keys($membershipTypes['values']);
1053 $this->_ids['membership_type']['org2'] = $this->membershipTypeCreate(array('contact_id' => $organization2ID, 'name' => 'Org 2'));
1054 $priceField = $this->callAPISuccess('PriceField', 'create', array(
1055 'price_set_id' => $this->_ids['price_set'],
1056 'html_type' => 'Radio',
1057 'name' => 'Org1 Price',
1058 'label' => 'Org1Price',
1059 ));
1060 $this->_ids['price_field']['org1'] = $priceField['id'];
1061
1062 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1063 'name' => 'org1 amount',
1064 'label' => 'org 1 Amount',
1065 'amount' => 2,
1066 'financial_type_id' => 'Member Dues',
1067 'format.only_id' => TRUE,
1068 'membership_type_id' => reset($this->_ids['membership_type']),
1069 'price_field_id' => $priceField['id'],
1070 ));
1071 $this->_ids['price_field_value']['org1'] = $priceFieldValue;
1072
1073 $priceField = $this->callAPISuccess('PriceField', 'create', array(
1074 'price_set_id' => $this->_ids['price_set'],
1075 'html_type' => 'Radio',
1076 'name' => 'Org2 Price',
1077 'label' => 'Org2Price',
1078 ));
1079 $this->_ids['price_field']['org2'] = $priceField['id'];
1080
1081 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1082 'name' => 'org2 amount',
1083 'label' => 'org 2 Amount',
1084 'amount' => 200,
1085 'financial_type_id' => 'Member Dues',
1086 'format.only_id' => TRUE,
1087 'membership_type_id' => $this->_ids['membership_type']['org2'],
1088 'price_field_id' => $priceField['id'],
1089 ));
1090 $this->_ids['price_field_value']['org2'] = $priceFieldValue;
1091
1092 }
1093
449f4c90 1094 /**
1095 * Test submit recurring membership with immediate confirmation (IATS style).
1096 *
1097 * - we process 2 membership transactions against with a recurring contribution against a contribution page with an immediate
1098 * processor (IATS style - denoted by returning trxn_id)
1099 * - the first creates a new membership, completed contribution, in progress recurring. Check these
1100 * - create another - end date should be extended
1101 */
1102 public function testSubmitMembershipPriceSetPaymentPaymentProcessorSeparatePaymentRecurInstantPayment() {
1103
1104 $this->setUpMembershipContributionPage(TRUE);
1105 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
1106 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1107
1108 $submitParams = array(
1109 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1110 'id' => (int) $this->_ids['contribution_page'],
1111 'amount' => 10,
1112 'billing_first_name' => 'Billy',
1113 'billing_middle_name' => 'Goat',
1114 'billing_last_name' => 'Gruff',
1115 'email' => 'billy@goat.gruff',
1116 'selectMembership' => $this->_ids['membership_type'],
1117 'payment_processor_id' => 1,
1118 'credit_card_number' => '4111111111111111',
1119 'credit_card_type' => 'Visa',
1120 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1121 'cvv2' => 123,
1122 'is_recur' => 1,
1123 'auto_renew' => TRUE,
1124 'frequency_interval' => 1,
1125 'frequency_unit' => 'month',
1126 );
1127
1128 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
1129 $contribution = $this->callAPISuccess('contribution', 'get', array(
1130 'contribution_page_id' => $this->_ids['contribution_page'],
1131 'contribution_status_id' => 1,
1132 ));
1133
1134 $this->assertEquals(2, $contribution['count']);
1135 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
1136 $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1137 $this->assertNotEmpty($contribution['values'][$membershipPayment['contribution_id']]['contribution_recur_id']);
1138 $this->callAPISuccess('contribution_recur', 'getsingle', array());
1139 }
1140
f8fe0df6 1141 /**
e6fa4056
EM
1142 * Test submit recurring membership with delayed confirmation (Authorize.net style)
1143 * - we process 2 membership transactions against with a recurring contribution against a contribution page with a delayed
1144 * processor (Authorize.net style - denoted by NOT returning trxn_id)
1145 * - the first creates a pending membership, pending contribution, penging recurring. Check these
1146 * - complete the transaction
1147 * - create another - end date should NOT be extended
f8fe0df6 1148 */
1149 public function testSubmitMembershipPriceSetPaymentPaymentProcessorRecurDelayed() {
1150 $this->params['is_recur'] = 1;
f8fe0df6 1151 $this->params['recur_frequency_unit'] = 'month';
1152 $this->setUpMembershipContributionPage();
ab30e033 1153 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
b5eacf76 1154 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
356bfcaf 1155 $this->membershipTypeCreate(array('name' => 'Student'));
1156
1157 // Add a contribution & a couple of memberships so the id will not be 1 & will differ from membership id.
1158 // This saves us from 'accidental success'.
51f8c59c 1159 $this->contributionCreate(array('contact_id' => $this->contactIds[0]));
356bfcaf 1160 $this->contactMembershipCreate(array('contact_id' => $this->contactIds[0]));
1161 $this->contactMembershipCreate(array('contact_id' => $this->contactIds[0], 'membership_type_id' => 'Student'));
f8fe0df6 1162
1163 $submitParams = array(
1164 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1165 'id' => (int) $this->_ids['contribution_page'],
1166 'amount' => 10,
1167 'billing_first_name' => 'Billy',
1168 'billing_middle_name' => 'Goat',
1169 'billing_last_name' => 'Gruff',
1170 'email' => 'billy@goat.gruff',
1171 'selectMembership' => $this->_ids['membership_type'],
579a1fb7 1172 'payment_processor_id' => 1,
f8fe0df6 1173 'credit_card_number' => '4111111111111111',
1174 'credit_card_type' => 'Visa',
5896d037 1175 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
f8fe0df6 1176 'cvv2' => 123,
e6fa4056
EM
1177 'is_recur' => 1,
1178 'frequency_interval' => 1,
1179 'frequency_unit' => 'month',
f8fe0df6 1180 );
1181
a828d7b8 1182 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
5896d037 1183 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
92915c55
TO
1184 'contribution_page_id' => $this->_ids['contribution_page'],
1185 'contribution_status_id' => 2,
1186 ));
356bfcaf 1187
f8fe0df6 1188 $membershipPayment = $this->callAPISuccess('membership_payment', 'getsingle', array());
1189 $this->assertEquals($membershipPayment['contribution_id'], $contribution['id']);
1190 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1191 $this->assertEquals($membership['contact_id'], $contribution['contact_id']);
e6fa4056 1192 $this->assertEquals(5, $membership['status_id']);
51f8c59c 1193
1194 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id']));
1195 $this->assertEquals('civicrm_membership', $line['entity_table']);
1196 $this->assertEquals($membership['id'], $line['entity_id']);
1197
5896d037 1198 $this->callAPISuccess('contribution', 'completetransaction', array(
92915c55
TO
1199 'id' => $contribution['id'],
1200 'trxn_id' => 'ipn_called',
b396c447 1201 'payment_processor_id' => $this->_paymentProcessor['id'],
92915c55 1202 ));
51f8c59c 1203 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $contribution['id']));
1204 $this->assertEquals('civicrm_membership', $line['entity_table']);
1205 $this->assertEquals($membership['id'], $line['entity_id']);
1206
e6fa4056 1207 $membership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
f8fe0df6 1208 //renew it with processor setting completed - should extend membership
1209 $submitParams = array_merge($submitParams, array(
5896d037
TO
1210 'contact_id' => $contribution['contact_id'],
1211 'is_recur' => 1,
1212 'frequency_interval' => 1,
1213 'frequency_unit' => 'month',
1214 )
f8fe0df6 1215 );
51f8c59c 1216
b5eacf76 1217 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 2));
f8fe0df6 1218 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
6c6e6187 1219 $newContribution = $this->callAPISuccess('contribution', 'getsingle', array(
5896d037 1220 'id' => array(
21dfd5f5 1221 'NOT IN' => array($contribution['id']),
5896d037
TO
1222 ),
1223 'contribution_page_id' => $this->_ids['contribution_page'],
21dfd5f5 1224 'contribution_status_id' => 2,
5896d037 1225 )
f8fe0df6 1226 );
51f8c59c 1227 $line = $this->callAPISuccess('line_item', 'getsingle', array('contribution_id' => $newContribution['id']));
1228 $this->assertEquals('civicrm_membership', $line['entity_table']);
1229 $this->assertEquals($membership['id'], $line['entity_id']);
e6fa4056 1230
f8fe0df6 1231 $renewedMembership = $this->callAPISuccessGetSingle('membership', array('id' => $membershipPayment['membership_id']));
1232 //no renewal as the date hasn't changed
1233 $this->assertEquals($membership['end_date'], $renewedMembership['end_date']);
e6fa4056
EM
1234 $recurringContribution = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $newContribution['contribution_recur_id']));
1235 $this->assertEquals(2, $recurringContribution['contribution_status_id']);
f8fe0df6 1236 }
1237
cf5f7a42
JP
1238 /**
1239 * Test non-recur contribution with membership payment
1240 */
1241 public function testSubmitMembershipIsSeparatePaymentNotRecur() {
1242 //Create recur contribution page.
1243 $this->setUpMembershipContributionPage(TRUE, TRUE);
1244 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
1245 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1246
1247 //Sumbit payment with recur disabled.
1248 $submitParams = array(
1249 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1250 'id' => (int) $this->_ids['contribution_page'],
1251 'amount' => 10,
f6fe1f10
JP
1252 'frequency_interval' => 1,
1253 'frequency_unit' => 'month',
cf5f7a42
JP
1254 'billing_first_name' => 'Billy',
1255 'billing_middle_name' => 'Goat',
1256 'billing_last_name' => 'Gruff',
1257 'email' => 'billy@goat.gruff',
1258 'selectMembership' => $this->_ids['membership_type'],
1259 'payment_processor_id' => 1,
1260 'credit_card_number' => '4111111111111111',
1261 'credit_card_type' => 'Visa',
1262 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1263 'cvv2' => 123,
1264 );
1265
1266 //Assert if recur contribution is created.
1267 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1268 $recur = $this->callAPISuccess('contribution_recur', 'get', array());
1269 $this->assertEmpty($recur['count']);
1270 }
1271
1272
f9342903 1273 /**
eceb18cc 1274 * Set up membership contribution page.
f9342903 1275 * @param bool $isSeparatePayment
cf5f7a42 1276 * @param bool $isRecur
f9342903 1277 */
cf5f7a42 1278 public function setUpMembershipContributionPage($isSeparatePayment = FALSE, $isRecur = FALSE) {
f9342903 1279 $this->setUpMembershipBlockPriceSet();
a380f4a0 1280 $this->setupPaymentProcessor();
cf5f7a42 1281 $this->setUpContributionPage($isRecur);
f9342903
EM
1282
1283 $this->callAPISuccess('membership_block', 'create', array(
1284 'entity_id' => $this->_ids['contribution_page'],
1285 'entity_table' => 'civicrm_contribution_page',
1286 'is_required' => TRUE,
1287 'is_active' => TRUE,
1288 'is_separate_payment' => $isSeparatePayment,
1289 'membership_type_default' => $this->_ids['membership_type'],
1290 ));
f64a217a
EM
1291 }
1292
dccd9f4f
ERL
1293 /**
1294 * Set up pledge block.
1295 */
1296 public function setUpPledgeBlock() {
1297 $params = array(
1298 'entity_table' => 'civicrm_contribution_page',
1299 'entity_id' => $this->_ids['contribution_page'],
1300 'pledge_frequency_unit' => 'week',
1301 'is_pledge_interval' => 0,
1302 'pledge_start_date' => json_encode(array('calendar_date' => date('Ymd', strtotime("+1 month")))),
1303 );
1304 $pledgeBlock = CRM_Pledge_BAO_PledgeBlock::create($params);
1305 $this->_ids['pledge_block_id'] = $pledgeBlock->id;
1306 }
1307
f9342903 1308 /**
dd1b539a 1309 * The default data set does not include a complete default membership price set - not quite sure why.
1310 *
f9342903
EM
1311 * This function ensures it exists & populates $this->_ids with it's data
1312 */
00be9182 1313 public function setUpMembershipBlockPriceSet() {
5896d037 1314 $this->_ids['price_set'][] = $this->callAPISuccess('price_set', 'getvalue', array(
92915c55
TO
1315 'name' => 'default_membership_type_amount',
1316 'return' => 'id',
1317 ));
f9342903 1318 if (empty($this->_ids['membership_type'])) {
3e28c791 1319 $this->_ids['membership_type'] = array($this->membershipTypeCreate(array('minimum_fee' => 2)));
f9342903 1320 }
3e28c791
EM
1321 $priceField = $this->callAPISuccess('price_field', 'create', array(
1322 'price_set_id' => reset($this->_ids['price_set']),
1323 'name' => 'membership_amount',
1324 'label' => 'Membership Amount',
1325 'html_type' => 'Radio',
1326 'sequential' => 1,
1327 ));
1328 $this->_ids['price_field'][] = $priceField['id'];
dd1b539a 1329
3e28c791
EM
1330 foreach ($this->_ids['membership_type'] as $membershipTypeID) {
1331 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
f9342903
EM
1332 'name' => 'membership_amount',
1333 'label' => 'Membership Amount',
15816cb4 1334 'amount' => $this->_membershipBlockAmount,
43dbd988 1335 'financial_type_id' => 'Donation',
3e28c791
EM
1336 'format.only_id' => TRUE,
1337 'membership_type_id' => $membershipTypeID,
1338 'price_field_id' => $priceField['id'],
f9342903 1339 ));
840e3907 1340 $this->_ids['price_field_value'][] = $priceFieldValue;
f9342903 1341 }
65e172a3 1342 if (!empty($this->_ids['membership_type']['org2'])) {
1343 $priceField = $this->callAPISuccess('price_field', 'create', array(
1344 'price_set_id' => reset($this->_ids['price_set']),
1345 'name' => 'membership_org2',
1346 'label' => 'Membership Org2',
1347 'html_type' => 'Checkbox',
1348 'sequential' => 1,
1349 ));
1350 $this->_ids['price_field']['org2'] = $priceField['id'];
1351
1352 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1353 'name' => 'membership_org2',
1354 'label' => 'Membership org 2',
1355 'amount' => 55,
1356 'financial_type_id' => 'Member Dues',
1357 'format.only_id' => TRUE,
1358 'membership_type_id' => $this->_ids['membership_type']['org2'],
1359 'price_field_id' => $priceField['id'],
1360 ));
1361 $this->_ids['price_field_value']['org2'] = $priceFieldValue;
1362 }
13a16f43 1363 $priceField = $this->callAPISuccess('price_field', 'create', array(
1364 'price_set_id' => reset($this->_ids['price_set']),
1365 'name' => 'Contribution',
1366 'label' => 'Contribution',
1367 'html_type' => 'Text',
1368 'sequential' => 1,
1369 'is_enter_qty' => 1,
1370 ));
1371 $this->_ids['price_field']['cont'] = $priceField['id'];
1372 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1373 'name' => 'contribution',
1374 'label' => 'Give me money',
1375 'amount' => 88,
1376 'financial_type_id' => 'Donation',
1377 'format.only_id' => TRUE,
1378 'price_field_id' => $priceField['id'],
1379 ));
1380 $this->_ids['price_field_value'][] = $priceFieldValue;
f9342903 1381 }
3e28c791 1382
2f59d010 1383 /**
1384 * Add text field other amount to the price set.
1385 */
1386 public function addOtherAmountFieldToMembershipPriceSet() {
1387 $this->_ids['price_field']['other_amount'] = $this->callAPISuccess('price_field', 'create', array(
1388 'price_set_id' => reset($this->_ids['price_set']),
1389 'name' => 'other_amount',
1390 'label' => 'Other Amount',
1391 'html_type' => 'Text',
1392 'format.only_id' => TRUE,
1393 'sequential' => 1,
1394 ));
1395 $this->_ids['price_field_value']['other_amount'] = $this->callAPISuccess('price_field_value', 'create', array(
1396 'financial_type_id' => 'Donation',
1397 'format.only_id' => TRUE,
1398 'label' => 'Other Amount',
1399 'amount' => 1,
1400 'price_field_id' => $this->_ids['price_field']['other_amount'],
1401 ));
1402 }
1403
f64a217a 1404 /**
eceb18cc 1405 * Help function to set up contribution page with some defaults.
cf5f7a42 1406 * @param bool $isRecur
f64a217a 1407 */
cf5f7a42
JP
1408 public function setUpContributionPage($isRecur = FALSE) {
1409 if ($isRecur) {
1410 $this->params['is_recur'] = 1;
1411 $this->params['recur_frequency_unit'] = 'month';
1412 }
f64a217a
EM
1413 $contributionPageResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
1414 if (empty($this->_ids['price_set'])) {
1415 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams);
1416 $this->_ids['price_set'][] = $priceSet['id'];
1417 }
1418 $priceSetID = reset($this->_ids['price_set']);
5896d037 1419 CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
f9342903
EM
1420
1421 if (empty($this->_ids['price_field'])) {
1422 $priceField = $this->callAPISuccess('price_field', 'create', array(
1423 'price_set_id' => $priceSetID,
1424 'label' => 'Goat Breed',
1425 'html_type' => 'Radio',
1426 ));
1427 $this->_ids['price_field'] = array($priceField['id']);
1428 }
1429 if (empty($this->_ids['price_field_value'])) {
1430 $this->callAPISuccess('price_field_value', 'create', array(
1431 'price_set_id' => $priceSetID,
1432 'price_field_id' => $priceField['id'],
1433 'label' => 'Long Haired Goat',
43dbd988 1434 'financial_type_id' => 'Donation',
f9342903 1435 'amount' => 20,
05465712 1436 'non_deductible_amount' => 15,
f9342903
EM
1437 )
1438 );
1439 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1440 'price_set_id' => $priceSetID,
1441 'price_field_id' => $priceField['id'],
1442 'label' => 'Shoe-eating Goat',
43dbd988 1443 'financial_type_id' => 'Donation',
f9342903 1444 'amount' => 10,
05465712 1445 'non_deductible_amount' => 5,
f9342903
EM
1446 )
1447 );
1448 $this->_ids['price_field_value'] = array($priceFieldValue['id']);
1449 }
f64a217a 1450 $this->_ids['contribution_page'] = $contributionPageResult['id'];
be26f3e0
EM
1451 }
1452
6a488035 1453 public static function setUpBeforeClass() {
6c6e6187 1454 // put stuff here that should happen before all tests in this unit
6a488035
TO
1455 }
1456
5896d037 1457 public static function tearDownAfterClass() {
6a488035
TO
1458 $tablesToTruncate = array(
1459 'civicrm_contact',
1460 'civicrm_financial_type',
1461 'civicrm_contribution',
1462 'civicrm_contribution_page',
1463 );
1464 $unitTest = new CiviUnitTestCase();
1465 $unitTest->quickCleanup($tablesToTruncate);
1466 }
96025800 1467
a380f4a0
EM
1468 /**
1469 * Create a payment processor instance.
1470 */
1471 protected function setupPaymentProcessor() {
1472 $this->params['payment_processor_id'] = $this->_ids['payment_processor'] = $this->paymentProcessorCreate(array(
1473 'payment_processor_type_id' => 'Dummy',
1474 'class_name' => 'Payment_Dummy',
1475 'billing_mode' => 1,
1476 ));
1477 $this->_paymentProcessor = $this->callAPISuccess('payment_processor', 'getsingle', array('id' => $this->params['payment_processor_id']));
1478 }
1479
dccd9f4f
ERL
1480 /**
1481 * Test submit recurring pledge.
1482 *
1483 * - 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.
1484 */
1485 public function testSubmitPledgePaymentPaymentProcessorRecurFuturePayment() {
1486 $this->params['adjust_recur_start_date'] = TRUE;
1487 $this->params['is_pay_later'] = FALSE;
1488 $this->setUpContributionPage();
1489 $this->setUpPledgeBlock();
1490 $this->setupPaymentProcessor();
1491 $dummyPP = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
1492 $dummyPP->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'create_first_success'));
1493
1494 $submitParams = array(
1495 'id' => (int) $this->_ids['contribution_page'],
1496 'amount' => 100,
1497 'billing_first_name' => 'Billy',
1498 'billing_middle_name' => 'Goat',
1499 'billing_last_name' => 'Gruff',
1500 'email' => 'billy@goat.gruff',
1501 'payment_processor_id' => 1,
1502 'credit_card_number' => '4111111111111111',
1503 'credit_card_type' => 'Visa',
1504 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1505 'cvv2' => 123,
1506 'pledge_frequency_interval' => 1,
1507 'pledge_frequency_unit' => 'week',
1508 'pledge_installments' => 3,
1509 'is_pledge' => TRUE,
1510 'pledge_block_id' => (int) $this->_ids['pledge_block_id'],
1511 );
1512
1513 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
1514
1515 // Check if contribution created.
1516 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1517 'contribution_page_id' => $this->_ids['contribution_page'],
1518 'contribution_status_id' => 'Completed', // Will be pending when actual payment processor is used (dummy processor does not support future payments).
1519 ));
1520
1521 $this->assertEquals('create_first_success', $contribution['trxn_id']);
1522
1523 // Check if pledge created.
1524 $pledge = $this->callAPISuccess('pledge', 'getsingle', array());
1525 $this->assertEquals(date('Ymd', strtotime($pledge['pledge_start_date'])), date('Ymd', strtotime("+1 month")));
1526 $this->assertEquals($pledge['pledge_amount'], 300.00);
1527
1528 // Check if pledge payments created.
1529 $params = array(
1530 'pledge_id' => $pledge['id'],
1531 );
1532 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1533 $this->assertEquals($pledgePayment['count'], 3);
1534 $this->assertEquals(date('Ymd', strtotime($pledgePayment['values'][1]['scheduled_date'])), date('Ymd', strtotime("+1 month")));
1535 $this->assertEquals($pledgePayment['values'][1]['scheduled_amount'], 100.00);
1536 $this->assertEquals($pledgePayment['values'][1]['status_id'], 1); // Will be pending when actual payment processor is used (dummy processor does not support future payments).
1537
1538 // Check contribution recur record.
1539 $recur = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $contribution['contribution_recur_id']));
1540 $this->assertEquals(date('Ymd', strtotime($recur['start_date'])), date('Ymd', strtotime("+1 month")));
1541 $this->assertEquals($recur['amount'], 100.00);
1542 $this->assertEquals($recur['contribution_status_id'], 5); // In progress status.
1543 }
1544
603577b2
E
1545 /**
1546 * Test submit pledge payment.
1547 *
1548 * - test submitting a pledge payment using contribution form.
1549 */
1550 public function testSubmitPledgePayment() {
1551 $this->testSubmitPledgePaymentPaymentProcessorRecurFuturePayment();
1552 $pledge = $this->callAPISuccess('pledge', 'getsingle', array());
1553 $params = array(
1554 'pledge_id' => $pledge['id'],
1555 );
1556 $submitParams = array(
1557 'id' => (int) $pledge['pledge_contribution_page_id'],
1558 'pledge_amount' => array(2 => 1),
1559 'billing_first_name' => 'Billy',
1560 'billing_middle_name' => 'Goat',
1561 'billing_last_name' => 'Gruff',
1562 'email' => 'billy@goat.gruff',
1563 'payment_processor_id' => 1,
1564 'credit_card_number' => '4111111111111111',
1565 'credit_card_type' => 'Visa',
1566 'credit_card_exp_date' => array('M' => 9, 'Y' => 2040),
1567 'cvv2' => 123,
1568 'pledge_id' => $pledge['id'],
1569 'cid' => $pledge['contact_id'],
1570 'contact_id' => $pledge['contact_id'],
1571 'amount' => 100.00,
1572 'is_pledge' => TRUE,
1573 'pledge_block_id' => $this->_ids['pledge_block_id'],
1574 );
1575 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1576 $this->assertEquals($pledgePayment['values'][2]['status_id'], 2);
1577
1578 $this->callAPIAndDocument('contribution_page', 'submit', $submitParams, __FUNCTION__, __FILE__, 'submit contribution page', NULL);
1579
1580 // Check if contribution created.
1581 $contribution = $this->callAPISuccess('contribution', 'getsingle', array(
1582 'contribution_page_id' => $pledge['pledge_contribution_page_id'],
1583 'contribution_status_id' => 'Completed',
1584 'contact_id' => $pledge['contact_id'],
1585 'contribution_recur_id' => array('IS NULL' => 1),
1586 ));
1587
1588 $this->assertEquals(100.00, $contribution['total_amount']);
1589 $pledgePayment = $this->callAPISuccess('pledge_payment', 'get', $params);
1590 $this->assertEquals($pledgePayment['values'][2]['status_id'], 1, "This pledge payment should have been completed");
1591 $this->assertEquals($pledgePayment['values'][2]['contribution_id'], $contribution['id']);
1592 }
1593
a93e9452
PN
1594 /**
1595 * Test form submission with multiple option price set.
83644f47 1596 *
1597 * @param string $thousandSeparator
1598 * punctuation used to refer to thousands.
1599 *
1600 * @dataProvider getThousandSeparators
a93e9452 1601 */
83644f47 1602 public function testSubmitContributionPageWithPriceSet($thousandSeparator) {
1603 $this->setCurrencySeparators($thousandSeparator);
a93e9452
PN
1604 $this->_priceSetParams['is_quick_config'] = 0;
1605 $this->setUpContributionPage();
1606 $submitParams = array(
1607 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1608 'id' => (int) $this->_ids['contribution_page'],
1609 'amount' => 80,
1610 'first_name' => 'Billy',
1611 'last_name' => 'Gruff',
1612 'email' => 'billy@goat.gruff',
1613 'is_pay_later' => TRUE,
1614 );
1615 $this->addPriceFields($submitParams);
1616
1617 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1618 $contribution = $this->callAPISuccessGetSingle('contribution', array(
1619 'contribution_page_id' => $this->_ids['contribution_page'],
1620 'contribution_status_id' => 2,
1621 ));
83644f47 1622 $this->assertEquals(80, $contribution['total_amount']);
1623 $lineItems = $this->callAPISuccess('LineItem', 'get', array(
1624 'contribution_id' => $contribution['id'],
1625 ));
1626 $this->assertEquals(3, $lineItems['count']);
1627 $totalLineAmount = 0;
1628 foreach ($lineItems['values'] as $lineItem) {
1629 $totalLineAmount = $totalLineAmount + $lineItem['line_total'];
1630 }
1631 $this->assertEquals(80, $totalLineAmount);
a93e9452
PN
1632 }
1633
1634 /**
1635 * Function to add additional price fields to priceset.
1636 * @param array $params
1637 */
1638 public function addPriceFields(&$params) {
1639 $priceSetID = reset($this->_ids['price_set']);
1640 $priceField = $this->callAPISuccess('price_field', 'create', array(
1641 'price_set_id' => $priceSetID,
1642 'label' => 'Chicken Breed',
1643 'html_type' => 'CheckBox',
1644 ));
1645 $priceFieldValue1 = $this->callAPISuccess('price_field_value', 'create', array(
1646 'price_set_id' => $priceSetID,
1647 'price_field_id' => $priceField['id'],
1648 'label' => 'Shoe-eating chicken -1',
1649 'financial_type_id' => 'Donation',
1650 'amount' => 30,
1651 ));
1652 $priceFieldValue2 = $this->callAPISuccess('price_field_value', 'create', array(
1653 'price_set_id' => $priceSetID,
1654 'price_field_id' => $priceField['id'],
1655 'label' => 'Shoe-eating chicken -2',
1656 'financial_type_id' => 'Donation',
1657 'amount' => 40,
1658 ));
1659 $params['price_' . $priceField['id']] = array(
1660 $priceFieldValue1['id'] => 1,
1661 $priceFieldValue2['id'] => 1,
1662 );
1663 }
1664
57d395bb
K
1665 /**
1666 * Test Tax Amount is calculated properly when using PriceSet with Field Type = Text/Numeric Quantity
83644f47 1667 *
1668 * @param string $thousandSeparator
1669 * punctuation used to refer to thousands.
1670 *
1671 * @dataProvider getThousandSeparators
57d395bb 1672 */
83644f47 1673 public function testSubmitContributionPageWithPriceSetQuantity($thousandSeparator) {
1674 $this->setCurrencySeparators($thousandSeparator);
57d395bb
K
1675 $this->_priceSetParams['is_quick_config'] = 0;
1676 $this->enableTaxAndInvoicing();
1677 $financialType = $this->createFinancialType();
1678 $financialTypeId = $financialType['id'];
1679 // 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%
83644f47 1680 $this->relationForFinancialTypeWithFinancialAccount($financialType['id'], 5);
57d395bb
K
1681
1682 $this->setUpContributionPage();
1683 $submitParams = array(
1684 'price_' . $this->_ids['price_field'][0] => reset($this->_ids['price_field_value']),
1685 'id' => (int) $this->_ids['contribution_page'],
1686 'first_name' => 'J',
1687 'last_name' => 'T',
1688 'email' => 'JT@ohcanada.ca',
1689 'is_pay_later' => TRUE,
6298b166 1690 'receive_date' => date('Y-m-d H:i:s'),
57d395bb
K
1691 );
1692
1693 // Create PriceSet/PriceField
1694 $priceSetID = reset($this->_ids['price_set']);
1695 $priceField = $this->callAPISuccess('price_field', 'create', array(
1696 'price_set_id' => $priceSetID,
1697 'label' => 'Printing Rights',
1698 'html_type' => 'Text',
1699 ));
1700 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
1701 'price_set_id' => $priceSetID,
1702 'price_field_id' => $priceField['id'],
1703 'label' => 'Printing Rights',
1704 'financial_type_id' => $financialTypeId,
1705 'amount' => '16.95',
1706 ));
1707 $priceFieldId = $priceField['id'];
1708
1709 // Set quantity for our test
1710 $submitParams['price_' . $priceFieldId] = 180;
1711
1712 // contribution_page submit requires amount and tax_amount - and that's ok we're not testing that - we're testing at the LineItem level
83644f47 1713 $submitParams['amount'] = $this->formatMoneyInput(180 * 16.95);
57d395bb 1714 // This is the correct Tax Amount - use it later to compare to what the CiviCRM Core came up with at the LineItem level
83644f47 1715 $submitParams['tax_amount'] = $this->formatMoneyInput(180 * 16.95 * 0.10);
57d395bb
K
1716
1717 $this->callAPISuccess('contribution_page', 'submit', $submitParams);
1718 $contribution = $this->callAPISuccessGetSingle('contribution', array(
1719 'contribution_page_id' => $this->_ids['contribution_page'],
1720 ));
1721
1722 // Retrieve the lineItem that belongs to the Printing Rights and check the tax_amount CiviCRM Core calculated for it
83644f47 1723 $lineItem = $this->callAPISuccessGetSingle('LineItem', array(
57d395bb
K
1724 'contribution_id' => $contribution['id'],
1725 'label' => 'Printing Rights',
1726 ));
57d395bb 1727
83644f47 1728 $lineItem_TaxAmount = round($lineItem['tax_amount'], 2);
1729
1730 $this->assertEquals($lineItem['line_total'], $contribution['total_amount'], 'Contribution total should match line total');
1731 $this->assertEquals($lineItem_TaxAmount, round(180 * 16.95 * 0.10, 2), 'Wrong Sales Tax Amount is calculated and stored.');
57d395bb
K
1732 }
1733
15816cb4
AS
1734 public function hook_civicrm_alterPaymentProcessorParams($paymentObj, &$rawParams, &$cookedParams) {
1735 // Ensure total_amount are the same if they're both given.
1736 $total_amount = CRM_Utils_Array::value('total_amount', $rawParams);
1737 $amount = CRM_Utils_Array::value('amount', $rawParams);
1738 if (!empty($total_amount) && !empty($amount) && $total_amount != $amount) {
1739 throw new Exception("total_amount '$total_amount' and amount '$amount' differ.");
1740 }
1741
1742 // Log parameters for later debugging and testing.
1743 $message = __FUNCTION__ . ": {$rawParams['TEST_UNIQ']}:";
1744 $log_params = array_intersect_key($rawParams, array(
1745 'amount' => 1,
1746 'total_amount' => 1,
1747 'contributionID' => 1,
1748 ));
1749 $message .= json_encode($log_params);
1750 $log = new CRM_Utils_SystemLogger();
1751 $log->debug($message, $_REQUEST);
1752 }
1753
6a488035 1754}