Merge pull request #22724 from braders/feature/group-search-null-columns
[civicrm-core.git] / CRM / Core / Payment / Dummy.php
CommitLineData
6a488035
TO
1<?php
2/*
3 * Copyright (C) 2007
4 * Licensed to CiviCRM under the Academic Free License version 3.0.
5 *
6 * Written and contributed by Ideal Solution, LLC (http://www.idealso.com)
7 *
8 */
9
10/**
11 *
12 * @package CRM
13 * @author Marshal Newrock <marshal@idealso.com>
6a488035
TO
14 */
15
65f1a9a1 16use Civi\Payment\Exception\PaymentProcessorException;
3bfefe56 17use Civi\Payment\PropertyBag;
65f1a9a1 18
c866eb5f
TO
19/**
20 * Dummy payment processor
6a488035
TO
21 */
22class CRM_Core_Payment_Dummy extends CRM_Core_Payment {
6a488035 23
651bff36 24 protected $_mode;
be2fb01f 25 protected $_doDirectPaymentResult = [];
f8fe0df6 26
27 /**
eca28728
EM
28 * Set result from do Direct Payment for test purposes.
29 *
f8fe0df6 30 * @param array $doDirectPaymentResult
eca28728 31 * Result to be returned from test.
f8fe0df6 32 */
33 public function setDoDirectPaymentResult($doDirectPaymentResult) {
34 $this->_doDirectPaymentResult = $doDirectPaymentResult;
9d0f10b7 35 if (empty($this->_doDirectPaymentResult['trxn_id'])) {
be2fb01f 36 $this->_doDirectPaymentResult['trxn_id'] = [];
9d0f10b7
EM
37 }
38 else {
39 $this->_doDirectPaymentResult['trxn_id'] = (array) $doDirectPaymentResult['trxn_id'];
40 }
f8fe0df6 41 }
6a488035 42
6a488035 43 /**
fe482240 44 * Constructor.
6a488035 45 *
6a0b768e
TO
46 * @param string $mode
47 * The mode of operation: live or test.
6a488035 48 *
3bfefe56 49 * @param array $paymentProcessor
6a488035 50 */
00be9182 51 public function __construct($mode, &$paymentProcessor) {
6a488035
TO
52 $this->_mode = $mode;
53 $this->_paymentProcessor = $paymentProcessor;
6a488035
TO
54 }
55
6a488035 56 /**
e4fcfa1d 57 * @param array|PropertyBag $params
6a488035 58 *
e4fcfa1d 59 * @param string $component
6a488035 60 *
a6c01b45 61 * @return array
e4fcfa1d
MW
62 * Result array (containing at least the key payment_status_id)
63 *
65f1a9a1 64 * @throws \Civi\Payment\Exception\PaymentProcessorException
6a488035 65 */
e4fcfa1d
MW
66 public function doPayment(&$params, $component = 'contribute') {
67 $this->_component = $component;
68 $statuses = CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id', 'validate');
69
3bfefe56 70 $propertyBag = PropertyBag::cast($params);
e4fcfa1d
MW
71
72 // If we have a $0 amount, skip call to processor and set payment_status to Completed.
73 // Conceivably a processor might override this - perhaps for setting up a token - but we don't
74 // have an example of that at the mome.
75 if ($propertyBag->getAmount() == 0) {
76 $result['payment_status_id'] = array_search('Completed', $statuses);
77 $result['payment_status'] = 'Completed';
78 return $result;
79 }
80
6a488035
TO
81 // Invoke hook_civicrm_paymentProcessor
82 // In Dummy's case, there is no translation of parameters into
83 // the back-end's canonical set of parameters. But if a processor
84 // does this, it needs to invoke this hook after it has done translation,
85 // but before it actually starts talking to its proprietary back-end.
3bfefe56 86 if ($propertyBag->getIsRecur()) {
87 $throwAnENoticeIfNotSetAsTheseAreRequired = $propertyBag->getRecurFrequencyInterval() . $propertyBag->getRecurFrequencyUnit();
3a1f9fd3 88 }
6a488035 89 // no translation in Dummy processor
e4fcfa1d 90 CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $propertyBag);
7758bd2b
EM
91 // This means we can test failing transactions by setting a past year in expiry. A full expiry check would
92 // be more complete.
118a7b84 93 if (!empty($params['credit_card_exp_date']['Y']) && CRM_Utils_Time::date('Y') >
7758bd2b 94 CRM_Core_Payment_Form::getCreditCardExpirationYear($params)) {
90530b87 95 throw new PaymentProcessorException(ts('Invalid expiry date'));
7758bd2b 96 }
e4fcfa1d 97
f8fe0df6 98 if (!empty($this->_doDirectPaymentResult)) {
9d0f10b7 99 $result = $this->_doDirectPaymentResult;
78bdb5a6
MW
100 if (empty($result['payment_status_id'])) {
101 $result['payment_status_id'] = array_search('Pending', $statuses);
102 $result['payment_status'] = 'Pending';
103 }
104 if ($result['payment_status_id'] === 'failed') {
65f1a9a1 105 throw new PaymentProcessorException($result['message'] ?? 'failed');
106 }
9d0f10b7
EM
107 $result['trxn_id'] = array_shift($this->_doDirectPaymentResult['trxn_id']);
108 return $result;
f8fe0df6 109 }
5562e2e3 110
e4fcfa1d 111 $result['trxn_id'] = $this->getTrxnID();
5562e2e3 112
3a2251cf 113 // Add a fee_amount so we can make sure fees are handled properly in underlying classes.
e4fcfa1d
MW
114 $result['fee_amount'] = 1.50;
115 $result['description'] = $this->getPaymentDescription($params);
116
117 if (!isset($result['payment_status_id'])) {
118 if (!empty($propertyBag->getIsRecur())) {
119 // See comment block.
120 $result['payment_status_id'] = array_search('Pending', $statuses);
121 $result['payment_status'] = 'Pending';
122 }
123 else {
124 $result['payment_status_id'] = array_search('Completed', $statuses);
125 $result['payment_status'] = 'Completed';
126 }
127 }
128
e4fcfa1d 129 return $result;
6a488035
TO
130 }
131
6c786a9b 132 /**
6bc775cf
MD
133 * Does this payment processor support refund?
134 *
135 * @return bool
136 */
137 public function supportsRefund() {
138 return TRUE;
139 }
140
b59a905f 141 /**
3bfefe56 142 * Supports altering future start dates.
143 *
b59a905f
SL
144 * @return bool
145 */
146 public function supportsFutureRecurStartDate() {
147 return TRUE;
148 }
149
6bc775cf
MD
150 /**
151 * Submit a refund payment
152 *
153 * @throws \Civi\Payment\Exception\PaymentProcessorException
154 *
155 * @param array $params
156 * Assoc array of input parameters for this transaction.
157 */
158 public function doRefund(&$params) {}
159
6a488035 160 /**
fe482240 161 * This function checks to see if we have the right config values.
6a488035 162 *
a6c01b45
CW
163 * @return string
164 * the error message if any
6a488035 165 */
00be9182 166 public function checkConfig() {
6a488035
TO
167 return NULL;
168 }
96025800 169
10df056e 170 /**
171 * Get an array of the fields that can be edited on the recurring contribution.
172 *
173 * Some payment processors support editing the amount and other scheduling details of recurring payments, especially
174 * those which use tokens. Others are fixed. This function allows the processor to return an array of the fields that
175 * can be updated from the contribution recur edit screen.
176 *
177 * The fields are likely to be a subset of these
178 * - 'amount',
179 * - 'installments',
180 * - 'frequency_interval',
181 * - 'frequency_unit',
182 * - 'cycle_day',
183 * - 'next_sched_contribution_date',
184 * - 'end_date',
185 * - 'failure_retry_day',
186 *
187 * The form does not restrict which fields from the contribution_recur table can be added (although if the html_type
188 * metadata is not defined in the xml for the field it will cause an error.
189 *
190 * Open question - would it make sense to return membership_id in this - which is sometimes editable and is on that
191 * form (UpdateSubscription).
192 *
193 * @return array
194 */
195 public function getEditableRecurringScheduleFields() {
be2fb01f 196 return ['amount', 'next_sched_contribution_date'];
10df056e 197 }
198
f926d56f 199 /**
3bfefe56 200 * Does this processor support cancelling recurring contributions through code.
f926d56f 201 *
3bfefe56 202 * If the processor returns true it must be possible to take action from within CiviCRM
203 * that will result in no further payments being processed. In the case of token processors (e.g
204 * IATS, eWay) updating the contribution_recur table is probably sufficient.
205 *
206 * @return bool
f926d56f 207 */
3bfefe56 208 protected function supportsCancelRecurring() {
f926d56f
MWMC
209 return TRUE;
210 }
211
3bfefe56 212 /**
213 * Cancel a recurring subscription.
214 *
215 * Payment processor classes should override this rather than implementing cancelSubscription.
216 *
217 * A PaymentProcessorException should be thrown if the update of the contribution_recur
218 * record should not proceed (in many cases this function does nothing
219 * as the payment processor does not need to take any action & this should silently
220 * proceed. Note the form layer will only call this after calling
221 * $processor->supports('cancelRecurring');
222 *
223 * @param \Civi\Payment\PropertyBag $propertyBag
224 *
225 * @return array
226 *
227 * @throws \Civi\Payment\Exception\PaymentProcessorException
228 */
229 public function doCancelRecurring(PropertyBag $propertyBag) {
230 return ['message' => ts('Recurring contribution cancelled')];
231 }
232
5562e2e3 233 /**
234 * Get a value for the transaction ID.
235 *
236 * Value is made up of the max existing value + a random string.
237 *
238 * Note the random string is likely a historical workaround.
239 *
240 * @return string
241 */
242 protected function getTrxnID() {
243 $string = $this->_mode;
244 $trxn_id = CRM_Core_DAO::singleValueQuery("SELECT MAX(trxn_id) FROM civicrm_contribution WHERE trxn_id LIKE '{$string}_%'");
245 $trxn_id = str_replace($string, '', $trxn_id);
246 $trxn_id = (int) $trxn_id + 1;
247 return $string . '_' . $trxn_id . '_' . uniqid();
248 }
249
6a488035 250}