hasMissingParams($req, [ 'phone_model', 'os_type', 'os_version', 'phone_id', ]); if ($missing) { return new ApiResponse(false, $missing); } // retry until we get a unique id while (true) { try { // instantiate session $sess = new CustomerSession(); $sess->setPhoneModel($req->request->get('phone_model')) ->setOSType($req->request->get('os_type')) ->setOSVersion($req->request->get('os_version')) ->setPhoneID($req->request->get('phone_id')); // reopen in case we get an exception if (!$this->em->isOpen()) { $this->em = $this->em->create( $this->em->getConnection(), $this->em->getConfiguration() ); } // save $this->em->persist($sess); $this->em->flush(); } catch (DBALException $e) { error_log($e->getMessage()); // delay one second and try again sleep(1); continue; } break; } // return data return new ApiResponse(true, '', [ 'session_key' => $sess->getID(), ]); } public function confirmNumber(RisingTideGateway $rt, Request $req, TranslatorInterface $translator) { // validate request $validity = $this->validateRequest($req, [ 'phone_number' ]); if (!$validity['is_valid']) { return new ApiResponse(false, $validity['error']); } // phone number $phone_number = $req->request->get('phone_number'); // get otp_mode from .env $otp_mode = $_ENV['OTP_MODE']; // check for hardcoded phone number for app store testing if ($phone_number == '9991112233' || $phone_number == '9221111111') { $code = '123456'; $this->session->setConfirmCode($code) ->setPhoneNumber($phone_number); $this->em->flush(); return new ApiResponse(); } // check if otp_mode is test if ($otp_mode == 'test') { $code = '123456'; $this->session->setConfirmCode($code) ->setPhoneNumber($phone_number); $this->em->flush(); return new ApiResponse(); } // TODO: spam protection // TODO: validate phone number // generate code and save $code = $this->generateConfirmCode(); $this->session->setConfirmCode($code) ->setPhoneNumber($phone_number); $this->em->flush(); if ($otp_mode != 'test') { // send sms to number $this->sendConfirmationCode($rt, $phone_number, $code, $translator); } // response return new ApiResponse(); } public function validateCode(Request $req) { // validate request $validity = $this->validateRequest($req, [ 'code', ]); if (!$validity['is_valid']) { return new ApiResponse(false, $validity['error']); } // already confirmed if ($this->session->isConfirmed()) { return new ApiResponse(false, 'User is already confirmed.'); } // code is wrong $code = $req->request->get('code'); if ($this->session->getConfirmCode() != $code) { return new ApiResponse(false, 'Wrong confirm code.'); } // set confirm date $date = new DateTime(); $this->session->setDateConfirmed($date) ->setConfirmed(); // figure out if we have customer and customer user already $customer_user = false; // TODO: check if we have the number registered before and merge $dupe_sess = $this->findNumberSession($this->session->getPhoneNumber()); if ($dupe_sess != null) { error_log("Found existing customer session for " . $this->session->getPhoneNumber()); $dupe_cust = $dupe_sess->getCustomer(); $this->session->setCustomer($dupe_cust); // set customer user if it exists $customer_user = $dupe_sess->getCustomerUser() ?? $dupe_cust->getCustomerUser(); } // TODO: check if mobile matches mobile of customer $customer = $this->findCustomerByNumber($this->session->getPhoneNumber()); if ($customer != null) { // TODO: if there is a dupe_sess, do we need to check if // dupe_cust is the same as the customer we found? $this->session->setCustomer($customer); } if (!$customer_user) { error_log("We don't have a customer user for session " . $this->session->getID()); $customer_user = $this->findCustomerUserByNumber($this->session->getPhoneNumber()); if ($customer_user === null) { error_log("Creating a new customer user for " . $this->session->getPhoneNumber()); $customer_user = new CustomerUser(); $customer_user->setCustomer($this->session->getCustomer()) ->setPhoneNumber($this->session->getPhoneNumber()); // save $this->em->persist($customer_user); $this->em->flush(); } else { error_log("Found existing customer user for " . $this->session->getPhoneNumber()); } } error_log("Customer user ID is " . $customer_user->getID()); // set session customer user $this->session->setCustomerUser($customer_user); $this->em->flush(); // response return new ApiResponse(true, '', [ 'api_key' => $customer_user->getApiKey(), 'secret_key'=> $customer_user->getSecretKey(), ]); } public function resendCode(Request $req, RisingTideGateway $rt, TranslatorInterface $translator) { // validate request $validity = $this->validateRequest($req); if (!$validity['is_valid']) { return new ApiResponse(false, $validity['error']); } // already confirmed if ($this->session->isConfirmed()) { return new ApiResponse(false, 'User is already confirmed.'); } // have sent code before if ($this->session->getDateCodeSent() != null) { return new ApiResponse(false, 'Can only send confirm code every 5 mins.'); } // TODO: send via sms $phone_number = $this->session->getPhoneNumber(); $code = $this->session->getConfirmCode(); $this->sendConfirmationCode($rt, $phone_number, $code, $translator); // response return new ApiResponse(); } protected function generateConfirmCode() { return sprintf("%06d", mt_rand(100000, 999999)); } protected function sendConfirmationCode(RisingTideGateway $rt, $phone_number, $code, TranslatorInterface $translator) { // send sms to number $message = $translator->trans('message.confirmation_code') . ' ' . $code; $rt->sendSMS($phone_number, $translator->trans('message.battery_brand_allcaps'), $message); } // TODO: find session customer by phone number protected function findNumberSession($number) { $query = $this->em->getRepository(CustomerSession::class)->createQueryBuilder('s') ->where('s.phone_number = :number') ->andWhere('s.customer is not null') ->andWhere('s.customer_user is not null') ->andWhere('s.confirm_flag = 1') ->setParameter('number', $number) ->setMaxResults(1) ->getQuery(); // we just need one $res = $query->getOneOrNullResult(); return $res; } protected function findCustomerByNumber($number) { $customers = $this->em->getRepository(Customer::class)->findBy(['phone_mobile' => $number]); // find the customer with the most number of cars $car_count = 0; $cust = null; foreach ($customers as $customer) { $vehicles = $customer->getVehicles(); if (count($vehicles) > $car_count) { $car_count = count($vehicles); // "save" customer object $cust = $customer; } } return $cust; } protected function findCustomerUserByNumber($number) { return $this->em->getRepository(CustomerUser::class)->findOneBy(['phone_number' => $number]); } }