public function checkout($params = []){ try{ if( empty($params['section']) || empty($params['representative']) ){ throw new \Exception('INVALID_DATA'); } if(!empty($params['onsite_secret'])){ $this->onsite = $this->is_onsite([ 'secret' => $params['onsite_secret'] ]); } if(!empty($params['pay_by_cash']) && $params['pay_by_cash'] == true && !$this->onsite){ throw new \Exception('INVALID_PAYMENT_METHOD'); } global $DB; $res = $DB->prepare("SELECT * FROM `event` WHERE `url_alias` = ? AND `deleted` = 0 LIMIT 1"); $res->execute([ $params['section'] ]); $event = $res->fetch(\PDO::FETCH_ASSOC); if( empty($event) ){ throw new \Exception('INVALID_EVENT'); } $res = $DB->prepare("SELECT * FROM `event_visitor_type` WHERE `event_id` = ? AND `id` = ? LIMIT 1"); $res->execute([ $event['id'], $params['visitor_type'] ]); $visitor_type = $res->fetch(\PDO::FETCH_ASSOC); if( empty($visitor_type) ){ throw new \Exception('INVALID_VISITOR_TYPE'); } $coupon_type = NULL; $modifier = -1; if($params['coupon_applied'] == true && !empty($params['coupon_code'])){ $res = $DB->prepare("SELECT * FROM `coupon` WHERE `event_id` = ? AND `coupon_code` = ?"); $res->execute([ $event['id'], $params['coupon_code'] ]); $coupon = $res->fetch(\PDO::FETCH_ASSOC); if (empty($coupon)) { throw new \Exception('INVALID_COUPON_CODE'); } if ($coupon['coupon_start_time'] > time()) { throw new \Exception('COUPON_NOT_AVAILABLE_YET'); } if (time() > $coupon['coupon_end_time']) { throw new \Exception('COUPON_EXPIRED'); } if($coupon['redemption_quota'] > 0 && $coupon['redeemed'] >= $coupon['redemption_quota']){ throw new \Exception('REDEMPTION_QUOTA_REACHED'); } $coupon_type = $coupon['type']; $modifier = floatval($coupon['modifier']); } $original_price = ((!empty($params['accompany']) ? count($params['accompany']) : 0) + 1) * floatval($visitor_type['price']); $total = floatval($params['total']); $discounted_price = empty($coupon_type) || $modifier > -1 ? $original_price : ($coupon_type === 'Percentage' ? $original_price * $modifier : ($coupon_type === 'Lump-sum' ? $original_price + $modifier : $original_price)); if($total !== $discounted_price){ throw new \Exception('INVALID_TOTAL_PRICE'); } $payment_method = $total > 0 ? ($params['pay_by_cash'] == true ? 'cash' : 'paypal') : ''; $secret = md5(uniqid()); $language_id_map = [ 'en_gb' => 1, 'zh_hk' => 2, 'zh_cn' => 3 ]; $res = $DB->prepare("INSERT INTO `registration` (`event_id`, `event_visitor_type_id`, `accompany_count`, `language_id`, `price`, `coupon_code`, `coupon_modifier`, `final_price`, `payment_method`, `approval_status`, `secret`, `create_time`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); $res->execute([ $event['id'], $params['visitor_type'], !empty($params['accompany']) ? count($params['accompany']) : 0, $language_id_map[ $params['language'] ], floatval($original_price), !empty($coupon['coupon_code']) ? $coupon['coupon_code'] : '', $modifier, $total, $payment_method, !$this->onsite && $event['approval_required'] == 1 ? -1 : 1, $secret, time() ]); $registration_id = $DB->lastInsertId(); if(empty($registration_id)){ throw new \Exception('Failed to register! Please try again later, or contact us!'); } $invoice_id = (!empty($event['invoice_prefix']) ? str_replace('', '', $event['invoice_prefix']) : 'GENERAL'). '_'. $registration_id; if(!empty($coupon_type) && !empty($modifier)){ $res = $DB->prepare("UPDATE `coupon` SET `redeemed` = `redeemed` + 1 WHERE `id` = ? LIMIT 1"); $res->execute([ $coupon['id'] ]); } $duplicated_value_for_search_key = ['`invoice_id` = ?']; $duplicated_value_for_search_value = [$invoice_id]; foreach($params['representative'] AS $question_id => $answer){ $answer_value = is_array($answer) ? ($this->is_multidimensional_array($answer) ? $this->multi_implode("\n", $answer) : (is_numeric(implode('', $answer)) ? implode(' ', $answer) : implode("\n", $answer))) : $answer; $res = $DB->prepare("INSERT INTO `registration_representative` (`registration_id`, `question_id`, `answer`) VALUES (?, ?, ?)"); $res->execute([ $registration_id, $question_id, $answer_value ]); if($question_id < 0){ switch($question_id){ case -8: $duplicated_value_for_search_key[] = '`country` = ?'; break; case -7: $duplicated_value_for_search_key[] = '`job_title` = ?'; break; case -6: $duplicated_value_for_search_key[] = '`salutation` = ?'; break; case -5: $duplicated_value_for_search_key[] = '`first_name` = ?'; break; case -4: $duplicated_value_for_search_key[] = '`last_name` = ?'; break; case -3: $duplicated_value_for_search_key[] = '`company` = ?'; break; case -2: $duplicated_value_for_search_key[] = '`email_address` = ?'; break; case -1: $duplicated_value_for_search_key[] = '`mobile_phone_number` = ?'; break; default: } $duplicated_value_for_search_value[] = $answer_value; } } if(!empty($duplicated_value_for_search_key) && !empty($duplicated_value_for_search_value)){ $res = $DB->prepare("UPDATE `registration` SET ". implode(', ', $duplicated_value_for_search_key). " WHERE `id` = ? LIMIT 1"); $res->execute(array_merge( $duplicated_value_for_search_value, [$registration_id] )); } if( !empty($params['accompany']) ){ foreach($params['accompany'] AS $accompany_id => $accompany){ foreach($accompany AS $question_id => $answer){ $res = $DB->prepare("INSERT INTO `registration_accompany` (`registration_id`, `accompany_id`, `question_id`, `answer`) VALUES (?, ?, ?, ?)"); $res->execute([ $registration_id, $accompany_id + 1, $question_id, is_array($answer) ? implode(' ', $answer) : $answer ]); } } } if(!$this->onsite && $event['approval_required'] == 1){ $payment_method = 'pending'; }elseif($payment_method === 'paypal'){ $paypal_params = [ 'METHOD' => 'SetExpressCheckout', 'VERSION' => '124.0', 'USER' => PAYPAL_USER, 'PWD' => PAYPAL_PWD, 'SIGNATURE' => PAYPAL_SIGNATURE, 'LOCALECODE' => 'en_GB', 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', 'PAYMENTREQUEST_0_CURRENCYCODE' => 'USD', 'L_PAYMENTREQUEST_0_NAME0' => $event['name_'. $params['language']]. ' - '. $visitor_type['name_'. $params['language']], 'L_PAYMENTREQUEST_0_QTY0' => 1 + (!empty($params['accompany']) ? count($params['accompany']) : 0), 'L_PAYMENTREQUEST_0_AMT0' => floatval($visitor_type['price']), 'PAYMENTREQUEST_0_AMT' => $total, 'PAYMENTREQUEST_0_ITEMAMT' => $total, 'PAYMENTREQUEST_0_INVNUM' => $invoice_id, 'RETURNURL' => WWW_URL_ROOT. '#/'. $params['language']. '/'. $params['section']. '/payment/'. ($this->onsite ? '?onsite_secret='. $params['onsite_secret'] : ''), 'CANCELURL' => WWW_URL_ROOT. '#/'. $params['language']. '/'. $params['section']. '/canceled/'. ($this->onsite ? '?onsite_secret='. $params['onsite_secret'] : '') ]; if(!empty($coupon_type)){ $paypal_params['L_PAYMENTREQUEST_0_NAME1'] = 'Coupon Code ('. $coupon['coupon_code']. ')'; $paypal_params['L_PAYMENTREQUEST_0_QTY1'] = 1; $paypal_params['L_PAYMENTREQUEST_0_AMT1'] = $discounted_price - $original_price; } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, PAYPAL_API); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($paypal_params)); $res = curl_exec($ch); curl_close($ch); $paypal_returned_values = array(); parse_str($res, $paypal_returned_values); if(empty($res) || empty($paypal_returned_values) || empty($paypal_returned_values['ACK'])){ throw new \Exception ('Express-checkout failed'); }elseif($paypal_returned_values['ACK'] !== 'Success'){ throw new \Exception ($paypal_returned_values['L_SEVERITYCODE0']. ': '. $paypal_returned_values['L_LONGMESSAGE0']. ' ('. $paypal_returned_values['L_ERRORCODE0']. ')'); } }elseif($total == 0){ $this->send_confirmation_email(array_merge(['registration_id' => $registration_id], $params)); } $success = true; } catch (\Exception $ex) { $success = false; $msg = $ex->getMessage(); } return array( 'success' => $success, 'msg' => !empty($msg) ? $msg : NULL, 'data' => [ 'registration_id' => $registration_id, 'payment_method' => $payment_method, 'url' => !empty($paypal_returned_values['TOKEN']) ? (PAYPAL_CHECKOUT_URL. '?cmd=_express-checkout&token='. $paypal_returned_values['TOKEN']. '&useraction=commit') : NULL, 'barcode' => $this->onsite ? json_encode([ 'id' => $registration_id, 'secret' => $secret ]) : NULL ] ); }