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