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: