diff --git a/au_com_agileware_ewayrecurring.class.php b/CRM/Core/Payment/eWAYRecurring.php
similarity index 96%
rename from au_com_agileware_ewayrecurring.class.php
rename to CRM/Core/Payment/eWAYRecurring.php
index 8f9bad2..3a5afba 100644
--- a/au_com_agileware_ewayrecurring.class.php
+++ b/CRM/Core/Payment/eWAYRecurring.php
@@ -1,19 +1,29 @@
$domain, 'id' => $id ] = $paymentProcessor;
+
+ if(empty(self::$instances["$domain/$id"])) {
+ $mode = empty( $paymentProcessor['is_test'] ) ? 'test' : 'live';
+ self::$instances["$domain/$id"] = new self( $mode, $paymentProcessor );
+ }
+
+ return self::$instances["$domain/$id"];
+ }
/**
* Constructor
@@ -28,24 +38,6 @@ function __construct($mode, &$paymentProcessor) {
$this->_processorName = ts('eWay Recurring');
}
- /**
- * Create eWay client using credentials from payment processor.
- *
- * @return \Eway\Rapid\Contract\Client
- */
- function getEWayClient() {
- if(!$this->eWayClient) {
- $eWayApiKey = $this->_paymentProcessor['user_name']; // eWay Api Key
- $eWayApiPassword = $this->_paymentProcessor['password']; // eWay Api Password
- $eWayEndPoint = ($this->_paymentProcessor['is_test']) ? \Eway\Rapid\Client::MODE_SANDBOX : \Eway\Rapid\Client::MODE_PRODUCTION;
-
-
- $this->eWayClient = \Eway\Rapid::createClient($eWayApiKey, $eWayApiPassword, $eWayEndPoint);
- }
-
- return $this->eWayClient;
- }
-
/**
* Validate contribution on successful response.
*
@@ -118,7 +110,7 @@ function getPaymentFormFieldsMetadata() {
$jsSetting .= "CRM.eway.ppid = {$this->_paymentProcessor['id']};";
$jsSetting .= 'CRM.eway.paymentTokenInitialize();';
- CRM_Core_Resources::singleton()->addScript($jsSetting);
+ CRM_Core_Resources::singleton()->addScript($jsSetting, ['weight' => 10, 'region' => 'page-footer']);
$this->jsEmbedded = TRUE;
}
return [
@@ -669,7 +661,7 @@ function checkConfig() {
* @return bool
*/
function handlePaymentCron() {
- return process_recurring_payments($this->_paymentProcessor, $this);
+ return $this->process_recurring_payments( $this->_paymentProcessor );
}
/**
diff --git a/CRM/eWAYRecurring/Page/VerifyPayment.php b/CRM/eWAYRecurring/Page/VerifyPayment.php
index fdc939f..0ae3dd2 100644
--- a/CRM/eWAYRecurring/Page/VerifyPayment.php
+++ b/CRM/eWAYRecurring/Page/VerifyPayment.php
@@ -24,11 +24,11 @@ public function run() {
if (count($paymentProcessorInfo) > 0) {
$paymentProcessorInfo = $paymentProcessorInfo[0];
- $paymentProcessor = new au_com_agileware_ewayrecurring(($paymentProcessorInfo['is_test']) ? 'test' : 'live', $paymentProcessorInfo);
+ $paymentProcessor = new CRM_Core_Payment_eWAYRecurring(($paymentProcessorInfo['is_test']) ? 'test' : 'live', $paymentProcessorInfo);
try {
// This function will do redirect if the payment failed
- $response = validateEwayContribution($paymentProcessor, $contributionInvoiceID);
+ $response = CRM_eWAYRecurring_Utils::validateEwayContribution($paymentProcessor, $contributionInvoiceID);
} catch (CRM_Core_Exception $e) {
} catch (CiviCRM_API3_Exception $e) {
}
diff --git a/CRM/eWAYRecurring/PaymentToken.php b/CRM/eWAYRecurring/PaymentToken.php
index f299861..6822189 100644
--- a/CRM/eWAYRecurring/PaymentToken.php
+++ b/CRM/eWAYRecurring/PaymentToken.php
@@ -1,5 +1,10 @@
$data) {
+ foreach (array_keys($_POST) as $key) {
+ $data = CRM_Utils_Request::retrieve($key, 'String');
if (strpos($key, 'billing_street_address') !== FALSE) {
$billingDetails['billing_street_address'] = $data;
}
@@ -288,7 +296,77 @@ public static function getPaymentProcessorById($id) {
return NULL;
}
$paymentProcessorInfo = $paymentProcessorInfo[0];
- $paymentProcessor = new au_com_agileware_ewayrecurring(($paymentProcessorInfo['is_test']) ? 'test' : 'live', $paymentProcessorInfo);
- return $paymentProcessor->getPaymentProcessor();
+
+ return CRM_Core_Payment_eWAYRecurring::getInstance($paymentProcessorInfo)
+ ->getPaymentProcessor();
+ }
+
+ public static function fillTokensMeta() {
+ // Caches payment processors
+ $processors = [];
+
+ $results = ['count' => 0];
+
+ $tokens = PaymentToken::get(FALSE)
+ ->addWhere('payment_processor_id.payment_processor_type_id.name', '=', 'eWay_Recurring')
+ // Unretrieved tokens can end up with a saved token with id 0
+ // ->addWhere('token', '!=', '0')
+ ->addClause('OR', [
+ 'expiry_date',
+ 'IS NULL'
+ ], [
+ 'masked_account_number',
+ 'IS NULL'
+ ])
+ ->execute();
+
+ foreach ($tokens as $token) {
+ // Get Login details for eWAY from payment processor
+ $processor = $processors[$token['payment_processor_id']] ??=
+ PaymentProcessor::get(FALSE)
+ ->addWhere('id', '=', $token['payment_processor_id'])
+ ->execute()->first();
+
+ $eway_client = $processor['eway_client'] ??= CRM_eWAYRecurring_Utils::getEWayClient($processor);
+
+ // Skip if unable to log in to eWAY
+ if($eway_client->getErrors()) {
+ continue;
+ }
+
+ $token_customer = $eway_client->queryCustomer($token['token']);
+
+ // Skip if custom query fails
+ $errors = $token_customer->getErrors();
+
+ if($errors){
+ foreach($errors as &$error) {
+ $error = E::ts('Error retrieving data for token id %1: %2', [1 => $token['id'], 2 => $error]);
+ }
+
+ $result['errors'] = array_merge($result['errors'] ?? [], $errors);
+ $result['is_error'] = TRUE;
+ continue;
+ }
+
+ $card_details = $token_customer->Customers[0]->CardDetails;
+
+ $card_number = $card_details->Number;
+
+ $expiry_date = new DateTime('00:00:00.000');
+ $expiry_date->setDate(2000 + (int) $card_details->ExpiryYear, $card_details->ExpiryMonth, 1);
+ $expiry_date->modify('+ 1 month - 1 second');
+
+ $expiry_date = $expiry_date->format('Ymd');
+
+ $update = PaymentToken::update(FALSE)
+ ->addWhere('id', '=', $token['id'])
+ ->addValue('masked_account_number', $card_number)
+ ->addValue('expiry_date', $expiry_date)
+ ->execute();
+
+ $results['count']++;
+ }
+ return $results;
}
-}
\ No newline at end of file
+}
diff --git a/CRM/eWAYRecurring/ProcessTrait.php b/CRM/eWAYRecurring/ProcessTrait.php
new file mode 100644
index 0000000..64b1466
--- /dev/null
+++ b/CRM/eWAYRecurring/ProcessTrait.php
@@ -0,0 +1,610 @@
+eWayClient) {
+ $eWayApiKey = $this->_paymentProcessor['user_name']; // eWay Api Key
+ $eWayApiPassword = $this->_paymentProcessor['password']; // eWay Api Password
+ $eWayEndPoint = ($this->_paymentProcessor['is_test']) ? \Eway\Rapid\Client::MODE_SANDBOX : \Eway\Rapid\Client::MODE_PRODUCTION;
+
+ $this->eWayClient = \Eway\Rapid::createClient($eWayApiKey, $eWayApiPassword, $eWayEndPoint);
+ }
+
+ return $this->eWayClient;
+ }
+
+ public function process_recurring_payments() {
+ // If an ewayrecurring job is already running, we want to exit as soon as possible.
+ $lock = \Civi\Core\Container::singleton()
+ ->get('lockManager')
+ ->create('worker.ewayrecurring');
+ if (!$lock->isFree() || !$lock->acquire()) {
+ Civi::log()
+ ->warning("Detected processing race for scheduled payments, aborting");
+
+ return FALSE;
+ }
+
+ // Process today's scheduled contributions.
+ $scheduled_contributions = $this->get_scheduled_contributions();
+ $scheduled_failed_contributions = $this->get_scheduled_failed_contributions();
+
+ $scheduled_contributions = array_merge($scheduled_failed_contributions, $scheduled_contributions);
+
+ foreach ($scheduled_contributions as $contribution) {
+ if ($contribution->payment_processor_id != $this->getPaymentProcessor()['id']) {
+ continue;
+ }
+
+ // Re-check schedule time, in case contribution already processed.
+ $next_sched = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionRecur',
+ $contribution->id,
+ 'next_sched_contribution_date',
+ 'id',
+ TRUE);
+
+ /* Get the number of Contributions already recorded for this Schedule. */
+ $mainContributions = civicrm_api3('Contribution', 'get', [
+ 'options' => ['limit' => 0],
+ 'sequential' => 1,
+ 'return' => ['total_amount', 'tax_amount'],
+ 'contribution_recur_id' => $contribution->id,
+ ]);
+
+ $mainContributions = $mainContributions['values'];
+ $ccount = count($mainContributions);
+
+ /* Schedule next contribution */
+ if (($contribution->installments <= 0) || ($contribution->installments > $ccount + 1)) {
+ $next_sched = date('Y-m-d 00:00:00', strtotime($next_sched . " +{$contribution->frequency_interval} {$contribution->frequency_unit}s"));
+ }
+ else {
+ $next_sched = NULL;
+ /* Mark recurring contribution as complteted*/
+ civicrm_api(
+ 'ContributionRecur', 'create',
+ [
+ 'version' => '3',
+ 'id' => $contribution->id,
+ 'contribution_recur_status_id' => _contribution_status_id('Completed'),
+ ]
+ );
+ }
+
+ // Process payment
+ $amount_in_cents = preg_replace('/\.([0-9]{0,2}).*$/', '$1',
+ $contribution->amount);
+
+ $addresses = civicrm_api('Address', 'get',
+ [
+ 'version' => '3',
+ 'contact_id' => $contribution->contact_id,
+ ]);
+
+ $billing_address = array_shift($addresses['values']);
+
+ $invoice_id = md5(uniqid(rand(), TRUE));
+ $eWayResponse = NULL;
+
+ try {
+ if (!$contribution->failure_retry_date) {
+ // Only update the next schedule if we're not in a retry state.
+ $this->update_contribution_status($next_sched, $contribution);
+ }
+
+ $mainContributions = $mainContributions[0];
+ $new_contribution_record = [];
+ if (empty($mainContributions['tax_amount'])) {
+ $mainContributions['tax_amount'] = 0;
+ }
+
+ $repeat_params = [
+ 'contribution_recur_id' => $contribution->id,
+ 'contribution_status_id' => _contribution_status_id('Pending'),
+ 'total_amount' => $contribution->amount,
+ 'is_email_receipt' => 0,
+ ];
+
+ $repeated = civicrm_api3('Contribution', 'repeattransaction', $repeat_params);
+
+ $new_contribution_record = $repeated;
+
+ $new_contribution_record['contact_id'] = $contribution->contact_id;
+ $new_contribution_record['receive_date'] = CRM_Utils_Date::isoToMysql(date('Y-m-d H:i:s'));
+ $new_contribution_record['total_amount'] = ($contribution->amount - $mainContributions['tax_amount']);
+ $new_contribution_record['contribution_recur_id'] = $contribution->id;
+ $new_contribution_record['payment_instrument_id'] = $contribution->payment_instrument_id;
+ $new_contribution_record['address_id'] = $billing_address['id'];
+ $new_contribution_record['invoice_id'] = $invoice_id;
+ $new_contribution_record['campaign_id'] = $contribution->campaign_id;
+ $new_contribution_record['financial_type_id'] = $contribution->financial_type_id;
+ $new_contribution_record['payment_processor'] = $contribution->payment_processor_id;
+ $new_contribution_record['payment_processor_id'] = $contribution->payment_processor_id;
+
+ $contributions = civicrm_api3(
+ 'Contribution', 'get', [
+ 'sequential' => 1,
+ 'contribution_recur_id' => $contribution->id,
+ 'options' => ['sort' => "id ASC"],
+ ]
+ );
+
+ $precedent = new CRM_Contribute_BAO_Contribution();
+ $precedent->contribution_recur_id = $contribution->id;
+
+ $contributionSource = '';
+ $contributionPageId = '';
+ $contributionIsTest = 0;
+
+ if ($precedent->find(TRUE)) {
+ $contributionSource = $precedent->source;
+ $contributionPageId = $precedent->contribution_page_id;
+ $contributionIsTest = $precedent->is_test;
+ }
+
+ try {
+ $financial_type = civicrm_api3(
+ 'FinancialType', 'getsingle', [
+ 'sequential' => 1,
+ 'return' => "name",
+ 'id' => $contribution->financial_type_id,
+ ]);
+ }
+ catch (CiviCRM_API3_Exception $e) { // Most likely due to FinancialType API not being available in < 4.5 - try DAO directly
+ $ft_bao = new CRM_Financial_BAO_FinancialType();
+ $ft_bao->id = $contribution->financial_type_id;
+ $found = $ft_bao->find(TRUE);
+
+ $financial_type = (array) $ft_bao;
+ }
+
+ if (!isset($financial_type['name'])) {
+ throw new Exception (
+ "Financial type could not be loaded for {$contribution->id}"
+ );
+ }
+
+ $new_contribution_record['source'] = "eWay Recurring {$financial_type['name']}:\n{$contributionSource}";
+ $new_contribution_record['contribution_page_id'] = $contributionPageId;
+ $new_contribution_record['is_test'] = $contributionIsTest;
+
+ // Retrieve the eWAY token
+
+ if (!empty($contribution->payment_token_id)) {
+ try {
+ $token = civicrm_api3('PaymentToken', 'getvalue', [
+ 'return' => 'token',
+ 'id' => $contribution->payment_token_id,
+ ]);
+ }
+ catch (CiviCRM_API3_Exception $e) {
+ $token = $contribution->processor_id;
+ }
+ }
+ else {
+ $token = $contribution->processor_id;
+ }
+
+ if (!$token) {
+ throw new CRM_Core_Exception(\CRM_eWAYRecurring_ExtensionUtil::ts('No eWAY token found for Recurring Contribution %1', [1 => $contribution->id]));
+ }
+
+ $eWayResponse = self::process_payment(
+ $token,
+ $amount_in_cents,
+ substr($invoice_id, 0, 16),
+ $financial_type['name'] . ($contributionSource ?
+ ":\n" . $contributionSource : '')
+ );
+
+ $new_contribution_record['trxn_id'] = $eWayResponse->getAttribute('TransactionID');
+
+ $responseErrors = $this->getEWayResponseErrors($eWayResponse);
+
+ if (!$eWayResponse->TransactionStatus) {
+ $responseMessages = array_map('\Eway\Rapid::getMessage', explode(', ', $eWayResponse->ResponseMessage));
+ $responseErrors = array_merge($responseMessages, $responseErrors);
+ }
+
+ if (count($responseErrors)) {
+ // Mark transaction as failed
+ $new_contribution_record['contribution_status_id'] = _contribution_status_id('Failed');
+ $this->mark_recurring_contribution_failed($contribution);
+ }
+ else {
+ // $this->send_receipt_email($new_contribution_record->id);
+ $new_contribution_record['contribution_status_id'] = _contribution_status_id('Completed');
+
+ $new_contribution_record['is_email_receipt'] = Civi::settings()
+ ->get('eway_recurring_keep_sending_receipts');
+
+ if ($contribution->failure_count > 0 && $contribution->contribution_status_id == _contribution_status_id('Failed')) {
+ // Failed recurring contribution completed successfuly after several retry.
+ $this->update_contribution_status($next_sched, $contribution);
+ CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
+ $contribution->id,
+ 'contribution_status_id',
+ _contribution_status_id('In Progress'));
+
+ try {
+ civicrm_api3('Activity', 'create', [
+ 'source_contact_id' => $contribution->contact_id,
+ 'activity_type_id' => 'eWay Transaction Succeeded',
+ 'source_record' => $contribution->id,
+ 'details' => 'Transaction Succeeded after ' . $contribution->failure_count . ' retries',
+ ]);
+ }
+ catch (CiviCRM_API3_Exception $e) {
+ \Civi::log()
+ ->info('eWAY Recurring: Couldn\'t record success activity: ' . $e->getMessage());
+ }
+ }
+
+ CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
+ $contribution->id, 'failure_count', 0);
+
+ CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
+ $contribution->id, 'failure_retry_date', '');
+ }
+
+ $api_action = (
+ $new_contribution_record['contribution_status_id'] == _contribution_status_id('Completed')
+ ? 'completetransaction'
+ : 'create'
+ );
+
+ $updated = civicrm_api3('Contribution', $api_action, $new_contribution_record);
+
+ $new_contribution_record = reset($updated['values']);
+
+ // The invoice_id does not seem to be recorded by
+ // Contribution.completetransaction, so let's update it directly.
+ if ($api_action === 'completetransaction') {
+ $updated = civicrm_api3('Contribution', 'create', [
+ 'id' => $new_contribution_record['id'],
+ 'invoice_id' => $invoice_id,
+ ]);
+ $new_contribution_record = reset($updated['values']);
+ }
+
+ if (count($responseErrors)) {
+ $note = new CRM_Core_BAO_Note();
+
+ $note->entity_table = 'civicrm_contribution';
+ $note->contact_id = $contribution->contact_id;
+ $note->entity_id = $new_contribution_record['id'];
+ $note->subject = ts('Transaction Error');
+ $note->note = implode("\n", $responseErrors);
+
+ $note->save();
+ }
+ }
+ catch (Exception $e) {
+ Civi::log()
+ ->warning("Processing payment {$contribution->id} for {$contribution->contact_id}: " . $e->getMessage());
+
+ // already talk to eway? then we need to check the payment status
+ if ($eWayResponse) {
+ $new_contribution_record['contribution_status_id'] = _contribution_status_id('Pending');
+ }
+ else {
+ $new_contribution_record['contribution_status_id'] = _contribution_status_id('Failed');
+ }
+
+ try {
+ $updated = civicrm_api3('Contribution', 'create', $new_contribution_record);
+
+ $new_contribution_record = reset($updated['values']);
+ }
+ catch (CiviCRM_API3_Exception $e) {
+ Civi::log()
+ ->warning("Recurring contribution - Unable to set status of new contribution: " . $e->getMessage(), $new_contribution_record);
+ }
+
+ // CIVIEWAY-147 there is an unknown system error that happen after civi talks to eway
+ // It might be a cache cleaning task happening at the same time that break this task
+ // Defer the query later to update the contribution status
+ if ($eWayResponse) {
+ $ewayParams = [
+ 'access_code' => $eWayResponse->TransactionID,
+ 'contribution_id' => $new_contribution_record['id'],
+ 'payment_processor_id' => $contribution->payment_processor_id,
+ ];
+ civicrm_api3('EwayContributionTransactions', 'create', $ewayParams);
+ }
+ else {
+ // Just mark it failed when eWay have no info about this at all
+ $this->mark_recurring_contribution_failed($contribution);
+ }
+
+ $note = new CRM_Core_BAO_Note();
+
+ $note->entity_table = 'civicrm_contribution';
+ $note->contact_id = $contribution->contact_id;
+ $note->entity_id = $new_contribution_record['id'];
+ $note->subject = ts('Contribution Error');
+ $note->note = $e->getMessage();
+
+ $note->save();
+ }
+
+ unset($eWayResponse);
+ }
+
+ $lock->release();
+ }
+
+ protected function update_contribution_status($next_sched, $contribution) {
+ $d_now = new DateTime();
+ if ($next_sched) {
+ CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
+ $contribution->id,
+ 'next_sched_contribution_date',
+ CRM_Utils_Date::isoToMysql($next_sched));
+ }
+ else {
+ CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
+ $contribution->id,
+ 'contribution_status_id',
+ _contribution_status_id('Completed'));
+ CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
+ $contribution->id,
+ 'end_date',
+ CRM_Utils_Date::isoToMysql($d_now));
+ }
+ }
+
+ public function mark_recurring_contribution_failed($contribution) {
+ $today = new DateTime();
+ $retryDelayInDays = Civi::settings()
+ ->get('eway_recurring_contribution_retry_delay');
+ $today->modify("+" . $retryDelayInDays . " days");
+ $today->setTime(0, 0, 0);
+
+ try {
+ civicrm_api3('Activity', 'create', [
+ 'source_contact_id' => $contribution->contact_id,
+ 'activity_type_id' => 'eWay Transaction Failed',
+ 'source_record' => $contribution->id,
+ ]);
+ }
+ catch (CiviCRM_API3_Exception $e) {
+ /* Failing to create the failure activity should not prevent the
+ ContributionRecur entity from being updated. Log it and move on. */
+ \Civi::log()
+ ->info('eWAY Recurring: Couldn\'t record failure activity: ' . $e->getMessage());
+ }
+
+ civicrm_api3('ContributionRecur', 'create', [
+ 'id' => $contribution->id,
+ 'failure_count' => (++$contribution->failure_count),
+ 'failure_retry_date' => $today->format("Y-m-d H:i:s"),
+ // CIVIEWAY-125: Don't actually mark as failed, because that causes the UI
+ // to melt down.
+ // 'contribution_status_id' => _contribution_status_id('Failed'),
+ ]);
+ }
+
+ /**
+ * get_scheduled_contributions
+ *
+ * Gets recurring contributions that are scheduled to be processed today
+ *
+ * @return array An array of contribtion_recur objects
+ */
+ protected function get_scheduled_contributions() {
+ $payment_processor = $this->getPaymentProcessor();
+ $scheduled_today = new CRM_Contribute_BAO_ContributionRecur();
+
+ // Only get contributions for the current processor
+ $scheduled_today->payment_processor_id = $payment_processor['id'];
+
+ // Only get contribution that are on or past schedule
+ $scheduled_today->whereAdd("`next_sched_contribution_date` <= now()");
+
+ // Don't get cancelled or failed contributions
+ $status_ids = implode(', ', [
+ _contribution_status_id('In Progress'),
+ _contribution_status_id('Pending'),
+ ]);
+ $scheduled_today->whereAdd("`contribution_status_id` IN ({$status_ids})");
+
+ // Ignore transactions that have a failure_retry_date, these are subject to different conditions
+ $scheduled_today->whereAdd("`failure_retry_date` IS NULL");
+
+ // CIVIEWAY-124: Exclude contributions that never completed
+ $t = $scheduled_today->tableName();
+ $ct = CRM_Contribute_BAO_Contribution::getTableName();
+ $scheduled_today->whereAdd("EXISTS (SELECT 1 FROM `{$ct}` WHERE `contribution_status_id` = 1 AND `{$t}`.id = `{$ct}`.`contribution_recur_id`)");
+
+ // Exclude contributions that have already been processed
+ $scheduled_today->whereAdd("NOT EXISTS (SELECT 1 FROM `{$ct}` WHERE `{$ct}`.`receive_date` >= `{$t}`.`next_sched_contribution_date` AND `{$t}`.id = `{$ct}`.`contribution_recur_id`)");
+
+ $scheduled_today->find();
+
+ $scheduled_contributions = [];
+
+ while ($scheduled_today->fetch()) {
+ $scheduled_contributions[] = clone $scheduled_today;
+ }
+
+ return $scheduled_contributions;
+ }
+
+ /**
+ * get_scheduled_failed_contributions
+ *
+ * Gets recurring contributions that are failed and to be processed today
+ *
+ * @return array An array of contribtion_recur objects
+ */
+ public function get_scheduled_failed_contributions() {
+ $payment_processor = $this->getPaymentProcessor();
+
+ $maxFailRetry = Civi::settings()
+ ->get('eway_recurring_contribution_max_retry');
+
+ $scheduled_today = new CRM_Contribute_BAO_ContributionRecur();
+
+ // Only get contributions for the current processor
+ $scheduled_today->payment_processor_id = $payment_processor['id'];
+
+ $scheduled_today->whereAdd("`failure_retry_date` <= now()");
+
+ $scheduled_today->contribution_status_id = _contribution_status_id('In Progress');
+ $scheduled_today->whereAdd("`failure_count` < " . $maxFailRetry);
+ $scheduled_today->whereAdd("`failure_count` > 0");
+
+ // CIVIEWAY-124: Exclude contributions that never completed
+ $t = $scheduled_today->tableName();
+ $ct = CRM_Contribute_BAO_Contribution::getTableName();
+ $scheduled_today->whereAdd("EXISTS (SELECT 1 FROM `{$ct}` WHERE `contribution_status_id` = 1 AND `{$t}`.id = `contribution_recur_id`)");
+
+ // Exclude contributions that have already been processed
+ $scheduled_today->whereAdd("NOT EXISTS (SELECT 1 FROM `{$ct}` WHERE `{$ct}`.`receive_date` >= `{$t}`.`failure_retry_date` AND `{$t}`.id = `{$ct}`.`contribution_recur_id`)");
+
+ $scheduled_today->find();
+
+ $scheduled_failed_contributions = [];
+
+ while ($scheduled_today->fetch()) {
+ $scheduled_failed_contributions[] = clone $scheduled_today;
+ }
+
+ return $scheduled_failed_contributions;
+ }
+
+ /**
+ * process_eway_payment
+ *
+ * Processes an eWay token payment
+ *
+ * @param object $eWayClient An eWay client set up and ready to go
+ * @param string $managed_customer_id The eWay token ID for the credit card
+ * you want to process
+ * @param string $amount_in_cents The amount in cents to charge the customer
+ * @param string $invoice_reference InvoiceReference to send to eWay
+ * @param string $invoice_description InvoiceDescription to send to eWay
+ *
+ * @return object eWay response object
+ * @throws SoapFault exceptions
+ */
+ public function process_payment($managed_customer_id, $amount_in_cents, $invoice_reference, $invoice_description) {
+ static $prev_response = NULL;
+
+ $eWayClient = $this->getEWayClient();
+
+ $paymentTransaction = [
+ 'Customer' => [
+ 'TokenCustomerID' => substr($managed_customer_id, 0, 16),
+ ],
+ 'Payment' => [
+ 'TotalAmount' => substr($amount_in_cents, 0, 10),
+ 'InvoiceDescription' => substr(trim($invoice_description), 0, 64),
+ 'InvoiceReference' => substr($invoice_reference, 0, 64),
+ ],
+ 'TransactionType' => \Eway\Rapid\Enum\TransactionType::MOTO,
+ ];
+ $eWayResponse = $eWayClient->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $paymentTransaction);
+
+ if (isset($prev_response) && $prev_response->getAttribute('TransactionID') == $eWayResponse->getAttribute('TransactionID')) {
+ throw new Exception (
+ 'eWay ProcessPayment returned duplicate transaction number: ' .
+ $prev_response->getAttribute('TransactionID') . ' vs ' . $eWayResponse->getAttribute('TransactionID')
+ );
+ }
+
+ $prev_response = &$eWayResponse;
+
+ return $eWayResponse;
+ }
+
+ /**
+ * send_receipt_email
+ *
+ * Sends a receipt for a contribution
+ *
+ * @param string $contribution_id The ID of the contribution to mark as
+ * complete
+ * @param CRM_Core_Payment $paymentObject CRM_Core_Payment object
+ *
+ * @return bool Success or failure
+ * @throws CRM_Core_Exception
+ */
+ protected function send_receipt_email($contribution_id, $paymentObject) {
+ $contribution = new CRM_Contribute_BAO_Contribution();
+ $contribution->id = $contribution_id;
+ $contribution->find(TRUE);
+
+ $is_email_receipt = civicrm_api3('ContributionPage', 'getvalue', [
+ 'id' => $contribution->contribution_page_id,
+ 'return' => 'is_email_receipt',
+ ]);
+
+ if (!$is_email_receipt) {
+ return NULL;
+ }
+
+ [
+ $name,
+ $email,
+ ] = CRM_Contact_BAO_Contact_Location::getEmailDetails($contribution->contact_id);
+
+ $domainValues = CRM_Core_BAO_Domain::getNameAndEmail();
+ $receiptFrom = "$domainValues[0] <$domainValues[1]>";
+ $receiptFromEmail = $domainValues[1];
+
+ $params = [
+ 'groupName' => 'msg_tpl_workflow_contribution',
+ 'valueName' => 'contribution_online_receipt',
+ 'contactId' => $contribution->contact_id,
+ 'tplParams' => [
+ 'contributeMode' => 'directIPN',
+ // Tells the person to contact us for cancellations
+ 'receiptFromEmail' => $receiptFromEmail,
+ 'amount' => $contribution->total_amount,
+ 'title' => self::RECEIPT_SUBJECT_TITLE,
+ 'is_recur' => TRUE,
+ 'is_monetary' => TRUE,
+ 'is_pay_later' => FALSE,
+ 'billingName' => $name,
+ 'email' => $email,
+ 'trxn_id' => $contribution->trxn_id,
+ 'receive_date' => CRM_Utils_Date::format($contribution->receive_date),
+ 'updateSubscriptionBillingUrl' => $paymentObject->subscriptionURL($contribution_id, 'contribution', 'billing'),
+ ],
+ 'from' => $receiptFrom,
+ 'toName' => $name,
+ 'toEmail' => $email,
+ 'isTest' => $contribution->is_test,
+ ];
+
+ [
+ $sent,
+ $subject,
+ $message,
+ $html,
+ ] = CRM_Core_BAO_MessageTemplate::sendTemplate($params);
+
+ return $sent;
+ }
+
+}
diff --git a/CRM/eWAYRecurring/Upgrader.php b/CRM/eWAYRecurring/Upgrader.php
index 1d3e01d..d6c414d 100644
--- a/CRM/eWAYRecurring/Upgrader.php
+++ b/CRM/eWAYRecurring/Upgrader.php
@@ -1,5 +1,6 @@
ctx->log->info('Apply 2.6.0 update; Update class names for eWAYRecurring payment processor type.');
+ PaymentProcessorType::update(FALSE)
+ ->addValue('class_name', 'Payment_eWAYRecurring')
+ ->addWhere('class_name', '=', 'au.com.agileware.ewayrecurring')
+ ->execute();
+ return TRUE;
+ }
+
}
diff --git a/CRM/eWAYRecurring/Utils.php b/CRM/eWAYRecurring/Utils.php
index 669ddf1..95662d9 100644
--- a/CRM/eWAYRecurring/Utils.php
+++ b/CRM/eWAYRecurring/Utils.php
@@ -1,5 +1,7 @@
id = $contribution['contribution_recur_id'];
$bao->find();
- _eWAYRecurring_mark_recurring_contribution_Failed($bao);
+ CRM_Core_Payment_eWAYRecurring::getInstance($paymentProcessor)
+ ->mark_recurring_contribution_failed($bao);
}
$transactionToValidate['status'] = self::STATUS_FAILED;
$transactionToValidate['failed_message'] = $response['transactionResponseError'];
@@ -418,9 +421,52 @@ private static function updateRecurringContribution($contribution, $customerToke
$recurringContribution['trxn_id'] = $transactionID;
civicrm_api3('ContributionRecur', 'create', $recurringContribution);
+ }
+ catch (CiviCRM_API3_Exception $e) {
+ }
+ }
- } catch (CiviCRM_API3_Exception $e) {
+ /**
+ * Validate eWay contribution by AccessCode, Invoice ID and Payment Processor.
+ *
+ * @param $paymentProcessor
+ * @param $invoiceID
+ *
+ * @return array|null
+ * @throws CRM_Core_Exception
+ * @throws CiviCRM_API3_Exception
+ */
+ public static function validateEwayContribution($paymentProcessor, $invoiceID) {
+ if ($paymentProcessor instanceof CRM_Core_Payment_eWAYRecurring) {
+ $contribution = civicrm_api3('Contribution', 'get', [
+ 'invoice_id' => $invoiceID,
+ 'sequential' => TRUE,
+ 'return' => [
+ 'contribution_page_id',
+ 'contribution_recur_id',
+ 'total_amount',
+ 'is_test',
+ ],
+ 'is_test' => ($paymentProcessor->_mode == 'test') ? 1 : 0,
+ ]);
+ if (count($contribution['values']) > 0 && $contribution['values'][0]['total_amount'] > 0) {
+ // Include eWay SDK.
+ require_once E::path('vendor/autoload.php');
+ $store = NULL;
+
+ $contribution = $contribution['values'][0];
+ // @TODO $form is an undefined variable
+ $eWayAccessCode = CRM_Utils_Request::retrieve('AccessCode', 'String', $store, FALSE, "");
+ $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $store, FALSE, "");
+
+ $paymentProcessor->validateContribution($eWayAccessCode, $contribution, $qfKey, $paymentProcessor->getPaymentProcessor());
+
+ return [
+ 'contribution' => $contribution,
+ ];
+ }
}
+ return NULL;
}
}
diff --git a/api/v3/EwayContributionTransactions.php b/api/v3/EwayContributionTransactions.php
index fc6da70..82996ce 100644
--- a/api/v3/EwayContributionTransactions.php
+++ b/api/v3/EwayContributionTransactions.php
@@ -1,6 +1,6 @@
validatePendingTransactions($params);
return civicrm_api3_create_success($response, $params, 'EwayContributionTransactions', 'validate');
-}
\ No newline at end of file
+}
diff --git a/api/v3/EwayRecurring.php b/api/v3/EwayRecurring.php
new file mode 100644
index 0000000..2b40f9e
--- /dev/null
+++ b/api/v3/EwayRecurring.php
@@ -0,0 +1,7 @@
+ 'eWay_Recurring',
'title' => 'eWay Recurring',
'description' => 'Recurring payments payment processor for eWay',
- 'class_name' => 'au.com.agileware.ewayrecurring',
+ 'class_name' => 'Payment_eWAYRecurring',
'user_name_label' => 'API Key',
'password_label' => 'API Password',
'billing_mode' => 'notify',
@@ -144,6 +138,22 @@ function ewayrecurring_civicrm_managed(&$entities) {
'is_active' => '1',
],
];
+ $entities[] = [
+ 'module' => 'au.com.agileware.ewayrecurring',
+ 'name' => 'eWay_fillTokensMeta_cron',
+ 'entity' => 'Job',
+ 'update' => 'never',
+ 'params' => [
+ 'version' => 3,
+ 'run_frequency' => 'Hourly',
+ 'name' => 'eWay Recurring: fill missing tokens metadata',
+ 'description' => 'Loops through PaymentTokens for eWAY Recurring linked PaymentTokens that are missing expiry date or masked card number and queries eWAY Rapid API to fill these details in',
+ 'api_entity' => 'EwayRecurring',
+ 'api_action' => 'fillTokensMeta',
+ 'paramters' => '',
+ 'is_active' => '1',
+ ],
+ ];
}
/**
@@ -173,7 +183,7 @@ function _contribution_status_id($name) {
function ewayrecurring_civicrm_buildForm($formName, &$form) {
if ($formName == 'CRM_Contribute_Form_UpdateSubscription') {
$paymentProcessor = $form->getVar('_paymentProcessorObj');
- if (($paymentProcessor instanceof au_com_agileware_ewayrecurring)) {
+ if (($paymentProcessor instanceof CRM_Core_Payment_eWAYRecurring)) {
($crid = $form->getVar('contributionRecurID')) || ($crid = $form->getVar('_crid'));
if ($crid) {
$sql = 'SELECT next_sched_contribution_date FROM civicrm_contribution_recur WHERE id = %1';
@@ -184,8 +194,10 @@ function ewayrecurring_civicrm_buildForm($formName, &$form) {
'Int',
],
])) {
- [$defaults['next_scheduled_date'],
- $defaults['next_scheduled_date_time']] = CRM_Utils_Date::setDateDefaults($default_nsd);
+ [
+ $defaults['next_scheduled_date'],
+ $defaults['next_scheduled_date_time'],
+ ] = CRM_Utils_Date::setDateDefaults($default_nsd);
$form->setDefaults($defaults);
}
// add next scheduled date field
@@ -204,7 +216,7 @@ function ewayrecurring_civicrm_buildForm($formName, &$form) {
}
}
}
- elseif ($formName == 'CRM_Contribute_Form_CancelSubscription' && $form->getVar('_paymentProcessorObj') instanceof au_com_agileware_ewayrecurring) {
+ elseif ($formName == 'CRM_Contribute_Form_CancelSubscription' && $form->getVar('_paymentProcessorObj') instanceof CRM_Core_Payment_eWAYRecurring) {
// remove send request to eway field
$form->removeElement('send_cancel_request');
}
@@ -263,7 +275,7 @@ function ewayrecurring_civicrm_preProcess($formName, &$form) {
$invoiceID = $form->_params['invoiceID'];
$validated = &Civi::$statics[__FUNCTION__ . '::validated'];
if(!isset($validated[$invoiceID])) {
- validateEwayContribution($paymentProcessor, $invoiceID);
+ CRM_eWAYRecurring_Utils::validateEwayContribution($paymentProcessor, $invoiceID);
}
// fixme CIVIEWAY-144 temporary fix, remove this if the issue is solved in core
if (!$form->_priceSetId || CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $form->_priceSetId, 'is_quick_config')) {
@@ -274,17 +286,18 @@ function ewayrecurring_civicrm_preProcess($formName, &$form) {
case 'CRM_Contribute_Form_Contribution_Confirm':
case 'CRM_Event_Form_Registration_Confirm':
$qfKey = $form->get('qfKey');
- $eWAYResponse = $qfKey ? unserialize(CRM_Core_Session::singleton()->get('eWAYResponse', $qfKey)) : false;
+ $eWAYResponse = $qfKey ? unserialize(CRM_Core_Session::singleton()
+ ->get('eWAYResponse', $qfKey)) : FALSE;
$paymentProcessor = $form->getVar('_paymentProcessor') ?? NULL;
- if (!empty($eWAYResponse->AccessCode) && ($paymentProcessor['object'] instanceof au_com_agileware_ewayrecurring)) {
+ if (!empty($eWAYResponse->AccessCode) && ($paymentProcessor['object'] instanceof CRM_Core_Payment_eWAYRecurring)) {
$transaction = CRM_eWAYRecurring_Utils::validateEwayAccessCode($eWAYResponse->AccessCode, $paymentProcessor);
- if($transaction['hasTransactionFailed']) {
+ if ($transaction['hasTransactionFailed']) {
CRM_Core_session::setStatus(E::ts(
'A transaction has already been submitted, but failed. Continuing will result in a new transaction.'
));
- CRM_Core_Session::singleton()->set('eWAYResponse', null, $qfKey);
+ CRM_Core_Session::singleton()->set('eWAYResponse', NULL, $qfKey);
}
- elseif(!$transaction['transactionNotProcessedYet']) {
+ elseif (!$transaction['transactionNotProcessedYet']) {
throw new PaymentProcessorException(
$form instanceof CRM_Event_Form_Registration_Confirm
? E::ts('Payment already completed for this Registration')
@@ -296,50 +309,6 @@ function ewayrecurring_civicrm_preProcess($formName, &$form) {
}
}
-/**
- * Validate eWay contribution by AccessCode, Invoice ID and Payment Processor.
- *
- * @param $paymentProcessor
- * @param $invoiceID
- *
- * @return array|null
- * @throws CRM_Core_Exception
- * @throws CiviCRM_API3_Exception
- */
-function validateEwayContribution($paymentProcessor, $invoiceID) {
- if ($paymentProcessor instanceof au_com_agileware_ewayrecurring) {
- $contribution = civicrm_api3('Contribution', 'get', [
- 'invoice_id' => $invoiceID,
- 'sequential' => TRUE,
- 'return' => [
- 'contribution_page_id',
- 'contribution_recur_id',
- 'total_amount',
- 'is_test',
- ],
- 'is_test' => ($paymentProcessor->_mode == 'test') ? 1 : 0,
- ]);
-
- if (count($contribution['values']) > 0 && $contribution['values'][0]['total_amount'] > 0) {
- // Include eWay SDK.
- require_once extensionPath('vendor/autoload.php');
- $store = NULL;
-
- $contribution = $contribution['values'][0];
- // @TODO $form is an undefined variable
- $eWayAccessCode = CRM_Utils_Request::retrieve('AccessCode', 'String', $store, FALSE, "");
- $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $store, FALSE, "");
-
- $paymentProcessor->validateContribution($eWayAccessCode, $contribution, $qfKey, $paymentProcessor->getPaymentProcessor());
-
- return [
- 'contribution' => $contribution,
- ];
- }
- return NULL;
- }
-}
-
function _ewayrecurring_upgrade_schema_version(CRM_Queue_TaskContext $ctx, $schema) {
CRM_Core_BAO_Extension::setSchemaVersion('au.com.agileware.ewayrecurring', $schema);
return CRM_Queue_Task::TASK_SUCCESS;
@@ -361,22 +330,6 @@ function _ewayrecurring_get_pp_id($processor) {
return $processor['id'];
}
-/**
- * Get the path of a resource file (in this extension).
- *
- * @param string|NULL $file
- * Ex: NULL.
- * Ex: 'css/foo.css'.
- *
- * @return string
- * Ex: '/var/www/example.org/sites/default/ext/org.example.foo'.
- * Ex: '/var/www/example.org/sites/default/ext/org.example.foo/css/foo.css'.
- */
-function extensionPath($file = NULL) {
- // return CRM_Core_Resources::singleton()->getPath(self::LONG_NAME, $file);
- return __DIR__ . ($file === NULL ? '' : (DIRECTORY_SEPARATOR . $file));
-}
-
function ewayrecurring_civicrm_navigationMenu(&$menu) {
_ewayrecurring_civix_insert_navigation_menu($menu, 'Administer', [
'label' => E::ts('eWay Recurring Settings'),
@@ -393,7 +346,7 @@ function ewayrecurring_civicrm_navigationMenu(&$menu) {
*/
function ewayrecurring_civicrm_coreResourceList(&$list, $region) {
if ($region == 'html-header') {
- Civi::resources()->addScriptFile('au.com.agileware.ewayrecurring', 'js/eway.js', $region);
+ Civi::resources()->addScriptFile('au.com.agileware.ewayrecurring', 'js/eway.js', [ 'region' => $region, 'weight' => 9 ]);
$result = civicrm_api3('PaymentProcessorType', 'get', [
'sequential' => 1,
'name' => "eWay_Recurring",
@@ -418,4 +371,4 @@ function ewayrecurring_civicrm_coreResourceList(&$list, $region) {
function ewayrecurring_civicrm_permission(&$permissions) {
$permissions['view payment tokens'] = E::ts('CiviContribute: view payment tokens');
$permissions['edit payment tokens'] = E::ts('CiviContribute: edit payment tokens');
-}
\ No newline at end of file
+}
diff --git a/eWAYRecurring.process.inc b/eWAYRecurring.process.inc
deleted file mode 100644
index b7a83cb..0000000
--- a/eWAYRecurring.process.inc
+++ /dev/null
@@ -1,583 +0,0 @@
-get('lockManager')
- ->create('worker.ewayrecurring');
- if (!$lock->isFree() || !$lock->acquire()) {
- Civi::log()->warning("Detected processing race for scheduled payments, aborting");
- return FALSE;
- }
-
- // Create eWay token client
- $eWayClient = $paymentObject->getEWayClient();
-
- // Process today's scheduled contributions.
- $scheduled_contributions = get_scheduled_contributions($payment_processor);
- $scheduled_failed_contributions = get_scheduled_failed_contributions($payment_processor);
-
- $scheduled_contributions = array_merge($scheduled_failed_contributions, $scheduled_contributions);
-
- foreach ($scheduled_contributions as $contribution) {
- if ($contribution->payment_processor_id != $payment_processor['id']) {
- continue;
- }
-
- // Re-check schedule time, in case contribution already processed.
- $next_sched = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_ContributionRecur',
- $contribution->id,
- 'next_sched_contribution_date',
- 'id',
- TRUE);
-
- /* Get the number of Contributions already recorded for this Schedule. */
- $mainContributions = civicrm_api3('Contribution', 'get', [
- 'options' => ['limit' => 0],
- 'sequential' => 1,
- 'return' => ['total_amount', 'tax_amount'],
- 'contribution_recur_id' => $contribution->id,
- ]);
-
- $mainContributions = $mainContributions['values'];
- $ccount = count($mainContributions);
-
- /* Schedule next contribution */
- if (($contribution->installments <= 0) || ($contribution->installments > $ccount + 1)) {
- $next_sched = date('Y-m-d 00:00:00', strtotime($next_sched . " +{$contribution->frequency_interval} {$contribution->frequency_unit}s"));
- }
- else {
- $next_sched = NULL;
- /* Mark recurring contribution as complteted*/
- civicrm_api(
- 'ContributionRecur', 'create',
- [
- 'version' => '3',
- 'id' => $contribution->id,
- 'contribution_recur_status_id' => _contribution_status_id('Completed'),
- ]
- );
- }
-
- // Process payment
- $amount_in_cents = preg_replace('/\.([0-9]{0,2}).*$/', '$1',
- $contribution->amount);
-
- $addresses = civicrm_api('Address', 'get',
- [
- 'version' => '3',
- 'contact_id' => $contribution->contact_id,
- ]);
-
- $billing_address = array_shift($addresses['values']);
-
- $invoice_id = md5(uniqid(rand(), TRUE));
- $eWayResponse = NULL;
-
- try {
- if (!$contribution->failure_retry_date) {
- // Only update the next schedule if we're not in a retry state.
- _eWayRecurring_update_contribution_status($next_sched, $contribution);
- }
-
- $mainContributions = $mainContributions[0];
- $new_contribution_record = [];
- if (empty($mainContributions['tax_amount'])) {
- $mainContributions['tax_amount'] = 0;
- }
-
- $repeat_params = [
- 'contribution_recur_id' => $contribution->id,
- 'contribution_status_id' => _contribution_status_id('Pending'),
- 'total_amount' => $contribution->amount,
- 'is_email_receipt' => 0,
- ];
-
- $repeated = civicrm_api3('Contribution', 'repeattransaction', $repeat_params);
-
- $new_contribution_record = $repeated;
-
- $new_contribution_record['contact_id'] = $contribution->contact_id;
- $new_contribution_record['receive_date'] = CRM_Utils_Date::isoToMysql(date('Y-m-d H:i:s'));
- $new_contribution_record['total_amount'] = ($contribution->amount - $mainContributions['tax_amount']);
- $new_contribution_record['contribution_recur_id'] = $contribution->id;
- $new_contribution_record['payment_instrument_id'] = $contribution->payment_instrument_id;
- $new_contribution_record['address_id'] = $billing_address['id'];
- $new_contribution_record['invoice_id'] = $invoice_id;
- $new_contribution_record['campaign_id'] = $contribution->campaign_id;
- $new_contribution_record['financial_type_id'] = $contribution->financial_type_id;
- $new_contribution_record['payment_processor'] = $contribution->payment_processor_id;
- $new_contribution_record['payment_processor_id'] = $contribution->payment_processor_id;
-
- $contributions = civicrm_api3(
- 'Contribution', 'get', [
- 'sequential' => 1,
- 'contribution_recur_id' => $contribution->id,
- 'options' => ['sort' => "id ASC"],
- ]
- );
-
- $precedent = new CRM_Contribute_BAO_Contribution();
- $precedent->contribution_recur_id = $contribution->id;
-
- $contributionSource = '';
- $contributionPageId = '';
- $contributionIsTest = 0;
-
- if ($precedent->find(TRUE)) {
- $contributionSource = $precedent->source;
- $contributionPageId = $precedent->contribution_page_id;
- $contributionIsTest = $precedent->is_test;
- }
-
- try {
- $financial_type = civicrm_api3(
- 'FinancialType', 'getsingle', [
- 'sequential' => 1,
- 'return' => "name",
- 'id' => $contribution->financial_type_id,
- ]);
- } catch (CiviCRM_API3_Exception $e) { // Most likely due to FinancialType API not being available in < 4.5 - try DAO directly
- $ft_bao = new CRM_Financial_BAO_FinancialType();
- $ft_bao->id = $contribution->financial_type_id;
- $found = $ft_bao->find(TRUE);
-
- $financial_type = (array) $ft_bao;
- }
-
-
- if (!isset($financial_type['name'])) {
- throw new Exception (
- "Financial type could not be loaded for {$contribution->id}"
- );
- }
-
- $new_contribution_record['source'] = "eWay Recurring {$financial_type['name']}:\n{$contributionSource}";
- $new_contribution_record['contribution_page_id'] = $contributionPageId;
- $new_contribution_record['is_test'] = $contributionIsTest;
-
- // Retrieve the eWAY token
-
- if (!empty($contribution->payment_token_id)) {
- try {
- $token = civicrm_api3('PaymentToken', 'getvalue', [
- 'return' => 'token',
- 'id' => $contribution->payment_token_id,
- ]);
- } catch (CiviCRM_API3_Exception $e) {
- $token = $contribution->processor_id;
- }
- }
- else {
- $token = $contribution->processor_id;
- }
-
- if (!$token) {
- throw new CRM_Core_Exception(E::ts('No eWAY token found for Recurring Contribution %1', [1 => $contribution->id]));
- }
-
- $eWayResponse = process_eway_payment(
- $eWayClient,
- $token,
- $amount_in_cents,
- substr($invoice_id, 0, 16),
- $financial_type['name'] . ($contributionSource ?
- ":\n" . $contributionSource : '')
- );
-
- $new_contribution_record['trxn_id'] = $eWayResponse->getAttribute('TransactionID');
-
- $responseErrors = $paymentObject->getEWayResponseErrors($eWayResponse);
-
- if (!$eWayResponse->TransactionStatus) {
- $responseMessages = array_map('\Eway\Rapid::getMessage', explode(', ', $eWayResponse->ResponseMessage));
- $responseErrors = array_merge($responseMessages, $responseErrors);
- }
-
- if (count($responseErrors)) {
- // Mark transaction as failed
- $new_contribution_record['contribution_status_id'] = _contribution_status_id('Failed');
- _eWAYRecurring_mark_recurring_contribution_Failed($contribution);
- }
- else {
- // send_receipt_email($new_contribution_record->id);
- $new_contribution_record['contribution_status_id'] = _contribution_status_id('Completed');
-
- $new_contribution_record['is_email_receipt'] = Civi::settings()->get('eway_recurring_keep_sending_receipts');
-
- if ($contribution->failure_count > 0 && $contribution->contribution_status_id == _contribution_status_id('Failed')) {
- // Failed recurring contribution completed successfuly after several retry.
- _eWayRecurring_update_contribution_status($next_sched, $contribution);
- CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
- $contribution->id,
- 'contribution_status_id',
- _contribution_status_id('In Progress'));
-
- try {
- civicrm_api3('Activity', 'create', [
- 'source_contact_id' => $contribution->contact_id,
- 'activity_type_id' => 'eWay Transaction Succeeded',
- 'source_record' => $contribution->id,
- 'details' => 'Transaction Succeeded after ' . $contribution->failure_count . ' retries',
- ]);
- }
- catch (CiviCRM_API3_Exception $e) {
- \Civi::log()->info('eWAY Recurring: Couldn\'t record success activity: ' . $e->getMessage());
- }
- }
-
- CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
- $contribution->id, 'failure_count', 0);
-
- CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
- $contribution->id, 'failure_retry_date', '');
- }
-
- $api_action = (
- $new_contribution_record['contribution_status_id'] == _contribution_status_id('Completed')
- ? 'completetransaction'
- : 'create'
- );
-
- $updated = civicrm_api3('Contribution', $api_action, $new_contribution_record);
-
- $new_contribution_record = reset($updated['values']);
-
- // The invoice_id does not seem to be recorded by
- // Contribution.completetransaction, so let's update it directly.
- if ($api_action === 'completetransaction') {
- $updated = civicrm_api3('Contribution', 'create', [
- 'id' => $new_contribution_record['id'],
- 'invoice_id' => $invoice_id,
- ]);
- $new_contribution_record = reset($updated['values']);
- }
-
- if (count($responseErrors)) {
- $note = new CRM_Core_BAO_Note();
-
- $note->entity_table = 'civicrm_contribution';
- $note->contact_id = $contribution->contact_id;
- $note->entity_id = $new_contribution_record['id'];
- $note->subject = ts('Transaction Error');
- $note->note = implode("\n", $responseErrors);
-
- $note->save();
- }
-
- } catch (Exception $e) {
- Civi::log()->warning("Processing payment {$contribution->id} for {$contribution->contact_id}: " . $e->getMessage());
-
- // already talk to eway? then we need to check the payment status
- if ($eWayResponse) {
- $new_contribution_record['contribution_status_id'] = _contribution_status_id('Pending');
- } else {
- $new_contribution_record['contribution_status_id'] = _contribution_status_id('Failed');
- }
-
- try {
- $updated = civicrm_api3('Contribution', 'create', $new_contribution_record);
-
- $new_contribution_record = reset($updated['values']);
- } catch (CiviCRM_API3_Exception $e) {
- Civi::log()->warning("Recurring contribution - Unable to set status of new contribution: " . $e->getMessage(), $new_contribution_record);
- }
-
- // CIVIEWAY-147 there is an unknown system error that happen after civi talks to eway
- // It might be a cache cleaning task happening at the same time that break this task
- // Defer the query later to update the contribution status
- if ($eWayResponse) {
- $ewayParams = [
- 'access_code' => $eWayResponse->TransactionID,
- 'contribution_id' => $new_contribution_record['id'],
- 'payment_processor_id' => $contribution->payment_processor_id,
- ];
- civicrm_api3('EwayContributionTransactions', 'create', $ewayParams);
- }
- else {
- // Just mark it failed when eWay have no info about this at all
- _eWAYRecurring_mark_recurring_contribution_Failed($contribution);
- }
-
- $note = new CRM_Core_BAO_Note();
-
- $note->entity_table = 'civicrm_contribution';
- $note->contact_id = $contribution->contact_id;
- $note->entity_id = $new_contribution_record['id'];
- $note->subject = ts('Contribution Error');
- $note->note = $e->getMessage();
-
- $note->save();
- }
-
- unset($eWayResponse);
-
- }
-
- $lock->release();
-}
-
-function _eWayRecurring_update_contribution_status($next_sched, $contribution) {
- $d_now = new DateTime();
- if ($next_sched) {
- CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
- $contribution->id,
- 'next_sched_contribution_date',
- CRM_Utils_Date::isoToMysql($next_sched));
- }
- else {
- CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
- $contribution->id,
- 'contribution_status_id',
- _contribution_status_id('Completed'));
- CRM_Core_DAO::setFieldValue('CRM_Contribute_DAO_ContributionRecur',
- $contribution->id,
- 'end_date',
- CRM_Utils_Date::isoToMysql($d_now));
- }
-}
-
-function _eWAYRecurring_mark_recurring_contribution_Failed($contribution) {
- $today = new DateTime();
- $retryDelayInDays = Civi::settings()
- ->get('eway_recurring_contribution_retry_delay');
- $today->modify("+" . $retryDelayInDays . " days");
- $today->setTime(0, 0, 0);
-
- try {
- civicrm_api3('Activity', 'create', [
- 'source_contact_id' => $contribution->contact_id,
- 'activity_type_id' => 'eWay Transaction Failed',
- 'source_record' => $contribution->id,
- ]);
- }
- catch (CiviCRM_API3_Exception $e) {
- /* Failing to create the failure activity should not prevent the
- ContributionRecur entity from being updated. Log it and move on. */
- \Civi::log()->info('eWAY Recurring: Couldn\'t record failure activity: ' . $e->getMessage());
- }
-
- civicrm_api3('ContributionRecur', 'create', [
- 'id' => $contribution->id,
- 'failure_count' => (++$contribution->failure_count),
- 'failure_retry_date' => $today->format("Y-m-d H:i:s"),
- // CIVIEWAY-125: Don't actually mark as failed, because that causes the UI
- // to melt down.
- // 'contribution_status_id' => _contribution_status_id('Failed'),
- ]);
-}
-
-/**
- * get_scheduled_contributions
- *
- * Gets recurring contributions that are scheduled to be processed today
- *
- * @return array An array of contribtion_recur objects
- */
-function get_scheduled_contributions($payment_processor) {
- $scheduled_today = new CRM_Contribute_BAO_ContributionRecur();
-
- // Only get contributions for the current processor
- $scheduled_today->payment_processor_id = $payment_processor['id'];
-
- // Only get contribution that are on or past schedule
- $scheduled_today->whereAdd("`next_sched_contribution_date` <= now()");
-
- // Don't get cancelled or failed contributions
- $status_ids = implode(', ', [ _contribution_status_id('In Progress'), _contribution_status_id('Pending') ]);
- $scheduled_today->whereAdd("`contribution_status_id` IN ({$status_ids})");
-
- // Ignore transactions that have a failure_retry_date, these are subject to different conditions
- $scheduled_today->whereAdd("`failure_retry_date` IS NULL");
-
- // CIVIEWAY-124: Exclude contributions that never completed
- $t = $scheduled_today->tableName();
- $ct = CRM_Contribute_BAO_Contribution::getTableName();
- $scheduled_today->whereAdd("EXISTS (SELECT 1 FROM `{$ct}` WHERE `contribution_status_id` = 1 AND `{$t}`.id = `{$ct}`.`contribution_recur_id`)");
-
- // Exclude contributions that have already been processed
- $scheduled_today->whereAdd("NOT EXISTS (SELECT 1 FROM `{$ct}` WHERE `{$ct}`.`receive_date` >= `{$t}`.`next_sched_contribution_date` AND `{$t}`.id = `{$ct}`.`contribution_recur_id`)");
-
- $scheduled_today->find();
-
- $scheduled_contributions = [];
-
- while ($scheduled_today->fetch()) {
- $scheduled_contributions[] = clone $scheduled_today;
- }
-
- return $scheduled_contributions;
-}
-
-/**
- * get_scheduled_failed_contributions
- *
- * Gets recurring contributions that are failed and to be processed today
- *
- * @return array An array of contribtion_recur objects
- */
-function get_scheduled_failed_contributions($payment_processor) {
- $maxFailRetry = Civi::settings()
- ->get('eway_recurring_contribution_max_retry');
-
- $scheduled_today = new CRM_Contribute_BAO_ContributionRecur();
-
- // Only get contributions for the current processor
- $scheduled_today->payment_processor_id = $payment_processor['id'];
-
- $scheduled_today->whereAdd("`failure_retry_date` <= now()");
-
- $scheduled_today->contribution_status_id = _contribution_status_id('In Progress');
- $scheduled_today->whereAdd("`failure_count` < " . $maxFailRetry);
- $scheduled_today->whereAdd("`failure_count` > 0");
-
- // CIVIEWAY-124: Exclude contributions that never completed
- $t = $scheduled_today->tableName();
- $ct = CRM_Contribute_BAO_Contribution::getTableName();
- $scheduled_today->whereAdd("EXISTS (SELECT 1 FROM `{$ct}` WHERE `contribution_status_id` = 1 AND `{$t}`.id = `contribution_recur_id`)");
-
- // Exclude contributions that have already been processed
- $scheduled_today->whereAdd("NOT EXISTS (SELECT 1 FROM `{$ct}` WHERE `{$ct}`.`receive_date` >= `{$t}`.`failure_retry_date` AND `{$t}`.id = `{$ct}`.`contribution_recur_id`)");
-
- $scheduled_today->find();
-
- $scheduled_failed_contributions = [];
-
- while ($scheduled_today->fetch()) {
- $scheduled_failed_contributions[] = clone $scheduled_today;
- }
-
- return $scheduled_failed_contributions;
-}
-
-/**
- * process_eway_payment
- *
- * Processes an eWay token payment
- *
- * @param object $eWayClient An eWay client set up and ready to go
- * @param string $managed_customer_id The eWay token ID for the credit card you
- * want to process
- * @param string $amount_in_cents The amount in cents to charge the customer
- * @param string $invoice_reference InvoiceReference to send to eWay
- * @param string $invoice_description InvoiceDescription to send to eWay
- *
- * @return object eWay response object
- * @throws SoapFault exceptions
- */
-function process_eway_payment($eWayClient, $managed_customer_id, $amount_in_cents, $invoice_reference, $invoice_description) {
-
- static $prev_response = NULL;
-
- $paymentTransaction = [
- 'Customer' => [
- 'TokenCustomerID' => substr($managed_customer_id,0,16)
- ],
- 'Payment' => [
- 'TotalAmount' => substr($amount_in_cents,0,10),
- 'InvoiceDescription' => substr(trim($invoice_description), 0, 64),
- 'InvoiceReference' => substr($invoice_reference,0,64),
- ],
- 'TransactionType' => \Eway\Rapid\Enum\TransactionType::MOTO
- ];
- $eWayResponse = $eWayClient->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $paymentTransaction);
-
- if (isset($prev_response) && $prev_response->getAttribute('TransactionID') == $eWayResponse->getAttribute('TransactionID')) {
- throw new Exception (
- 'eWay ProcessPayment returned duplicate transaction number: ' .
- $prev_response->getAttribute('TransactionID') . ' vs ' . $eWayResponse->getAttribute('TransactionID')
- );
- }
-
- $prev_response = &$eWayResponse;
-
- return $eWayResponse;
-}
-
-/**
- * send_receipt_email
- *
- * Sends a receipt for a contribution
- *
- * @param string $contribution_id The ID of the contribution to mark as complete
- * @param CRM_Core_Payment $paymentObject CRM_Core_Payment object
- *
- * @return bool Success or failure
- */
-function send_receipt_email($contribution_id, $paymentObject) {
- $contribution = new CRM_Contribute_BAO_Contribution();
- $contribution->id = $contribution_id;
- $contribution->find(TRUE);
-
- $is_email_receipt = civicrm_api3('ContributionPage', 'getvalue', [
- 'id' => $contribution->contribution_page_id,
- 'return' => 'is_email_receipt',
- ]);
-
- if (!$is_email_receipt) {
- return NULL;
- }
-
- list($name, $email) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contribution->contact_id);
-
- $domainValues = CRM_Core_BAO_Domain::getNameAndEmail();
- $receiptFrom = "$domainValues[0] <$domainValues[1]>";
- $receiptFromEmail = $domainValues[1];
-
- $params = [
- 'groupName' => 'msg_tpl_workflow_contribution',
- 'valueName' => 'contribution_online_receipt',
- 'contactId' => $contribution->contact_id,
- 'tplParams' => [
- 'contributeMode' => 'directIPN',
- // Tells the person to contact us for cancellations
- 'receiptFromEmail' => $receiptFromEmail,
- 'amount' => $contribution->total_amount,
- 'title' => RECEIPT_SUBJECT_TITLE,
- 'is_recur' => TRUE,
- 'is_monetary' => TRUE,
- 'is_pay_later' => FALSE,
- 'billingName' => $name,
- 'email' => $email,
- 'trxn_id' => $contribution->trxn_id,
- 'receive_date' => CRM_Utils_Date::format($contribution->receive_date),
- 'updateSubscriptionBillingUrl' => $paymentObject->subscriptionURL($contribution_id, 'contribution', 'billing'),
- ],
- 'from' => $receiptFrom,
- 'toName' => $name,
- 'toEmail' => $email,
- 'isTest' => $contribution->is_test,
- ];
-
- list($sent, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate($params);
-
- return $sent;
-}
diff --git a/info.xml b/info.xml
index 5addec3..62ae8ed 100644
--- a/info.xml
+++ b/info.xml
@@ -15,8 +15,8 @@
support@agileware.com.au
stable
- 2023-05-19
- 2.5.0
+ 2023-07-21
+ 2.6.0
5.38
diff --git a/js/eway.js b/js/eway.js
index 6d2a973..075f6bb 100644
--- a/js/eway.js
+++ b/js/eway.js
@@ -3,17 +3,20 @@ CRM.eway.paymentTokens = [];
CRM.eway.updatedToken = 0;
CRM.eway.selectedToken = 0;
-CRM.eway.setPaymentTokenOptions = function () {
- CRM.api4('PaymentToken', 'get', {
- where: [
- [ 'contact_id', '=', CRM.eway.contact_id ],
- [ 'expiry_date' , '>', 'now' ],
- ],
- orderBy: { expiry_date: 'DESC' }
- }).then(
- CRM.eway.updateOptions,
- console.error
- );
+CRM.eway.setPaymentTokenOptions = async function () {
+ try {
+ const options = await CRM.api4('PaymentToken', 'get', {
+ where: [
+ ['contact_id', '=', CRM.eway.contact_id],
+ ['expiry_date', '>', 'now'],
+ ],
+ orderBy: {expiry_date: 'DESC'}
+ });
+
+ CRM.eway.updateOptions(options);
+ } catch (e) {
+ console.error(e);
+ }
};
/**