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