Fix token subscriber to format the display of the custom tokens
[civicrm-core.git] / tests / phpunit / CRM / Core / Payment / AuthorizeNetIPNTest.php
1 <?php
2
3 use Civi\Payment\Exception\PaymentProcessorException;
4 use Civi\Api4\Contribution;
5
6 /**
7 * Class CRM_Core_Payment_PayPalProIPNTest
8 * @group headless
9 */
10 class CRM_Core_Payment_AuthorizeNetIPNTest extends CiviUnitTestCase {
11 use CRMTraits_Financial_OrderTrait;
12
13 protected $_contributionID;
14 protected $_invoiceID = 'c2r9c15f7be20b4f3fef1f77e4c37424';
15 protected $_financialTypeID = 1;
16 protected $_contactID;
17 protected $_contributionRecurID;
18 protected $_contributionPageID;
19 protected $_paymentProcessorID;
20
21 /**
22 *
23 * @throws \CRM_Core_Exception
24 */
25 public function setUp() {
26 parent::setUp();
27 $this->_paymentProcessorID = $this->paymentProcessorAuthorizeNetCreate(['is_test' => 0]);
28 $this->_contactID = $this->individualCreate();
29 $contributionPage = $this->callAPISuccess('contribution_page', 'create', [
30 'title' => 'Test Contribution Page',
31 'financial_type_id' => $this->_financialTypeID,
32 'currency' => 'USD',
33 'payment_processor' => $this->_paymentProcessorID,
34 'max_amount' => 1000,
35 'receipt_from_email' => 'gaia@the.cosmos',
36 'receipt_from_name' => 'Pachamama',
37 'is_email_receipt' => TRUE,
38 ]);
39 $this->_contributionPageID = $contributionPage['id'];
40 }
41
42 public function tearDown() {
43 $this->quickCleanUpFinancialEntities();
44 }
45
46 /**
47 * Ensure recurring contributions from Contribution Pages
48 * with receipt turned off don't send a receipt.
49 *
50 * @throws \CiviCRM_API3_Exception
51 * @throws \CRM_Core_Exception
52 */
53 public function testIPNPaymentRecurNoReceipt() {
54 $mut = new CiviMailUtils($this, TRUE);
55 // Turn off receipts in contribution page.
56 $api_params = [
57 'id' => $this->_contributionPageID,
58 'is_email_receipt' => FALSE,
59 ];
60 $this->callAPISuccess('contributionPage', 'update', $api_params);
61
62 // Create initial recurring payment and initial contribution.
63 // Note - we can't use setupRecurringPaymentProcessorTransaction(), which
64 // would be convenient because it does not fully mimic the real user
65 // experience. Using setupRecurringPaymentProcessorTransaction() doesn't
66 // specify is_email_receipt so it is always set to 1. We need to more
67 // closely mimic what happens with a live transaction to test that
68 // is_email_receipt is not set to 1 if the originating contribution page
69 // has is_email_receipt set to 0.
70 $form = new CRM_Contribute_Form_Contribution();
71 $form->_mode = 'Live';
72 try {
73 $contribution = $form->testSubmit([
74 'total_amount' => 200,
75 'financial_type_id' => 1,
76 'receive_date' => date('m/d/Y'),
77 'receive_date_time' => date('H:i:s'),
78 'contact_id' => $this->_contactID,
79 'contribution_status_id' => 1,
80 'credit_card_number' => 4444333322221111,
81 'cvv2' => 123,
82 'credit_card_exp_date' => [
83 'M' => 9,
84 'Y' => 2025,
85 ],
86 'credit_card_type' => 'Visa',
87 'billing_first_name' => 'Junko',
88 'billing_middle_name' => '',
89 'billing_last_name' => 'Adams',
90 'billing_street_address-5' => time() . ' Lincoln St S',
91 'billing_city-5' => 'Maryknoll',
92 'billing_state_province_id-5' => 1031,
93 'billing_postal_code-5' => 10545,
94 'billing_country_id-5' => 1228,
95 'frequency_interval' => 1,
96 'frequency_unit' => 'month',
97 'installments' => 2,
98 'hidden_AdditionalDetail' => 1,
99 'hidden_Premium' => 1,
100 'payment_processor_id' => $this->_paymentProcessorID,
101 'currency' => 'USD',
102 'source' => 'bob sled race',
103 'contribution_page_id' => $this->_contributionPageID,
104 'is_recur' => TRUE,
105 ], CRM_Core_Action::ADD);
106 }
107 catch (PaymentProcessorException $e) {
108 $this->markTestSkipped('Error from A.net - cannot proceed');
109 }
110 $this->_contributionID = $contribution->id;
111 $this->ids['Contribution'][0] = $contribution->id;
112 $this->_contributionRecurID = $contribution->contribution_recur_id;
113
114 $contributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', ['id' => $this->_contributionRecurID]);
115 $processor_id = $contributionRecur['processor_id'];
116 $this->assertEquals('Pending', CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contributionRecur['contribution_status_id']));
117 // Process the initial one after a second's break to ensure modified date really is later.
118 sleep(1);
119 $IPN = new CRM_Core_Payment_AuthorizeNetIPN(
120 $this->getRecurTransaction(['x_subscription_id' => $processor_id])
121 );
122 $IPN->main();
123 $updatedContributionRecur = $this->callAPISuccessGetSingle('ContributionRecur', ['id' => $this->_contributionRecurID]);
124 $this->assertEquals('In Progress', CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', $updatedContributionRecur['contribution_status_id']));
125 $this->assertTrue(strtotime($updatedContributionRecur['modified_date']) > strtotime($contributionRecur['modified_date']));
126
127 // Now send a second one (authorize seems to treat first and second contributions
128 // differently.
129 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurSubsequentTransaction([
130 'x_subscription_id' => $processor_id,
131 'x_subscription_paynum' => 2,
132 ]));
133 $IPN->main();
134 $updatedContributionRecurAgain = $this->callAPISuccessGetSingle('ContributionRecur', ['id' => $this->_contributionRecurID]);
135 $this->assertEquals('Completed', CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', $updatedContributionRecurAgain['contribution_status_id']));
136 $this->assertEquals(date('Y-m-d'), substr($updatedContributionRecurAgain['end_date'], 0, 10));
137 // There should not be any email.
138 $mut->assertMailLogEmpty();
139
140 $contributions = Contribution::get()->addWhere('contribution_recur_id', '=', $this->_contributionRecurID)->addSelect('contribution_page_id')->execute();
141 foreach ($contributions as $contribution) {
142 $this->assertEquals($this->_contributionPageID, $contribution['contribution_page_id']);
143 }
144 }
145
146 /**
147 * Test IPN response updates contribution_recur & contribution for first & second contribution
148 *
149 * @throws \CRM_Core_Exception
150 */
151 public function testIPNPaymentRecurSuccess() {
152 $this->setupRecurringPaymentProcessorTransaction();
153 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurTransaction());
154 $IPN->main();
155 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $this->_contributionID]);
156 $this->assertEquals(1, $contribution['contribution_status_id']);
157 $this->assertEquals('6511143069', $contribution['trxn_id']);
158 // source gets set by processor
159 $this->assertTrue(substr($contribution['contribution_source'], 0, 20) == "Online Contribution:");
160 $contributionRecur = $this->callAPISuccess('contribution_recur', 'getsingle', ['id' => $this->_contributionRecurID]);
161 $this->assertEquals(5, $contributionRecur['contribution_status_id']);
162 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurSubsequentTransaction());
163 $IPN->main();
164 $contribution = $this->callAPISuccess('contribution', 'get', [
165 'contribution_recur_id' => $this->_contributionRecurID,
166 'sequential' => 1,
167 ])['values'];
168 $this->assertCount(2, $contribution);
169 $secondContribution = $contribution[1];
170 $this->assertEquals('second_one', $secondContribution['trxn_id']);
171 $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($secondContribution['receive_date'])));
172 $this->assertEquals('expensive', $secondContribution['amount_level']);
173 $this->assertEquals($this->ids['campaign'][0], $secondContribution['campaign_id']);
174 }
175
176 /**
177 * Test payment processor is correctly assigned for the IPN payment.
178 */
179 public function testIPNPaymentRecurSuccessMultiAuthNetProcessor() {
180 //Create and set up recur payment using second instance of AuthNet Processor.
181 $this->_paymentProcessorID2 = $this->paymentProcessorAuthorizeNetCreate(['name' => 'Authorize2', 'is_test' => 0]);
182 $this->setupRecurringPaymentProcessorTransaction(['payment_processor_id' => $this->_paymentProcessorID2]);
183
184 //Call IPN with processor id.
185 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurTransaction(['processor_id' => $this->_paymentProcessorID2]));
186 $IPN->main();
187 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $this->_contributionID]);
188 $this->assertEquals(1, $contribution['contribution_status_id']);
189 $this->assertEquals('6511143069', $contribution['trxn_id']);
190 // source gets set by processor
191 $this->assertTrue(substr($contribution['contribution_source'], 0, 20) == "Online Contribution:");
192 $contributionRecur = $this->callAPISuccess('contribution_recur', 'getsingle', ['id' => $this->_contributionRecurID]);
193 $this->assertEquals(5, $contributionRecur['contribution_status_id']);
194 }
195
196 /**
197 * Test IPN response updates contribution_recur & contribution for first & second contribution
198 *
199 * @throws \CRM_Core_Exception
200 */
201 public function testIPNPaymentRecurSuccessSuppliedReceiveDate() {
202 $this->setupRecurringPaymentProcessorTransaction();
203 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurTransaction());
204 $IPN->main();
205 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $this->_contributionID]);
206 $this->assertEquals(1, $contribution['contribution_status_id']);
207 $this->assertEquals('6511143069', $contribution['trxn_id']);
208 // source gets set by processor
209 $this->assertTrue(substr($contribution['contribution_source'], 0, 20) == "Online Contribution:");
210 $contributionRecur = $this->callAPISuccess('contribution_recur', 'getsingle', ['id' => $this->_contributionRecurID]);
211 $this->assertEquals(5, $contributionRecur['contribution_status_id']);
212 $IPN = new CRM_Core_Payment_AuthorizeNetIPN(array_merge(['receive_date' => '2010-07-01'], $this->getRecurSubsequentTransaction()));
213 $IPN->main();
214 $contribution = $this->callAPISuccess('contribution', 'get', [
215 'contribution_recur_id' => $this->_contributionRecurID,
216 'sequential' => 1,
217 ]);
218 $this->assertEquals(2, $contribution['count']);
219 $this->assertEquals('second_one', $contribution['values'][1]['trxn_id']);
220 $this->assertEquals('2010-07-01', date('Y-m-d', strtotime($contribution['values'][1]['receive_date'])));
221 }
222
223 /**
224 * Test IPN response updates contribution_recur & contribution for first &
225 * second contribution
226 *
227 * @throws \CRM_Core_Exception
228 * @throws \CiviCRM_API3_Exception
229 */
230 public function testIPNPaymentMembershipRecurSuccess(): void {
231 $this->createRepeatMembershipOrder();
232 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurTransaction());
233 $IPN->main();
234 $contribution = $this->callAPISuccess('contribution', 'getsingle', ['id' => $this->ids['Contribution'][0]]);
235 $this->assertEquals(1, $contribution['contribution_status_id']);
236 $this->assertEquals('6511143069', $contribution['trxn_id']);
237
238 // source gets set by processor
239 $this->assertEquals('Online Contribution:', substr($contribution['contribution_source'], 0, 20));
240 $contributionRecur = $this->callAPISuccess('contribution_recur', 'getsingle', ['id' => $this->_contributionRecurID]);
241 $this->assertEquals(5, $contributionRecur['contribution_status_id']);
242 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurSubsequentTransaction());
243 $IPN->main();
244 $contribution = $this->callAPISuccess('contribution', 'get', [
245 'contribution_recur_id' => $this->_contributionRecurID,
246 'sequential' => 1,
247 ]);
248 $this->assertEquals(2, $contribution['count']);
249 // Ensure both contributions are coded as credit card contributions.
250 $this->assertEquals(1, $contribution['values'][0]['payment_instrument_id']);
251 $this->assertEquals(1, $contribution['values'][1]['payment_instrument_id']);
252 $this->assertEquals('second_one', $contribution['values'][1]['trxn_id']);
253 $this->callAPISuccessGetSingle('membership_payment', ['contribution_id' => $contribution['values'][1]['id']]);
254 $this->callAPISuccessGetSingle('line_item', [
255 'contribution_id' => $contribution['values'][1]['id'],
256 'entity_table' => 'civicrm_membership',
257 ]);
258 $this->validateAllContributions();
259 $this->validateAllPayments();
260 }
261
262 /**
263 * Test IPN response mails don't leak.
264 *
265 * @throws \CRM_Core_Exception|\CiviCRM_API3_Exception
266 */
267 public function testIPNPaymentMembershipRecurSuccessNoLeakage() {
268 $mut = new CiviMailUtils($this, TRUE);
269 $this->setupMembershipRecurringPaymentProcessorTransaction(['is_email_receipt' => TRUE]);
270 $this->addProfile('supporter_profile', $this->_contributionPageID);
271 $this->addProfile('honoree_individual', $this->_contributionPageID, 'soft_credit');
272
273 $this->callAPISuccess('ContributionSoft', 'create', [
274 'contact_id' => $this->individualCreate(),
275 'contribution_id' => $this->_contributionID,
276 'soft_credit_type_id' => 'in_memory_of',
277 'amount' => 200,
278 ]);
279
280 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurTransaction());
281 $IPN->main();
282 $mut->checkAllMailLog([
283 'Membership Type: General',
284 'Mr. Anthony Anderson II" <anthony_anderson@civicrm.org>',
285 'Amount: $ 200.00',
286 'Membership Start Date:',
287 'Supporter Profile',
288 'First Name: Anthony',
289 'Last Name: Anderson',
290 'Email Address: anthony_anderson@civicrm.org',
291 'Honor',
292 'This membership will be automatically renewed every',
293 'Dear Anthony',
294 'Thanks for your auto renew membership sign-up',
295 'In Memory of',
296 ]);
297 $mut->clearMessages();
298 $this->_contactID = $this->individualCreate(['first_name' => 'Antonia', 'prefix_id' => 'Mrs.', 'email' => 'antonia_anderson@civicrm.org']);
299 $this->_invoiceID = uniqid();
300
301 // Note, the second contribution is not in honor of anyone and the
302 // receipt should not mention honor at all.
303 $this->setupMembershipRecurringPaymentProcessorTransaction(['is_email_receipt' => TRUE]);
304 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurTransaction(['x_trans_id' => 'hers']));
305 $IPN->main();
306
307 $mut->checkAllMailLog([
308 'Membership Type: General',
309 'Mrs. Antonia Anderson II',
310 'antonia_anderson@civicrm.org',
311 'Amount: $ 200.00',
312 'Membership Start Date:',
313 'Transaction #: hers',
314 'Supporter Profile',
315 'First Name: Antonia',
316 'Last Name: Anderson',
317 'Email Address: antonia_anderson@civicrm.org',
318 'This membership will be automatically renewed every',
319 'Dear Antonia',
320 'Thanks for your auto renew membership sign-up',
321 ]);
322
323 $shouldNotBeInMailing = [
324 'Honor',
325 'In Memory of',
326 ];
327 $mails = $mut->getAllMessages('raw');
328 foreach ($mails as $mail) {
329 $mut->checkMailForStrings([], $shouldNotBeInMailing, '', $mail);
330 }
331 $mut->stop();
332 $mut->clearMessages();
333 }
334
335 /**
336 * Test IPN response mails don't leak.
337 */
338 public function testIPNPaymentMembershipRecurSuccessNoLeakageOnlineThenOffline() {
339 $mut = new CiviMailUtils($this, TRUE);
340 $this->setupMembershipRecurringPaymentProcessorTransaction(['is_email_receipt' => TRUE]);
341 $this->addProfile('supporter_profile', $this->_contributionPageID);
342 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurTransaction());
343 $IPN->main();
344 $mut->checkAllMailLog([
345 'Membership Type: General',
346 'Mr. Anthony Anderson II" <anthony_anderson@civicrm.org>',
347 'Amount: $ 200.00',
348 'Membership Start Date:',
349 'Supporter Profile',
350 'First Name: Anthony',
351 'Last Name: Anderson',
352 'Email Address: anthony_anderson@civicrm.org',
353 'This membership will be automatically renewed every',
354 'Dear Anthony',
355 'Thanks for your auto renew membership sign-up',
356 ]);
357
358 $this->_contactID = $this->individualCreate(['first_name' => 'Antonia', 'prefix_id' => 'Mrs.', 'email' => 'antonia_anderson@civicrm.org']);
359 $this->_invoiceID = uniqid();
360 $this->_contributionPageID = NULL;
361
362 $this->setupMembershipRecurringPaymentProcessorTransaction(['is_email_receipt' => TRUE]);
363 $mut->clearMessages();
364 $IPN = new CRM_Core_Payment_AuthorizeNetIPN($this->getRecurTransaction(['x_trans_id' => 'hers']));
365 $IPN->main();
366
367 $mut->checkAllMailLog([
368 'Membership Type: General',
369 'Mrs. Antonia Anderson II',
370 'antonia_anderson@civicrm.org',
371 'Amount: $ 200.00',
372 'Membership Start Date:',
373 'Transaction #: hers',
374 'This membership will be automatically renewed every',
375 'Dear Antonia',
376 'Thanks for your auto renew membership sign-up',
377 ],
378 [
379 'First Name: Anthony',
380 'First Name: Antonia',
381 'Last Name: Anderson',
382 'Supporter Profile',
383 'Email Address: antonia_anderson@civicrm.org',
384 ]);
385
386 $mut->stop();
387 $mut->clearMessages();
388 }
389
390 /**
391 * Get detail for recurring transaction.
392 *
393 * @param array $params
394 * Additional parameters.
395 *
396 * @return array
397 * Parameters like AuthorizeNet silent post paramters.
398 */
399 public function getRecurTransaction($params = []) {
400 return array_merge([
401 'x_amount' => '200.00',
402 "x_country" => 'US',
403 'x_phone' => "",
404 "x_fax" => "",
405 "x_email" => "me@gmail.com",
406 "x_description" => "lots of money",
407 "x_type" => "auth_capture",
408 "x_ship_to_first_name" => "",
409 "x_ship_to_last_name" => "",
410 "x_ship_to_company" => "",
411 "x_ship_to_address" => "",
412 "x_ship_to_city" => "",
413 "x_ship_to_state" => "",
414 "x_ship_to_zip" => "",
415 "x_ship_to_country" => "",
416 "x_tax" => "0.00",
417 "x_duty" => "0.00",
418 "x_freight" => "0.00",
419 "x_tax_exempt" => "FALSE",
420 "x_po_num" => "",
421 "x_MD5_Hash" => "1B7C0C5B4DEDD9CAD0636E35E22FC594",
422 "x_cvv2_resp_code" => "",
423 "x_cavv_response" => "",
424 "x_test_request" => "false",
425 "x_subscription_id" => $this->_contactID,
426 "x_subscription_paynum" => "1",
427 'x_first_name' => 'Robert',
428 'x_zip' => '90210',
429 'x_state' => 'WA',
430 'x_city' => 'Dallas',
431 'x_address' => '41 My ST',
432 'x_invoice_num' => $this->ids['Contribution'][0],
433 'x_cust_id' => $this->_contactID,
434 'x_company' => 'nowhere@civicrm.org',
435 'x_last_name' => 'Roberts',
436 'x_account_number' => 'XXXX5077',
437 'x_card_type' => 'Visa',
438 'x_method' => 'CC',
439 'x_trans_id' => '6511143069',
440 'x_auth_code' => '123456',
441 'x_avs_code' => 'Z',
442 'x_response_reason_text' => 'This transaction has been approved.',
443 'x_response_reason_code' => '1',
444 'x_response_code' => '1',
445 ], $params);
446 }
447
448 /**
449 * @return array
450 */
451 public function getRecurSubsequentTransaction($params = []) {
452 return array_merge($this->getRecurTransaction(), [
453 'x_trans_id' => 'second_one',
454 'x_MD5_Hash' => 'EA7A3CD65A85757827F51212CA1486A8',
455 ], $params);
456 }
457
458 }