Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to Payment.create & repeattransaction in Authorize.net #26620

Merged
merged 1 commit into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CRM/Contribute/BAO/Contribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -3816,6 +3816,7 @@ public static function isSingleLineItem($id) {
*/
public static function completeOrder($input, $recurringContributionID, $contributionID, $isPostPaymentCreate = FALSE) {
if (!$contributionID) {
CRM_Core_Error::deprecatedFunctionWarning('v3api Contribution.repeattransaction. This handling will be removed around 5.70 (calling this function directly has never been supported outside core anyway)');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattwire it's the end of an era!!!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eileenmcnaughton Shouldn't this say "Contribution.completetransaction"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattwire nope - this is the calling of completeOrder to call repeattransaction that is deprected

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, the comment confuses me :-)

return self::repeatTransaction($input, $recurringContributionID);
}
$transaction = new CRM_Core_Transaction();
Expand Down
100 changes: 73 additions & 27 deletions CRM/Core/Payment/AuthorizeNetIPN.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ public function __construct($inputData) {
parent::__construct();
}

/**
* @var string
*/
protected $transactionID;

/**
* @var string
*/
protected $contributionStatus;

/**
* Main IPN processing function.
*/
Expand All @@ -45,7 +55,7 @@ public function main() {
$x_subscription_id = $this->getRecurProcessorID();

if (!$this->isSuccess()) {
$errorMessage = ts('Subscription payment failed - %1', [1 => htmlspecialchars($input['response_reason_text'])]);
$errorMessage = ts('Subscription payment failed - %1', [1 => htmlspecialchars($this->getInput()['response_reason_text'])]);
ContributionRecur::update(FALSE)
->addWhere('id', '=', $this->getContributionRecurID())
->setValues([
Expand All @@ -56,11 +66,30 @@ public function main() {
\Civi::log('authorize_net')->info($errorMessage);
return;
}
if ($this->isSuccess() && ($this->getContributionStatus() !== 'Completed')) {
if ($this->getContributionStatus() !== 'Completed') {
ContributionRecur::update(FALSE)->addWhere('id', '=', $this->getContributionRecurID())
->setValues(['trxn_id' => $this->getRecurProcessorID()])->execute();
$contributionID = $this->getContributionID();
}
else {
$contribution = civicrm_api3('Contribution', 'repeattransaction', [
'contribution_recur_id' => $this->getContributionRecurID(),
'receive_date' => $this->getInput()['receive_date'],
'payment_processor_id' => $this->getPaymentProcessorID(),
'trxn_id' => $this->getInput()['trxn_id'],
'amount' => $this->getAmount(),
]);
$contributionID = $contribution['id'];
}
$this->recur();
civicrm_api3('Payment', 'create', [
'trxn_id' => $this->getInput()['trxn_id'],
'trxn_date' => $this->getInput()['receive_date'],
'payment_processor_id' => $this->getPaymentProcessorID(),
'contribution_id' => $contributionID,
'total_amount' => $this->getAmount(),
'is_send_contribution_notification' => $this->getContributionRecur()->is_email_receipt,
]);
$this->notify();
}
catch (CRM_Core_Exception $e) {
Civi::log('authorize_net')->debug($e->getMessage());
Expand All @@ -71,13 +100,11 @@ public function main() {
/**
* @throws \CRM_Core_Exception
*/
public function recur() {
public function notify() {
$recur = $this->getContributionRecur();
$input = $this->getInput();
$input['payment_processor_id'] = $this->getPaymentProcessorID();

$now = date('YmdHis');

$isFirstOrLastRecurringPayment = FALSE;
if ($this->isSuccess()) {
// Approved
Expand All @@ -93,7 +120,6 @@ public function recur() {
}
}

CRM_Contribute_BAO_Contribution::completeOrder($input, $recur->id, $this->getContributionStatus() !== 'Completed' ? $this->getContributionID() : NULL);
if ($isFirstOrLastRecurringPayment) {
//send recurring Notification email for user
CRM_Contribute_BAO_ContributionPage::recurringNotify($this->getContributionID(), TRUE,
Expand All @@ -117,7 +143,7 @@ public function getInput(): array {
$input['response_reason_text'] = $this->retrieve('x_response_reason_text', 'String', FALSE);
$input['subscription_paynum'] = $this->retrieve('x_subscription_paynum', 'Integer', FALSE, 0);
$input['trxn_id'] = $this->retrieve('x_trans_id', 'String', FALSE);
$input['receive_date'] = $this->retrieve('receive_date', 'String', FALSE, date('YmdHis', strtotime('now')));
$input['receive_date'] = $this->retrieve('receive_date', 'String', FALSE, date('YmdHis', time()));

if ($input['trxn_id']) {
$input['is_test'] = 0;
Expand All @@ -126,9 +152,11 @@ public function getInput(): array {
// Per CRM-17611 it would also not be passed back for a decline.
elseif ($this->isSuccess()) {
$input['is_test'] = 1;
$input['trxn_id'] = md5(uniqid(rand(), TRUE));
$input['trxn_id'] = $this->transactionID ?: md5(uniqid(mt_rand(), TRUE));
}
$this->transactionID = $input['trxn_id'];

// None of this is used...
$billingID = CRM_Core_BAO_LocationType::getBilling();
$params = [
'first_name' => 'x_first_name',
Expand All @@ -146,6 +174,17 @@ public function getInput(): array {
return $input;
}

/**
* Get amount.
*
* @return string
*
* @throws \CRM_Core_Exception
*/
protected function getAmount(): string {
return $this->retrieve('x_amount', 'String');
}

/**
* Was the transaction successful.
*
Expand Down Expand Up @@ -193,22 +232,26 @@ public function retrieve($name, $type, $abort = TRUE, $default = NULL) {
*/
protected function getContributionRecurObject(string $processorID, int $contactID, int $contributionID) {
// joining with contribution table for extra checks
$sql = "
$sql = '
SELECT cr.id, cr.contact_id
FROM civicrm_contribution_recur cr
INNER JOIN civicrm_contribution co ON co.contribution_recur_id = cr.id
WHERE cr.processor_id = '{$processorID}' AND
(cr.contact_id = $contactID OR co.id = $contributionID)
LIMIT 1";
$contRecur = CRM_Core_DAO::executeQuery($sql);
if (!$contRecur->fetch()) {
WHERE cr.processor_id = %1 AND
(cr.contact_id = %2 OR co.id = %3)
LIMIT 1';
$contributionRecur = CRM_Core_DAO::executeQuery($sql, [
1 => [$processorID, 'String'],
2 => [$contactID, 'Integer'],
3 => [$contributionID, 'Integer'],
]);
if (!$contributionRecur->fetch()) {
throw new CRM_Core_Exception('Could not find contributionRecur id');
}
if ($contactID != $contRecur->contact_id) {
$message = ts('Recurring contribution appears to have been re-assigned from id %1 to %2, continuing with %2.', [1 => $contactID, 2 => $contRecur->contact_id]);
CRM_Core_Error::debug_log_message($message);
if ($contactID != $contributionRecur->contact_id) {
$message = ts('Recurring contribution appears to have been re-assigned from id %1 to %2, continuing with %2.', [1 => $contactID, 2 => $contributionRecur->contact_id]);
\Civi::log('authorize_net')->warning($message);
}
return $contRecur;
return $contributionRecur;
}

/**
Expand Down Expand Up @@ -307,15 +350,18 @@ private function getContributionRecur(): CRM_Contribute_BAO_ContributionRecur {
* @throws \CRM_Core_Exception
*/
private function getContributionStatus(): string {
// Check if the contribution exists
// make sure contribution exists and is valid
$contribution = Contribution::get(FALSE)
->addWhere('id', '=', $this->getContributionID())
->addSelect('contribution_status_id:name')->execute()->first();
if (empty($contribution)) {
throw new CRM_Core_Exception('Failure: Could not find contribution record for ' . $this->getContributionID(), NULL, ['context' => 'Could not find contribution record: ' . $this->getContributionID() . ' in IPN request: ' . print_r($this->getInput(), TRUE)]);
if (!$this->contributionStatus) {
// Check if the contribution exists
// make sure contribution exists and is valid
$contribution = Contribution::get(FALSE)
->addWhere('id', '=', $this->getContributionID())
->addSelect('contribution_status_id:name')->execute()->first();
if (empty($contribution)) {
throw new CRM_Core_Exception('Failure: Could not find contribution record for ' . $this->getContributionID(), NULL, ['context' => 'Could not find contribution record: ' . $this->getContributionID() . ' in IPN request: ' . print_r($this->getInput(), TRUE)]);
}
$this->contributionStatus = $contribution['contribution_status_id:name'];
}
return $contribution['contribution_status_id:name'];
return $this->contributionStatus;
}

}
1 change: 1 addition & 0 deletions CRM/Financial/BAO/Payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ public static function create(array $params): CRM_Financial_DAO_FinancialTrxn {
'is_post_payment_create' => TRUE,
'is_email_receipt' => $params['is_send_contribution_notification'],
'trxn_date' => $params['trxn_date'],
'trxn_id' => $params['trxn_id'] ?? NULL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line has caused a regression and change in behaviour in CiviCRM core - see comment on main PR and test failures https://github.com/SemperIT/CiviCARROT/actions/runs/5370451060/jobs/9742545452

'payment_instrument_id' => $paymentTrxnParams['payment_instrument_id'],
'payment_processor_id' => $paymentTrxnParams['payment_processor_id'] ?? '',
]);
Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/CRM/Core/Payment/AuthorizeNetIPNTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function testIPNPaymentRecurNoReceipt(): void {
'billing_middle_name' => '',
'billing_last_name' => 'Adams',
'billing_street_address-5' => time() . ' Lincoln St S',
'billing_city-5' => 'Maryknoll',
'billing_city-5' => 'Mary-knoll',
'billing_state_province_id-5' => 1031,
'billing_postal_code-5' => 10545,
'billing_country_id-5' => 1228,
Expand Down