Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
26 | class EEG_Aim extends EE_Onsite_Gateway |
||
27 | { |
||
28 | |||
29 | const LIVE_URL = 'https://secure2.authorize.net/gateway/transact.dll'; // Authnet URL |
||
30 | |||
31 | const SANDBOX_URL = 'https://test.authorize.net/gateway/transact.dll'; |
||
32 | |||
33 | protected $_login_id; |
||
34 | |||
35 | protected $_transaction_key; |
||
36 | |||
37 | protected $_server; |
||
38 | |||
39 | protected $_currencies_supported = array( |
||
40 | 'AUD', |
||
41 | 'USD', |
||
42 | 'CAD', |
||
43 | 'EUR', |
||
44 | 'GBP', |
||
45 | 'NZD', |
||
46 | ); |
||
47 | |||
48 | /** |
||
49 | * Whether to send test transactions (even to live site) |
||
50 | * |
||
51 | * @var boolean |
||
52 | */ |
||
53 | protected $_test_transactions; |
||
54 | |||
55 | private $VERIFY_PEER = false; |
||
56 | |||
57 | private $_x_post_fields = array( |
||
58 | "version" => "3.1", |
||
59 | "delim_char" => ",", |
||
60 | "delim_data" => "TRUE", |
||
61 | "relay_response" => "FALSE", |
||
62 | "encap_char" => "|", |
||
63 | ); |
||
64 | |||
65 | private $_additional_line_items = array(); |
||
66 | |||
67 | /** |
||
68 | * A list of all fields in the AIM API. |
||
69 | * Used to warn user if they try to set a field not offered in the API. |
||
70 | */ |
||
71 | private $_all_aim_fields = array( |
||
72 | "address", |
||
73 | "allow_partial_auth", |
||
74 | "amount", |
||
75 | "auth_code", |
||
76 | "authentication_indicator", |
||
77 | "bank_aba_code", |
||
78 | "bank_acct_name", |
||
79 | "bank_acct_num", |
||
80 | "bank_acct_type", |
||
81 | "bank_check_number", |
||
82 | "bank_name", |
||
83 | "card_code", |
||
84 | "card_num", |
||
85 | "cardholder_authentication_value", |
||
86 | "city", |
||
87 | "company", |
||
88 | "country", |
||
89 | "cust_id", |
||
90 | "customer_ip", |
||
91 | "delim_char", |
||
92 | "delim_data", |
||
93 | "description", |
||
94 | "duplicate_window", |
||
95 | "duty", |
||
96 | "echeck_type", |
||
97 | "email", |
||
98 | "email_customer", |
||
99 | "encap_char", |
||
100 | "exp_date", |
||
101 | "fax", |
||
102 | "first_name", |
||
103 | "footer_email_receipt", |
||
104 | "freight", |
||
105 | "header_email_receipt", |
||
106 | "invoice_num", |
||
107 | "last_name", |
||
108 | "line_item", |
||
109 | "login", |
||
110 | "method", |
||
111 | "phone", |
||
112 | "po_num", |
||
113 | "recurring_billing", |
||
114 | "relay_response", |
||
115 | "ship_to_address", |
||
116 | "ship_to_city", |
||
117 | "ship_to_company", |
||
118 | "ship_to_country", |
||
119 | "ship_to_first_name", |
||
120 | "ship_to_last_name", |
||
121 | "ship_to_state", |
||
122 | "ship_to_zip", |
||
123 | "split_tender_id", |
||
124 | "state", |
||
125 | "tax", |
||
126 | "tax_exempt", |
||
127 | "test_request", |
||
128 | "tran_key", |
||
129 | "trans_id", |
||
130 | "type", |
||
131 | "version", |
||
132 | "zip", |
||
133 | "solution_id", |
||
134 | "currency_code" |
||
135 | ); |
||
136 | |||
137 | |||
138 | /** |
||
139 | * Gets the URL where the request should go. This is filterable |
||
140 | * |
||
141 | * @return string |
||
142 | */ |
||
143 | protected function _get_server_url() |
||
144 | { |
||
145 | return apply_filters( |
||
146 | 'FHEE__EEG_Aim___get_server_url', |
||
147 | $this->_debug_mode ? self::SANDBOX_URL : self::LIVE_URL, |
||
148 | $this |
||
149 | ); |
||
150 | } |
||
151 | |||
152 | |||
153 | /** |
||
154 | * TEMPORARY CALLBACK! Do not use |
||
155 | * Callback which filters the server url. This is added so site admins can revert to using |
||
156 | * the old AIM server in case Akamai service breaks their integration. |
||
157 | * Using Akamai will, however, be mandatory on June 30th 2016 Authorize.net |
||
158 | * (see http://www.authorize.net/support/akamaifaqs/#firewall?utm_campaign=April%202016%20Technical%20Updates%20for%20Merchants.html&utm_medium=email&utm_source=Eloqua&elqTrackId=46103bdc375c411a979c2f658fc99074&elq=7026706360154fee9b6d588b27d8eb6a&elqaid=506&elqat=1&elqCampaignId=343) |
||
159 | * Once that happens, this will be obsolete and WILL BE REMOVED. |
||
160 | * |
||
161 | * @param string $url |
||
162 | * @param EEG_Aim $gateway_object |
||
163 | * @return string |
||
164 | */ |
||
165 | public function possibly_use_deprecated_aim_server($url, EEG_Aim $gateway_object) |
||
166 | { |
||
167 | if ($gateway_object->_server === 'authorize.net' && ! $gateway_object->_debug_mode) { |
||
168 | return 'https://secure.authorize.net/gateway/transact.dll'; |
||
169 | } else { |
||
170 | return $url; |
||
171 | } |
||
172 | } |
||
173 | |||
174 | |||
175 | /** |
||
176 | * Asks the gateway to do whatever it does to process the payment. Onsite gateways will |
||
177 | * usually send a request directly to the payment provider and update the payment's status based on that; |
||
178 | * whereas offsite gateways will usually just update the payment with the URL and query parameters to use |
||
179 | * for sending the request via http_remote_request() |
||
180 | * |
||
181 | * @param EEI_Payment $payment |
||
182 | * @param array $billing_info { |
||
183 | * @type $credit_card string |
||
184 | * @type $cvv string |
||
185 | * @type $exp_month string |
||
186 | * @type $exp_year string |
||
187 | * @see parent::do_direct_payment |
||
188 | * } |
||
189 | * @return EEI_Payment updated |
||
190 | */ |
||
191 | public function do_direct_payment($payment, $billing_info = null) |
||
192 | { |
||
193 | add_filter('FHEE__EEG_Aim___get_server_url', array($this, 'possibly_use_deprecated_aim_server'), 10, 2); |
||
194 | // Enable test mode if needed |
||
195 | // 4007000000027 <-- test successful visa |
||
196 | // 4222222222222 <-- test failure card number |
||
197 | |||
198 | $item_num = 1; |
||
199 | $transaction = $payment->transaction(); |
||
200 | $gateway_formatter = $this->_get_gateway_formatter(); |
||
201 | $order_description = $this->prepareStringForAuthnet($gateway_formatter->formatOrderDescription($payment)); |
||
202 | $primary_registrant = $transaction->primary_registration(); |
||
203 | // if we're are charging for the full amount, show the normal line items |
||
204 | // and the itemized total adds up properly |
||
205 | if ($this->_can_easily_itemize_transaction_for($payment)) { |
||
206 | $total_line_item = $transaction->total_line_item(); |
||
207 | foreach ($total_line_item->get_items() as $line_item) { |
||
208 | if ($line_item->quantity() == 0) { |
||
209 | continue; |
||
210 | } |
||
211 | $this->addLineItem( |
||
212 | $item_num++, |
||
213 | $gateway_formatter->formatLineItemName($line_item, $payment), |
||
214 | $gateway_formatter->formatLineItemDesc($line_item, $payment), |
||
215 | $line_item->quantity(), |
||
216 | $line_item->unit_price(), |
||
217 | 'N' |
||
218 | ); |
||
219 | $order_description .= $this->prepareStringForAuthnet($line_item->desc()) . ', '; |
||
220 | } |
||
221 | foreach ($total_line_item->tax_descendants() as $tax_line_item) { |
||
222 | $this->addLineItem( |
||
223 | $item_num++, |
||
224 | $tax_line_item->name(), |
||
225 | $tax_line_item->desc(), |
||
226 | 1, |
||
227 | $tax_line_item->total(), |
||
228 | 'N' |
||
229 | ); |
||
230 | } |
||
231 | } |
||
232 | |||
233 | // start transaction |
||
234 | // if in debug mode, use authorize.net's sandbox id; otherwise use the Event Espresso partner id |
||
235 | $partner_id = $this->_debug_mode ? 'AAA100302' : 'AAA105363'; |
||
236 | $this->setField('solution_id', $partner_id); |
||
237 | $this->setField('amount', $gateway_formatter->formatCurrency($payment->amount())); |
||
238 | $this->setField('description', substr(rtrim($order_description, ', '), 0, 255)); |
||
239 | $this->_set_sensitive_billing_data($billing_info); |
||
240 | $this->setField('first_name', $billing_info['first_name']); |
||
241 | $this->setField('last_name', $billing_info['last_name']); |
||
242 | $this->setField('email', $billing_info['email']); |
||
243 | $this->setField('company', $billing_info['company']); |
||
244 | $this->setField('address', $billing_info['address'].' '.$billing_info['address2']); |
||
245 | $this->setField('city', $billing_info['city']); |
||
246 | $this->setField('state', $billing_info['state']); |
||
247 | $this->setField('country', $billing_info['country']); |
||
248 | $this->setField('zip', $billing_info['zip']); |
||
249 | $this->setField('fax', $billing_info['fax']); |
||
250 | $this->setField('cust_id', $primary_registrant->ID()); |
||
251 | $this->setField('phone', $billing_info['phone']); |
||
252 | $currency_config = LoaderFactory::getLoader()->load('EE_Currency_Config'); |
||
253 | $this->setField('currency_code', $currency_config->code); |
||
254 | // invoice_num would be nice to have it be unique per SPCO page-load, that way if users |
||
255 | // press back, they don't submit a duplicate. However, we may be keeping the user on teh same spco page |
||
256 | // in which case, we need to generate teh invoice num per request right here... |
||
257 | $this->setField('invoice_num', wp_generate_password(12, false));// $billing_info['_reg-page-billing-invoice-'.$this->_gateway_name]['value']); |
||
258 | // tell AIM that any duplicates sent in the next 5 minutes are to be ignored |
||
259 | $this->setField('duplicate_window', 5 * MINUTE_IN_SECONDS); |
||
260 | |||
261 | if ($this->_test_transactions) { |
||
262 | $this->test_request = "true"; |
||
|
|||
263 | } |
||
264 | |||
265 | // Capture response |
||
266 | $this->type = "AUTH_CAPTURE"; |
||
267 | $response = $this->_sendRequest($payment); |
||
268 | if (! empty($response)) { |
||
269 | if ($response->error_message) { |
||
270 | $payment->set_status($this->_pay_model->failed_status()); |
||
271 | $payment->set_gateway_response($response->error_message); |
||
272 | } else { |
||
273 | $payment_status = $response->approved |
||
274 | ? $this->_pay_model->approved_status() |
||
275 | : $this->_pay_model->declined_status(); |
||
276 | $payment->set_status($payment_status); |
||
277 | // make sure we interpret the AMT as a float, not an international string (where periods are thousand separators) |
||
278 | $payment->set_amount((float) $response->amount); |
||
279 | $payment->set_gateway_response( |
||
280 | sprintf( |
||
281 | esc_html__('%1$s (Reason Code: %2$s)', 'event_espresso'), |
||
282 | $response->response_reason_text, |
||
283 | $response->response_reason_code |
||
284 | ) |
||
285 | ); |
||
286 | if ($this->_debug_mode) { |
||
287 | $txn_id = $response->invoice_number; |
||
288 | } else { |
||
289 | $txn_id = $response->transaction_id; |
||
290 | } |
||
291 | $payment->set_txn_id_chq_nmbr($txn_id); |
||
292 | } |
||
293 | $payment->set_extra_accntng($primary_registrant->reg_code()); |
||
294 | $payment->set_details(print_r($response, true)); |
||
295 | } else { |
||
296 | $payment->set_status($this->_pay_model->failed_status()); |
||
297 | $payment->set_gateway_response(__("There was no response from Authorize.net", 'event_espresso')); |
||
298 | $payment->set_details(print_r($response, true)); |
||
299 | } |
||
300 | return $payment; |
||
301 | } |
||
302 | |||
303 | |||
304 | /** |
||
305 | * Sets billing data for the upcoming request to AIM that is considered sensitive; |
||
306 | * also this method can be overridden by children classes to easily change |
||
307 | * what billing data gets sent |
||
308 | * |
||
309 | * @param array $billing_info |
||
310 | */ |
||
311 | protected function _set_sensitive_billing_data($billing_info) |
||
317 | |||
318 | |||
319 | /** |
||
320 | * Add a line item. |
||
321 | * |
||
322 | * @param string $item_id |
||
323 | * @param string $item_name |
||
324 | * @param string $item_description |
||
325 | * @param string $item_quantity |
||
326 | * @param string $item_unit_price |
||
327 | * @param string $item_taxable |
||
328 | */ |
||
329 | public function addLineItem($item_id, $item_name, $item_description, $item_quantity, $item_unit_price, $item_taxable) |
||
341 | |||
342 | |||
343 | /** |
||
344 | * Set an individual name/value pair. This will append x_ to the name |
||
345 | * before posting. |
||
346 | * |
||
347 | * @param string $name |
||
348 | * @param string $value |
||
349 | * @throws AuthorizeNetException |
||
350 | */ |
||
351 | protected function setField($name, $value) |
||
360 | |||
361 | |||
362 | /** |
||
363 | * Posts the request to AuthorizeNet & returns response. |
||
364 | * |
||
365 | * @param $payment |
||
366 | * @return \EE_AuthorizeNetAIM_Response |
||
367 | */ |
||
368 | private function _sendRequest($payment) |
||
406 | |||
407 | |||
408 | /** |
||
409 | * Logs the clean data only |
||
410 | * |
||
411 | * @param array $request_array |
||
412 | * @param EEI_Payment $payment |
||
413 | */ |
||
414 | protected function _log_clean_request($request_array, $payment) |
||
434 | |||
435 | |||
436 | |||
437 | /** |
||
438 | * Logs the response and cleans it |
||
439 | * |
||
440 | * @param EE_AuthorizeNetAIM_Response $response_obj |
||
441 | * @param EE_Payment $payment |
||
442 | * @return \EE_AuthorizeNetAIM_Response |
||
443 | */ |
||
444 | private function _log_and_clean_response($response_obj, $payment) |
||
450 | |||
451 | /** |
||
452 | * Removes characters Authorize.net doesn't handle well. |
||
453 | * @since $VID:$ |
||
454 | * @param $text |
||
455 | * @return string |
||
456 | */ |
||
457 | private function prepareStringForAuthnet($text) |
||
465 | } |
||
466 | |||
669 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: