Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
Extensions
authnet
Commits
a58e2452
Commit
a58e2452
authored
Jun 07, 2019
by
mattwire
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement IPN/Webhooks
parent
4128a82d
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
886 additions
and
161 deletions
+886
-161
CRM/AuthorizeNet/Webhook.php
CRM/AuthorizeNet/Webhook.php
+91
-25
CRM/AuthorizeNet/WebhookTrait.php
CRM/AuthorizeNet/WebhookTrait.php
+5
-7
CRM/Core/Payment/AuthNetIPN.php
CRM/Core/Payment/AuthNetIPN.php
+173
-68
CRM/Core/Payment/AuthNetIPNTrait.php
CRM/Core/Payment/AuthNetIPNTrait.php
+185
-6
CRM/Core/Payment/AuthorizeNetCommon.php
CRM/Core/Payment/AuthorizeNetCommon.php
+3
-16
CRM/Core/Payment/AuthorizeNetTrait.php
CRM/Core/Payment/AuthorizeNetTrait.php
+97
-2
README.md
README.md
+0
-3
api/v3/Authnet.php
api/v3/Authnet.php
+52
-0
api/v3/Mjwpayment.php
api/v3/Mjwpayment.php
+210
-0
composer.lock
composer.lock
+15
-7
vendor/composer/installed.json
vendor/composer/installed.json
+16
-8
vendor/stymiee/authnetjson/CHANGELOG
vendor/stymiee/authnetjson/CHANGELOG
+5
-0
vendor/stymiee/authnetjson/composer.json
vendor/stymiee/authnetjson/composer.json
+14
-2
vendor/stymiee/authnetjson/src/authnet/AuthnetJsonResponse.php
...r/stymiee/authnetjson/src/authnet/AuthnetJsonResponse.php
+4
-4
vendor/stymiee/authnetjson/src/authnet/AuthnetWebhooksRequest.php
...tymiee/authnetjson/src/authnet/AuthnetWebhooksRequest.php
+4
-11
vendor/stymiee/authnetjson/tests/AuthnetWebhooksRequestTest.php
.../stymiee/authnetjson/tests/AuthnetWebhooksRequestTest.php
+12
-2
No files found.
CRM/AuthorizeNet/Webhook.php
View file @
a58e2452
...
...
@@ -7,15 +7,7 @@ use \JohnConde\Authnet\AuthnetApiFactory as AuthnetApiFactory;
class
CRM_AuthorizeNet_Webhook
{
use
CRM_AuthorizeNet_WebhookTrait
;
/**
* Get the constant for test/live mode when using JohnConde\Authnet library
*
* @return int
*/
protected
function
getIsTestMode
()
{
return
isset
(
$this
->
_paymentProcessor
[
'is_test'
])
&&
$this
->
_paymentProcessor
[
'is_test'
]
?
AuthnetApiFactory
::
USE_DEVELOPMENT_SERVER
:
AuthnetApiFactory
::
USE_PRODUCTION_SERVER
;
}
use
CRM_Core_Payment_AuthorizeNetTrait
;
/**
* CRM_AuthorizeNet_Webhook constructor.
...
...
@@ -26,20 +18,76 @@ class CRM_AuthorizeNet_Webhook {
$this
->
_paymentProcessor
=
$paymentProcessor
;
}
/**
* Get a request handler for authnet webhooks
*
* @return \JohnConde\Authnet\AuthnetWebhooksRequest
* @throws \ErrorException
* @throws \JohnConde\Authnet\AuthnetInvalidCredentialsException
* @throws \JohnConde\Authnet\AuthnetInvalidServerException
*/
public
function
getRequest
()
{
return
AuthnetApiFactory
::
getWebhooksHandler
(
CRM_Core_Payment_AuthorizeNetCommon
::
getApiLoginId
(
$this
->
_paymentProcessor
),
CRM_Core_Payment_AuthorizeNetCommon
::
getTransactionKey
(
$this
->
_paymentProcessor
),
$this
->
getIsTestMode
());
$this
->
getIsTestMode
()
?
AuthnetApiFactory
::
USE_DEVELOPMENT_SERVER
:
AuthnetApiFactory
::
USE_PRODUCTION_SERVER
);
}
/**
* Get a list of configured webhooks
*
* @return \JohnConde\Authnet\AuthnetWebhooksResponse
* @throws \ErrorException
* @throws \JohnConde\Authnet\AuthnetCurlException
* @throws \JohnConde\Authnet\AuthnetInvalidCredentialsException
* @throws \JohnConde\Authnet\AuthnetInvalidJsonException
* @throws \JohnConde\Authnet\AuthnetInvalidServerException
*/
public
function
getWebhooks
()
{
$request
=
$this
->
getRequest
();
return
$request
->
getWebhooks
();
}
/**
* Create a new webhook
*
* @throws \ErrorException
* @throws \JohnConde\Authnet\AuthnetCurlException
* @throws \JohnConde\Authnet\AuthnetInvalidCredentialsException
* @throws \JohnConde\Authnet\AuthnetInvalidJsonException
* @throws \JohnConde\Authnet\AuthnetInvalidServerException
*/
public
function
createWebhook
()
{
$request
=
$this
->
getRequest
();
$request
->
createWebhooks
(
self
::
getDefaultEnabledEvents
(),
self
::
getWebhookPath
(
TRUE
,
$this
->
_paymentProcessor
[
'id'
]),
'active'
);
$request
->
createWebhooks
(
self
::
getDefaultEnabledEvents
(),
self
::
getWebhookPath
(
$this
->
_paymentProcessor
[
'id'
]),
'active'
);
}
/**
* Check and update existing webhook
*
* @param array $webhook
*/
/**
* @param \JohnConde\Authnet\AuthnetWebhooksResponse $webhook
*
* @throws \ErrorException
* @throws \JohnConde\Authnet\AuthnetCurlException
* @throws \JohnConde\Authnet\AuthnetInvalidCredentialsException
* @throws \JohnConde\Authnet\AuthnetInvalidJsonException
* @throws \JohnConde\Authnet\AuthnetInvalidServerException
*/
public
function
checkAndUpdateWebhook
(
$webhook
)
{
$update
=
FALSE
;
if
(
$webhook
->
getStatus
()
!==
'active'
)
{
$update
=
TRUE
;
}
if
(
array_diff
(
self
::
getDefaultEnabledEvents
(),
$webhook
->
getEventTypes
()))
{
$update
=
TRUE
;
}
if
(
$update
)
{
$request
=
$this
->
getRequest
();
$request
->
updateWebhook
([
$webhook
->
getWebhooksId
()],
self
::
getWebhookPath
(
$this
->
_paymentProcessor
[
'id'
]),
self
::
getDefaultEnabledEvents
(),
'active'
);
}
}
/**
...
...
@@ -48,6 +96,14 @@ class CRM_AuthorizeNet_Webhook {
* for now, avoid having false alerts that will annoy people).
*
* @see hook_civicrm_check()
*
* @return array
* @throws \CiviCRM_API3_Exception
* @throws \ErrorException
* @throws \JohnConde\Authnet\AuthnetCurlException
* @throws \JohnConde\Authnet\AuthnetInvalidCredentialsException
* @throws \JohnConde\Authnet\AuthnetInvalidJsonException
* @throws \JohnConde\Authnet\AuthnetInvalidServerException
*/
public
static
function
check
()
{
$checkMessage
=
[
...
...
@@ -60,10 +116,11 @@ class CRM_AuthorizeNet_Webhook {
]);
foreach
(
$result
[
'values'
]
as
$paymentProcessor
)
{
$webhook_path
=
self
::
getWebhookPath
(
TRUE
,
$paymentProcessor
[
'id'
]);
$webhook_path
=
self
::
getWebhookPath
(
$paymentProcessor
[
'id'
]);
try
{
$webhookHandler
=
new
CRM_AuthorizeNet_Webhook
(
$paymentProcessor
);
/** @var JohnConde\Authnet\AuthnetWebhooksResponse $webhooks */
$webhooks
=
$webhookHandler
->
getWebhooks
();
}
catch
(
Exception
$e
)
{
...
...
@@ -83,18 +140,18 @@ class CRM_AuthorizeNet_Webhook {
continue
;
}
$found
_wh
=
FALSE
;
foreach
(
$webhooks
->
data
as
$wh
)
{
if
(
$w
h
->
url
==
$webhook_path
)
{
$found
_wh
=
TRUE
;
$found
Webhook
=
FALSE
;
foreach
(
$webhooks
->
getWebhooks
()
as
$webhook
)
{
if
(
$w
ebhook
->
getURL
()
==
$webhook_path
)
{
$found
Webhook
=
TRUE
;
// Check and update webhook
$webhookHandler
->
checkAndUpdateWebhook
(
$w
h
);
$webhookHandler
->
checkAndUpdateWebhook
(
$w
ebhook
);
}
}
if
(
!
$found
_wh
)
{
if
(
!
$found
Webhook
)
{
try
{
$webhookHandler
->
createWebhook
(
$paymentProcessor
[
'id'
]
);
$webhookHandler
->
createWebhook
();
}
catch
(
Exception
$e
)
{
$messages
[]
=
new
CRM_Utils_Check_Message
(
...
...
@@ -123,13 +180,22 @@ class CRM_AuthorizeNet_Webhook {
* @return array
*/
public
static
function
getDefaultEnabledEvents
()
{
// See https://developer.authorize.net/api/reference/features/webhooks.html#Event_Types_and_Payloads
return
[
'net.authorize.payment.authorization.created'
,
'net.authorize.payment.capture.created'
,
'net.authorize.payment.authcapture.created'
,
'net.authorize.payment.priorAuthCapture.created'
,
'net.authorize.payment.refund.created'
,
'net.authorize.payment.void.created'
'net.authorize.payment.authcapture.created'
,
// Notifies you that an authorization and capture transaction was created.
'net.authorize.payment.refund.created'
,
// Notifies you that a successfully settled transaction was refunded.
'net.authorize.payment.void.created'
,
// Notifies you that an unsettled transaction was voided.
//'net.authorize.customer.subscription.created', // Notifies you that a subscription was created.
//'net.authorize.customer.subscription.updated', // Notifies you that a subscription was updated.
//'net.authorize.customer.subscription.suspended',// Notifies you that a subscription was suspended.
'net.authorize.customer.subscription.terminated'
,
// Notifies you that a subscription was terminated.
'net.authorize.customer.subscription.cancelled'
,
// Notifies you that a subscription was cancelled.
//'net.authorize.customer.subscription.expiring', // Notifies you when a subscription has only one recurrence left to be charged.
'net.authorize.payment.fraud.held'
,
// Notifies you that a transaction was held as suspicious.
'net.authorize.payment.fraud.approved'
,
// Notifies you that a previously held transaction was approved.
'net.authorize.payment.fraud.declined'
,
// Notifies you that a previously held transaction was declined.
];
}
...
...
CRM/AuthorizeNet/WebhookTrait.php
View file @
a58e2452
...
...
@@ -18,17 +18,17 @@ trait CRM_AuthorizeNet_WebhookTrait {
*
* @return string
*/
public
static
function
getWebhookPath
(
$includeBaseUrl
=
TRUE
,
$paymentProcessorId
=
'NN'
)
{
public
static
function
getWebhookPath
(
$paymentProcessorId
=
'NN'
)
{
// Assuming frontend URL because that's how the function behaved before.
// @fixme this doesn't return the right webhook path on Wordpress (often includes an extra path between .com and ? eg. abc.com/xxx/?page=CiviCRM
// We can't use CRM_Utils_System::url('civicrm/payment/ipn/' . $paymentProcessorId, NULL, $includeBaseUrl, NULL, FALSE, TRUE);
// because it returns the query string urlencoded and the base URL non urlencoded so we can't use to match existing webhook URLs
$UFWebhookPaths
=
[
/*
$UFWebhookPaths = [
"Drupal" => "civicrm/payment/ipn/{$paymentProcessorId}",
"Joomla" => "?option=com_civicrm&task=civicrm/payment/ipn/{$paymentProcessorId}",
"WordPress" => "?page=CiviCRM&q=civicrm/payment/ipn/{$paymentProcessorId}"
];
];
*/
$basePage
=
''
;
$config
=
CRM_Core_Config
::
singleton
();
...
...
@@ -37,10 +37,8 @@ trait CRM_AuthorizeNet_WebhookTrait {
$basePage
=
(
substr
(
$config
->
wpBasePage
,
-
1
)
==
'/'
)
?
$config
->
wpBasePage
:
"
$config->wpBasePage
/"
;
}
// Use Drupal path as default if the UF isn't in the map above
$UFWebhookPath
=
(
array_key_exists
(
CIVICRM_UF
,
$UFWebhookPaths
))
?
$UFWebhookPaths
[
CIVICRM_UF
]
:
$UFWebhookPaths
[
'Drupal'
];
if
(
$includeBaseUrl
)
{
return
CRM_Utils_System
::
baseURL
()
.
$basePage
.
$UFWebhookPath
;
}
//$UFWebhookPath = (array_key_exists(CIVICRM_UF, $UFWebhookPaths)) ? $UFWebhookPaths[CIVICRM_UF] : $UFWebhookPaths['Drupal'];
$UFWebhookPath
=
CRM_Utils_System
::
url
(
'civicrm/payment/ipn/'
.
$paymentProcessorId
,
NULL
,
TRUE
,
NULL
,
FALSE
,
TRUE
);
return
$UFWebhookPath
;
}
...
...
CRM/Core/Payment/AuthNetIPN.php
View file @
a58e2452
<?php
/*
* @file
* Handle Twocheckout Webhooks for recurring payments.
/**
* https://civicrm.org/licensing
*/
use
CRM_AuthNetEcheck_ExtensionUtil
as
E
;
use
\
JohnConde\Authnet\AuthnetWebhook
as
AuthnetWebhook
;
use
\
JohnConde\Authnet\AuthnetApiFactory
as
AuthnetApiFactory
;
use
\
JohnConde\Authnet\AuthnetWebhooksResponse
as
AuthnetWebhooksResponse
;
class
CRM_Core_Payment_AuthNetIPN
extends
CRM_Core_Payment_BaseIPN
{
use
CRM_Core_Payment_AuthNetIPNTrait
;
use
CRM_Core_Payment_AuthorizeNetTrait
;
use
CRM_Core_Payment_AuthNetIPNTrait
;
/**
* Authorize.net webhook transaction ID
...
...
@@ -19,17 +21,22 @@ class CRM_Core_Payment_AuthNetIPN extends CRM_Core_Payment_BaseIPN {
*/
private
$trxnId
;
/**
* Get the transaction ID
* @return string
*/
private
function
getTransactionId
()
{
return
$this
->
trxnId
;
}
/**
* CRM_Core_Payment_
Twocheckou
tIPN constructor.
* CRM_Core_Payment_
AuthNe
tIPN constructor.
*
* @param $ipnData
* @param
bool $verify
* @param
string
$ipnData
* @param
array $headers
*
* @throws \CRM_Core_Exception
* @throws \JohnConde\Authnet\AuthnetInvalidCredentialsException
* @throws \JohnConde\Authnet\AuthnetInvalidJsonException
*/
public
function
__construct
(
$ipnData
,
$headers
)
{
$this
->
_params
=
$ipnData
;
...
...
@@ -44,84 +51,182 @@ class CRM_Core_Payment_AuthNetIPN extends CRM_Core_Payment_BaseIPN {
}
/**
* @
throws \CRM_Core_Exception
* @
return bool
* @throws \CiviCRM_API3_Exception
* @throws \ErrorException
* @throws \JohnConde\Authnet\AuthnetInvalidCredentialsException
* @throws \JohnConde\Authnet\AuthnetInvalidServerException
*/
public
function
main
()
{
// Here you can get more information about the transaction
$request
=
AuthnetApiFactory
::
getJsonApiHandler
(
CRM_Core_Payment_AuthorizeNetCommon
::
getApiLoginId
(
$this
->
_paymentProcessor
),
CRM_Core_Payment_AuthorizeNetCommon
::
getTransactionKey
(
$this
->
_paymentProcessor
));
$request
=
AuthnetApiFactory
::
getJsonApiHandler
(
CRM_Core_Payment_AuthorizeNetCommon
::
getApiLoginId
(
$this
->
_paymentProcessor
),
CRM_Core_Payment_AuthorizeNetCommon
::
getTransactionKey
(
$this
->
_paymentProcessor
),
$this
->
getIsTestMode
()
?
AuthnetApiFactory
::
USE_DEVELOPMENT_SERVER
:
AuthnetApiFactory
::
USE_PRODUCTION_SERVER
);
/** @var AuthnetWebhooksResponse $response */
$response
=
$request
->
getTransactionDetailsRequest
([
'transId'
=>
$this
->
getTransactionId
()]);
/* You can put these response values in the database or whatever your business logic dictates.
$response->transaction->transactionType
$response->transaction->transactionStatus
$response->transaction->authCode
$response->transaction->AVSResponse
*/
$verify
=
Twocheckout_Notification
::
check
(
$this
->
_params
,
$this
->
getSecretWord
());
if
(
$verify
[
'response_code'
]
!==
'Success'
)
{
$this
->
handleError
(
$verify
[
'response_code'
],
$verify
[
'response_message'
]);
return
FALSE
;
if
(
$response
->
messages
->
resultCode
!==
'Ok'
)
{
$this
->
exception
(
'Bad response from getTransactionDetailsRequest in IPN handler'
);
}
// We need a contribution ID - from the transactionID (invoice ID)
try
{
// Same approach as api repeattransaction.
$contribution
=
civicrm_api3
(
'contribution'
,
'getsingle'
,
[
'return'
=>
[
'id'
,
'contribution_status_id'
,
'total_amount'
,
'trxn_id'
],
'contribution_test'
=>
$this
->
getIsTestMode
(),
'options'
=>
[
'limit'
=>
1
,
'sort'
=>
'id DESC'
],
'trxn_id'
=>
$this
->
getParam
(
'invoice_id'
),
]);
$contributionId
=
$contribution
[
'id'
];
// Set parameters required for IPN functions
if
(
$this
->
getParamFromResponse
(
$response
,
'is_recur'
))
{
$this
->
contribution_recur_id
=
$this
->
getRecurringContributionIDFromSubscriptionID
(
$this
->
getParamFromResponse
(
$response
,
'subscription_id'
));
}
catch
(
Exception
$e
)
{
$this
->
exception
(
'Cannot find any contributions with invoice ID: '
.
$this
->
getParam
(
'invoice_id'
)
.
'. '
.
$e
->
getMessage
());
}
// See https://www.2checkout.com/documentation/notifications
switch
(
$this
->
getParam
(
'message_type'
))
{
case
'FRAUD_STATUS_CHANGED'
:
switch
(
$this
->
getParam
(
'fraud_status'
))
{
case
'pass'
:
// Do something when sale passes fraud review.
// The last one was not completed, so complete it.
civicrm_api3
(
'Contribution'
,
'completetransaction'
,
array
(
'id'
=>
$contributionId
,
'payment_processor_id'
=>
$this
->
_paymentProcessor
[
'id'
],
'is_email_receipt'
=>
$this
->
getSendEmailReceipt
(),
));
break
;
case
'fail'
:
// Do something when sale fails fraud review.
$this
->
failtransaction
([
'id'
=>
$contributionId
,
'payment_processor_id'
=>
$this
->
_paymentProcessor
[
'id'
]
]);
break
;
case
'wait'
:
// Do something when sale requires additional fraud review.
// Do nothing, we'll remain in Pending.
break
;
$this
->
event_type
=
$response
->
transaction
->
transactionType
;
// Process the event
switch
(
$response
->
transaction
->
transactionType
)
{
case
'net.authorize.payment.authcapture.created'
:
// Notifies you that an authorization and capture transaction was created.
if
(
$this
->
getParamFromResponse
(
$response
,
'is_recur'
))
{
$params
=
[
'contribution_id'
=>
$this
->
getContributionIDFromInvoiceID
(
$this
->
getParamFromResponse
(
$response
,
'invoice_id'
)),
'contribution_recur_id'
=>
$this
->
contribution_recur_id
,
'payment_processor_transaction_id'
=>
$this
->
getParamFromResponse
(
$response
,
'transaction_id'
),
];
$this
->
recordCompleted
(
$params
);
}
else
{
$params
=
[
'contribution_id'
=>
$this
->
getContributionIDFromInvoiceID
(
$this
->
getParamFromResponse
(
$response
,
'invoice_id'
)),
];
}
$this
->
recordCompleted
(
$params
);
break
;
case
'REFUND_ISSUED'
:
// To be implemented
case
'net.authorize.payment.refund.created'
:
// Notifies you that a successfully settled transaction was refunded.
$params
=
[
'contribution_id'
=>
$this
->
getContributionIDFromInvoiceID
(
$this
->
getParamFromResponse
(
$response
,
'invoice_id'
)),
'total_amount'
=>
$this
->
getParamFromResponse
(
$response
,
'refund_amount'
),
];
$this
->
recordRefund
(
$params
);
break
;
case
'net.authorize.payment.void.created'
:
// Notifies you that an unsettled transaction was voided.
$params
=
[
'contribution_id'
=>
$this
->
getContributionIDFromInvoiceID
(
$this
->
getParamFromResponse
(
$response
,
'invoice_id'
)),
];
$this
->
recordCancelled
(
$params
);
break
;
case
'net.authorize.payment.fraud.held'
:
// Notifies you that a transaction was held as suspicious.
$params
=
[
'contribution_id'
=>
$this
->
getContributionIDFromInvoiceID
(
$this
->
getParamFromResponse
(
$response
,
'invoice_id'
)),
];
$this
->
recordPending
(
$params
);
break
;
case
'net.authorize.payment.fraud.approved'
:
// Notifies you that a previously held transaction was approved.
$params
=
[
'contribution_id'
=>
$this
->
getContributionIDFromInvoiceID
(
$this
->
getParamFromResponse
(
$response
,
'invoice_id'
)),
];
$this
->
recordCompleted
(
$params
);
break
;
case
'net.authorize.payment.fraud.declined'
:
// Notifies you that a previously held transaction was declined.
$params
=
[
'contribution_id'
=>
$this
->
getContributionIDFromInvoiceID
(
$this
->
getParamFromResponse
(
$response
,
'invoice_id'
)),
];
$this
->
recordFailed
(
$params
);
break
;
// Now the "subscription" (recurring) ones
// case 'net.authorize.customer.subscription.created':
// Notifies you that a subscription was created.
// case 'net.authorize.customer.subscription.updated':
// Notifies you that a subscription was updated.
// case 'net.authorize.customer.subscription.suspended':
// Notifies you that a subscription was suspended.
case
'net.authorize.customer.subscription.terminated'
:
// Notifies you that a subscription was terminated.
case
'net.authorize.customer.subscription.cancelled'
:
// Notifies you that a subscription was cancelled.
$this
->
recordSubscriptionCancelled
();
break
;
// case 'net.authorize.customer.subscription.expiring':
// Notifies you when a subscription has only one recurrence left to be charged.
}
// Unhandled event type.
return
TRUE
;
}
public
function
exception
(
$message
)
{
$errorMessage
=
$this
->
getPaymentProcessorLabel
()
.
' Exception: Event: '
.
$this
->
event_type
.
' Error: '
.
$message
;
Civi
::
log
()
->
debug
(
$errorMessage
);
http_response_code
(
400
);
exit
(
1
);
/**
* Retrieve parameters from IPN response
*
* @param AuthnetWebhooksResponse $response
* @param string $param
*
* @return mixed
*/
protected
function
getParamFromResponse
(
$response
,
$param
)
{
switch
(
$param
)
{
case
'transaction_id'
:
return
$response
->
transaction
->
transId
;
case
'invoice_id'
:
return
$response
->
transaction
->
order
->
invoiceNumber
;
case
'refund_amount'
:
// @todo: Check that this is the correct parameter?
return
$response
->
transaction
->
refundAmount
;
case
'is_recur'
:
return
$response
->
transaction
->
recurringBilling
;
case
'subscription_id'
:
return
$response
->
transaction
->
subscription
->
id
;
case
'subscription_payment_number'
:
return
$response
->
transaction
->
subscription
->
payNum
;
}
}
/**
* Get the contribution ID from the paymentprocessor invoiceID.
* For AuthorizeNet we save the 20character invoice ID into the contribution trxn_id
*
* @param string $invoiceID
*
* @return int
* @throws \CiviCRM_API3_Exception
*/
protected
function
getContributionIDFromInvoiceID
(
$invoiceID
)
{
$contribution
=
civicrm_api3
(
'Contribution'
,
'get'
,
[
'trxn_id'
=>
[
'LIKE'
=>
"
{
$invoiceID
}
%"
],
'options'
=>
[
'limit'
=>
1
,
'sort'
=>
"id DESC"
],
]);
if
(
empty
(
$contribution
[
'id'
]))
{
$this
->
exception
(
"Could not find matching contribution for invoice ID:
{
$invoiceID
}
"
);
}
return
$contribution
[
'id'
];
}
/**
* @param $subscriptionID
*
* @return int
* @throws \CiviCRM_API3_Exception
*/
protected
function
getRecurringContributionIDFromSubscriptionID
(
$subscriptionID
)
{
$contributionRecur
=
civicrm_api3
(
'ContributionRecur'
,
'get'
,
[
'trxn_id'
=>
$subscriptionID
,
]);
if
(
empty
(
$contributionRecur
[
'id'
]))
{
$this
->
exception
(
"Could not find matching contribution for invoice ID:
{
$subscriptionID
}
"
);
}
return
$contributionRecur
[
'id'
];
}
}
CRM/Core/Payment/AuthNetIPNTrait.php
View file @
a58e2452
<?php
/**
* https://civicrm.org/licensing
*/
/**
* Shared payment IPN functions that should one day be migrated to CiviCRM core
*/
trait
CRM_Core_Payment_AuthNetIPNTrait
{
/**********************
* Version 20190524
* MJW_Core_Payment_IPNTrait: 20190707
* @requires MJW_Payment_Api: 20190707
*********************/
/**
...
...
@@ -13,11 +18,6 @@ trait CRM_Core_Payment_AuthNetIPNTrait {
*/
private
$_paymentProcessor
;
/**
* @var array Params sent by IPN callback
*/
private
$_params
;
/**
* Do we send an email receipt for each contribution?
*
...
...
@@ -25,6 +25,18 @@ trait CRM_Core_Payment_AuthNetIPNTrait {
*/
protected
$is_email_receipt
=
NULL
;
/**
* The recurring contribution ID associated with the transaction
* @var int
*/
protected
$contribution_recur_id
=
NULL
;
/**
* The IPN event type
* @var string
*/
protected
$event_type
=
NULL
;
/**
* Set the value of is_email_receipt to use when a new contribution is received for a recurring contribution
* If not set, we respect the value set on the ContributionRecur entity.
...
...
@@ -85,6 +97,7 @@ trait CRM_Core_Payment_AuthNetIPNTrait {
}
/**
* @deprecated Use recordCancelled()
* Mark a contribution as cancelled and update related entities
*
* @param array $params [ 'id' -> contribution_id, 'payment_processor_id' -> payment_processor_id]
...
...
@@ -97,6 +110,7 @@ trait CRM_Core_Payment_AuthNetIPNTrait {
}
/**
* @deprecated - Use recordFailed()
* Mark a contribution as failed and update related entities
*
* @param array $params [ 'id' -> contribution_id, 'payment_processor_id' -> payment_processor_id]
...
...
@@ -109,6 +123,7 @@ trait CRM_Core_Payment_AuthNetIPNTrait {
}
/**
* @deprecated - Use recordXX methods
* Handler for failtransaction and canceltransaction - do not call directly
*
* @param array $params
...
...
@@ -157,7 +172,171 @@ trait CRM_Core_Payment_AuthNetIPNTrait {
default
:
throw
new
CiviCRM_API3_Exception
(
'Unknown incomplete transaction type: '
.
$mode
);
}
}
protected
function
recordPending
(
$params
)
{
// Nothing to do
// @todo Maybe in the future record things like the pending reason if a payment is temporarily held?
}
/**
* Record a completed (successful) contribution
* @param array $params
*
* @throws \CiviCRM_API3_Exception
*/
protected
function
recordCompleted
(
$params
)
{
$description
=
'recordCompleted'
;
$this
->
checkRequiredParams
(
$description
,
[
'contribution_id'
],
$params
);
$contributionStatusID
=
CRM_Core_PseudoConstant
::
getKey
(
'CRM_Contribute_BAO_Contribution'
,
'contribution_status_id'
,
'Completed'
);
if
(
!
empty
(
$params
[
'contribution_recur_id'
]))
{
$this
->
recordRecur
(
$params
,
$description
,
$contributionStatusID
);
}
else
{
$params
[
'id'
]
=
$params
[
'contribution_id'
];
$pendingStatusId
=
CRM_Core_PseudoConstant
::
getKey
(
'CRM_Contribute_BAO_Contribution'
,
'contribution_status_id'
,
'Pending'
);
if
(
civicrm_api3
(
'Contribution'
,
'getvalue'
,
[
'return'
=>
"contribution_status_id"
,
'id'
=>
$params
[
'id'
]
])
==
$pendingStatusId
)
{
civicrm_api3
(
'Contribution'
,
'completetransaction'
,
$params
);
}
}
}
/**
* Record a failed contribution
* @param array $params
*
* @throws \CiviCRM_API3_Exception
*/
protected
function
recordFailed
(
$params
)
{
$description
=
'recordFailed'
;
$this
->
checkRequiredParams
(
$description
,
[
'contribution_id'
],
$params
);
$contributionStatusID
=
CRM_Core_PseudoConstant
::
getKey
(
'CRM_Contribute_BAO_Contribution'
,
'contribution_status_id'
,
'Failed'
);