squareup.php (21702B)
1 <?php 2 3 class ModelExtensionPaymentSquareup extends Model { 4 const RECURRING_ACTIVE = 1; 5 const RECURRING_INACTIVE = 2; 6 const RECURRING_CANCELLED = 3; 7 const RECURRING_SUSPENDED = 4; 8 const RECURRING_EXPIRED = 5; 9 const RECURRING_PENDING = 6; 10 11 const TRANSACTION_DATE_ADDED = 0; 12 const TRANSACTION_PAYMENT = 1; 13 const TRANSACTION_OUTSTANDING_PAYMENT = 2; 14 const TRANSACTION_SKIPPED = 3; 15 const TRANSACTION_FAILED = 4; 16 const TRANSACTION_CANCELLED = 5; 17 const TRANSACTION_SUSPENDED = 6; 18 const TRANSACTION_SUSPENDED_FAILED = 7; 19 const TRANSACTION_OUTSTANDING_FAILED = 8; 20 const TRANSACTION_EXPIRED = 9; 21 22 public function getMethod($address, $total) { 23 $geo_zone_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$this->config->get('payment_squareup_geo_zone_id') . "' AND country_id = '" . (int)$address['country_id'] . "' AND (zone_id = '" . (int)$address['zone_id'] . "' OR zone_id = '0')"); 24 25 $squareup_display_name = $this->config->get('payment_squareup_display_name'); 26 27 $this->load->language('extension/payment/squareup'); 28 29 if (!empty($squareup_display_name[$this->config->get('config_language_id')])) { 30 $title = $squareup_display_name[$this->config->get('config_language_id')]; 31 } else { 32 $title = $this->language->get('text_default_squareup_name'); 33 } 34 35 $status = true; 36 37 $minimum_total = (float)$this->config->get('payment_squareup_total'); 38 39 $squareup_geo_zone_id = $this->config->get('payment_squareup_geo_zone_id'); 40 41 if ($minimum_total > 0 && $minimum_total > $total) { 42 $status = false; 43 } else if (empty($squareup_geo_zone_id)) { 44 $status = true; 45 } else if ($geo_zone_query->num_rows == 0) { 46 $status = false; 47 } 48 49 $method_data = array(); 50 51 if ($status) { 52 $method_data = array( 53 'code' => 'squareup', 54 'title' => $title, 55 'terms' => '', 56 'sort_order' => (int)$this->config->get('payment_squareup_sort_order') 57 ); 58 } 59 60 return $method_data; 61 } 62 63 public function addTransaction($transaction, $merchant_id, $address, $order_id, $user_agent, $ip) { 64 $amount = $this->squareup->standardDenomination($transaction['tenders'][0]['amount_money']['amount'], $transaction['tenders'][0]['amount_money']['currency']); 65 66 $this->db->query("INSERT INTO `" . DB_PREFIX . "squareup_transaction` SET transaction_id='" . $this->db->escape($transaction['id']) . "', merchant_id='" . $this->db->escape($merchant_id) . "', location_id='" . $this->db->escape($transaction['location_id']) . "', order_id='" . (int)$order_id . "', transaction_type='" . $this->db->escape($transaction['tenders'][0]['card_details']['status']) . "', transaction_amount='" . (float)$amount . "', transaction_currency='" . $this->db->escape($transaction['tenders'][0]['amount_money']['currency']) . "', billing_address_city='" . $this->db->escape($address['locality']) . "', billing_address_country='" . $this->db->escape($address['country']) . "', billing_address_postcode='" . $this->db->escape($address['postal_code']) . "', billing_address_province='" . $this->db->escape($address['sublocality']) . "', billing_address_street_1='" . $this->db->escape($address['address_line_1']) . "', billing_address_street_2='" . $this->db->escape($address['address_line_2']) . "', device_browser='" . $this->db->escape($user_agent) . "', device_ip='" . $this->db->escape($ip) . "', created_at='" . $this->db->escape($transaction['created_at']) . "', is_refunded='" . (int)(!empty($transaction['refunds'])) . "', refunded_at='" . $this->db->escape(!empty($transaction['refunds']) ? $transaction['refunds'][0]['created_at'] : '') . "', tenders='" . $this->db->escape(json_encode($transaction['tenders'])) . "', refunds='" . $this->db->escape(json_encode(!empty($transaction['refunds']) ? $transaction['refunds'] : array())) . "'"); 67 } 68 69 public function tokenExpiredEmail() { 70 if (!$this->mailResendPeriodExpired('token_expired')) { 71 return; 72 } 73 74 $mail = new Mail(); 75 76 $mail->protocol = $this->config->get('config_mail_protocol'); 77 $mail->parameter = $this->config->get('config_mail_parameter'); 78 79 $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname'); 80 $mail->smtp_username = $this->config->get('config_mail_smtp_username'); 81 $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8'); 82 $mail->smtp_port = $this->config->get('config_mail_smtp_port'); 83 $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout'); 84 85 $subject = $this->language->get('text_token_expired_subject'); 86 $message = $this->language->get('text_token_expired_message'); 87 88 $mail->setTo($this->config->get('config_email')); 89 $mail->setFrom($this->config->get('config_email')); 90 $mail->setSender($this->config->get('config_name')); 91 $mail->setSubject(html_entity_decode($subject, ENT_QUOTES, 'UTF-8')); 92 $mail->setText(strip_tags($message)); 93 $mail->setHtml($message); 94 95 $mail->send(); 96 } 97 98 public function tokenRevokedEmail() { 99 if (!$this->mailResendPeriodExpired('token_revoked')) { 100 return; 101 } 102 103 $mail = new Mail(); 104 105 $mail->protocol = $this->config->get('config_mail_protocol'); 106 $mail->parameter = $this->config->get('config_mail_parameter'); 107 108 $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname'); 109 $mail->smtp_username = $this->config->get('config_mail_smtp_username'); 110 $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8'); 111 $mail->smtp_port = $this->config->get('config_mail_smtp_port'); 112 $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout'); 113 114 $subject = $this->language->get('text_token_revoked_subject'); 115 $message = $this->language->get('text_token_revoked_message'); 116 117 $mail->setTo($this->config->get('config_email')); 118 $mail->setFrom($this->config->get('config_email')); 119 $mail->setSender($this->config->get('config_name')); 120 $mail->setSubject(html_entity_decode($subject, ENT_QUOTES, 'UTF-8')); 121 $mail->setText(strip_tags($message)); 122 $mail->setHtml($message); 123 124 $mail->send(); 125 } 126 127 public function cronEmail($result) { 128 $mail = new Mail(); 129 130 $mail->protocol = $this->config->get('config_mail_protocol'); 131 $mail->parameter = $this->config->get('config_mail_parameter'); 132 133 $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname'); 134 $mail->smtp_username = $this->config->get('config_mail_smtp_username'); 135 $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8'); 136 $mail->smtp_port = $this->config->get('config_mail_smtp_port'); 137 $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout'); 138 139 $br = '<br />'; 140 141 $subject = $this->language->get('text_cron_subject'); 142 143 $message = $this->language->get('text_cron_message') . $br . $br; 144 145 $message .= '<strong>' . $this->language->get('text_cron_summary_token_heading') . '</strong>' . $br; 146 147 if ($result['token_update_error']) { 148 $message .= $result['token_update_error'] . $br . $br; 149 } else { 150 $message .= $this->language->get('text_cron_summary_token_updated') . $br . $br; 151 } 152 153 if (!empty($result['transaction_error'])) { 154 $message .= '<strong>' . $this->language->get('text_cron_summary_error_heading') . '</strong>' . $br; 155 156 $message .= implode($br, $result['transaction_error']) . $br . $br; 157 } 158 159 if (!empty($result['transaction_fail'])) { 160 $message .= '<strong>' . $this->language->get('text_cron_summary_fail_heading') . '</strong>' . $br; 161 162 foreach ($result['transaction_fail'] as $order_recurring_id => $amount) { 163 $message .= sprintf($this->language->get('text_cron_fail_charge'), $order_recurring_id, $amount) . $br; 164 } 165 } 166 167 if (!empty($result['transaction_success'])) { 168 $message .= '<strong>' . $this->language->get('text_cron_summary_success_heading') . '</strong>' . $br; 169 170 foreach ($result['transaction_success'] as $order_recurring_id => $amount) { 171 $message .= sprintf($this->language->get('text_cron_success_charge'), $order_recurring_id, $amount) . $br; 172 } 173 } 174 175 $mail->setTo($this->config->get('payment_squareup_cron_email')); 176 $mail->setFrom($this->config->get('config_email')); 177 $mail->setSender($this->config->get('config_name')); 178 $mail->setSubject(html_entity_decode($subject, ENT_QUOTES, 'UTF-8')); 179 $mail->setText(strip_tags($message)); 180 $mail->setHtml($message); 181 $mail->send(); 182 } 183 184 public function recurringPayments() { 185 return (bool)$this->config->get('payment_squareup_recurring_status'); 186 } 187 188 public function createRecurring($recurring, $order_id, $description, $reference) { 189 $this->db->query("INSERT INTO `" . DB_PREFIX . "order_recurring` SET `order_id` = '" . (int)$order_id . "', `date_added` = NOW(), `status` = '" . self::RECURRING_ACTIVE . "', `product_id` = '" . (int)$recurring['product_id'] . "', `product_name` = '" . $this->db->escape($recurring['name']) . "', `product_quantity` = '" . $this->db->escape($recurring['quantity']) . "', `recurring_id` = '" . (int)$recurring['recurring']['recurring_id'] . "', `recurring_name` = '" . $this->db->escape($recurring['recurring']['name']) . "', `recurring_description` = '" . $this->db->escape($description) . "', `recurring_frequency` = '" . $this->db->escape($recurring['recurring']['frequency']) . "', `recurring_cycle` = '" . (int)$recurring['recurring']['cycle'] . "', `recurring_duration` = '" . (int)$recurring['recurring']['duration'] . "', `recurring_price` = '" . (float)$recurring['recurring']['price'] . "', `trial` = '" . (int)$recurring['recurring']['trial'] . "', `trial_frequency` = '" . $this->db->escape($recurring['recurring']['trial_frequency']) . "', `trial_cycle` = '" . (int)$recurring['recurring']['trial_cycle'] . "', `trial_duration` = '" . (int)$recurring['recurring']['trial_duration'] . "', `trial_price` = '" . (float)$recurring['recurring']['trial_price'] . "', `reference` = '" . $this->db->escape($reference) . "'"); 190 191 return $this->db->getLastId(); 192 } 193 194 public function validateCRON() { 195 if (!$this->config->get('payment_squareup_status') || !$this->config->get('payment_squareup_recurring_status')) { 196 return false; 197 } 198 199 if (isset($this->request->get['cron_token']) && $this->request->get['cron_token'] == $this->config->get('payment_squareup_cron_token')) { 200 return true; 201 } 202 203 if (defined('SQUAREUP_ROUTE')) { 204 return true; 205 } 206 207 return false; 208 } 209 210 public function updateToken() { 211 try { 212 $response = $this->squareup->refreshToken(); 213 214 if (!isset($response['access_token']) || !isset($response['token_type']) || !isset($response['expires_at']) || !isset($response['merchant_id']) || $response['merchant_id'] != $this->config->get('payment_squareup_merchant_id')) { 215 return $this->language->get('error_squareup_cron_token'); 216 } else { 217 $this->editTokenSetting(array( 218 'payment_squareup_access_token' => $response['access_token'], 219 'payment_squareup_access_token_expires' => $response['expires_at'] 220 )); 221 } 222 } catch (\Squareup\Exception $e) { 223 return $e->getMessage(); 224 } 225 226 return ''; 227 } 228 229 public function nextRecurringPayments() { 230 $payments = array(); 231 232 $this->load->library('squareup'); 233 234 $recurring_sql = "SELECT * FROM `" . DB_PREFIX . "order_recurring` `or` INNER JOIN `" . DB_PREFIX . "squareup_transaction` st ON (st.transaction_id = `or`.reference) WHERE `or`.status='" . self::RECURRING_ACTIVE . "'"; 235 236 $this->load->model('checkout/order'); 237 238 foreach ($this->db->query($recurring_sql)->rows as $recurring) { 239 if (!$this->paymentIsDue($recurring['order_recurring_id'])) { 240 continue; 241 } 242 243 $order_info = $this->model_checkout_order->getOrder($recurring['order_id']); 244 245 $billing_address = array( 246 'first_name' => $order_info['payment_firstname'], 247 'last_name' => $order_info['payment_lastname'], 248 'address_line_1' => $recurring['billing_address_street_1'], 249 'address_line_2' => $recurring['billing_address_street_2'], 250 'locality' => $recurring['billing_address_city'], 251 'sublocality' => $recurring['billing_address_province'], 252 'postal_code' => $recurring['billing_address_postcode'], 253 'country' => $recurring['billing_address_country'], 254 'organization' => $recurring['billing_address_company'] 255 ); 256 257 $transaction_tenders = @json_decode($recurring['tenders'], true); 258 259 $price = (float)($recurring['trial'] ? $recurring['trial_price'] : $recurring['recurring_price']); 260 261 $transaction = array( 262 'idempotency_key' => uniqid(), 263 'amount_money' => array( 264 'amount' => $this->squareup->lowestDenomination($price * $recurring['product_quantity'], $recurring['transaction_currency']), 265 'currency' => $recurring['transaction_currency'] 266 ), 267 'billing_address' => $billing_address, 268 'buyer_email_address' => $order_info['email'], 269 'delay_capture' => false, 270 'customer_id' => $transaction_tenders[0]['customer_id'], 271 'customer_card_id' => $transaction_tenders[0]['card_details']['card']['id'], 272 'integration_id' => Squareup::SQUARE_INTEGRATION_ID 273 ); 274 275 $payments[] = array( 276 'is_free' => $price == 0, 277 'order_id' => $recurring['order_id'], 278 'order_recurring_id' => $recurring['order_recurring_id'], 279 'billing_address' => $billing_address, 280 'transaction' => $transaction 281 ); 282 } 283 284 return $payments; 285 } 286 287 public function addRecurringTransaction($order_recurring_id, $reference, $amount, $status) { 288 if ($status) { 289 $type = self::TRANSACTION_PAYMENT; 290 } else { 291 $type = self::TRANSACTION_FAILED; 292 } 293 294 $this->db->query("INSERT INTO `" . DB_PREFIX . "order_recurring_transaction` SET order_recurring_id='" . (int)$order_recurring_id . "', reference='" . $this->db->escape($reference) . "', type='" . (int)$type . "', amount='" . (float)$amount . "', date_added=NOW()"); 295 } 296 297 public function updateRecurringExpired($order_recurring_id) { 298 $recurring_info = $this->getRecurring($order_recurring_id); 299 300 if ($recurring_info['trial']) { 301 // If we are in trial, we need to check if the trial will end at some point 302 $expirable = (bool)$recurring_info['trial_duration']; 303 } else { 304 // If we are not in trial, we need to check if the recurring will end at some point 305 $expirable = (bool)$recurring_info['recurring_duration']; 306 } 307 308 // If recurring payment can expire (trial_duration > 0 AND recurring_duration > 0) 309 if ($expirable) { 310 $number_of_successful_payments = $this->getTotalSuccessfulPayments($order_recurring_id); 311 312 $total_duration = (int)$recurring_info['trial_duration'] + (int)$recurring_info['recurring_duration']; 313 314 // If successful payments exceed total_duration 315 if ($number_of_successful_payments >= $total_duration) { 316 $this->db->query("UPDATE `" . DB_PREFIX . "order_recurring` SET status='" . self::RECURRING_EXPIRED . "' WHERE order_recurring_id='" . (int)$order_recurring_id . "'"); 317 318 return true; 319 } 320 } 321 322 return false; 323 } 324 325 public function updateRecurringTrial($order_recurring_id) { 326 $recurring_info = $this->getRecurring($order_recurring_id); 327 328 // If recurring payment is in trial and can expire (trial_duration > 0) 329 if ($recurring_info['trial'] && $recurring_info['trial_duration']) { 330 $number_of_successful_payments = $this->getTotalSuccessfulPayments($order_recurring_id); 331 332 // If successful payments exceed trial_duration 333 if ($number_of_successful_payments >= $recurring_info['trial_duration']) { 334 $this->db->query("UPDATE `" . DB_PREFIX . "order_recurring` SET trial='0' WHERE order_recurring_id='" . (int)$order_recurring_id . "'"); 335 336 return true; 337 } 338 } 339 340 return false; 341 } 342 343 public function suspendRecurringProfile($order_recurring_id) { 344 $this->db->query("UPDATE `" . DB_PREFIX . "order_recurring` SET status='" . self::RECURRING_SUSPENDED . "' WHERE order_recurring_id='" . (int)$order_recurring_id . "'"); 345 346 return true; 347 } 348 349 private function getLastSuccessfulRecurringPaymentDate($order_recurring_id) { 350 return $this->db->query("SELECT date_added FROM `" . DB_PREFIX . "order_recurring_transaction` WHERE order_recurring_id='" . (int)$order_recurring_id . "' AND type='" . self::TRANSACTION_PAYMENT . "' ORDER BY date_added DESC LIMIT 0,1")->row['date_added']; 351 } 352 353 private function getRecurring($order_recurring_id) { 354 $recurring_sql = "SELECT * FROM `" . DB_PREFIX . "order_recurring` WHERE order_recurring_id='" . (int)$order_recurring_id . "'"; 355 356 return $this->db->query($recurring_sql)->row; 357 } 358 359 private function getTotalSuccessfulPayments($order_recurring_id) { 360 return $this->db->query("SELECT COUNT(*) as total FROM `" . DB_PREFIX . "order_recurring_transaction` WHERE order_recurring_id='" . (int)$order_recurring_id . "' AND type='" . self::TRANSACTION_PAYMENT . "'")->row['total']; 361 } 362 363 private function paymentIsDue($order_recurring_id) { 364 // We know the recurring profile is active. 365 $recurring_info = $this->getRecurring($order_recurring_id); 366 367 if ($recurring_info['trial']) { 368 $frequency = $recurring_info['trial_frequency']; 369 $cycle = (int)$recurring_info['trial_cycle']; 370 } else { 371 $frequency = $recurring_info['recurring_frequency']; 372 $cycle = (int)$recurring_info['recurring_cycle']; 373 } 374 // Find date of last payment 375 if (!$this->getTotalSuccessfulPayments($order_recurring_id)) { 376 $previous_time = strtotime($recurring_info['date_added']); 377 } else { 378 $previous_time = strtotime($this->getLastSuccessfulRecurringPaymentDate($order_recurring_id)); 379 } 380 381 switch ($frequency) { 382 case 'day' : $time_interval = 24 * 3600; break; 383 case 'week' : $time_interval = 7 * 24 * 3600; break; 384 case 'semi_month' : $time_interval = 15 * 24 * 3600; break; 385 case 'month' : $time_interval = 30 * 24 * 3600; break; 386 case 'year' : $time_interval = 365 * 24 * 3600; break; 387 } 388 389 $due_date = date('Y-m-d', $previous_time + ($time_interval * $cycle)); 390 391 $this_date = date('Y-m-d'); 392 393 return $this_date >= $due_date; 394 } 395 396 private function editTokenSetting($settings) { 397 foreach ($settings as $key => $value) { 398 $this->db->query("DELETE FROM `" . DB_PREFIX . "setting` WHERE `code`='payment_squareup' AND `key`='" . $key . "'"); 399 400 $this->db->query("INSERT INTO `" . DB_PREFIX . "setting` SET `code`='payment_squareup', `key`='" . $key . "', `value`='" . $this->db->escape($value) . "', serialized=0, store_id=0"); 401 } 402 } 403 404 private function mailResendPeriodExpired($key) { 405 $result = (int)$this->cache->get('squareup.' . $key); 406 407 if (!$result) { 408 // No result, therefore this is the first e-mail and the re-send period should be regarded as expired. 409 $this->cache->set('squareup.' . $key, time()); 410 } else { 411 // There is an entry in the cache. We will calculate the time difference (delta) 412 $delta = time() - $result; 413 414 if ($delta >= 15 * 60) { 415 // More than 15 minutes have passed, therefore the re-send period has expired. 416 $this->cache->set('squareup.' . $key, time()); 417 } else { 418 // Less than 15 minutes have passed before the last e-mail, therefore the re-send period has not expired. 419 return false; 420 } 421 } 422 423 // In all other cases, the re-send period has expired. 424 return true; 425 } 426 }