twilio / twilio-php

A PHP library for communicating with the Twilio REST API and generating TwiML.
MIT License
1.56k stars 559 forks source link

Users and channels are not getting created when executed inside Paypal IPN #680

Closed AbhijitAQB closed 3 years ago

AbhijitAQB commented 3 years ago

Issue Summary

I implemented Twilio chat service for my CodeIgniter 3 application. I added a new method and called it from Paypal IPN which will create two types of users and a private channel for one-to-one communication.

Strangely, after executing few lines of that method, execution is getting stopped. It's happening when the Paypal IPN is hit for the first time. But next time, when I make a payment and the IPN is hit again, all the code is working properly.

Recently updated to the latest version of twilio-php but the issue remains. Not sure whether I'm doing something wrong. Please suggest a solution for this.

Code Snippet

// Paypal IPN
public function paypal_ipn() {
   ...............[code]...............
   if ($this->makeChatKitRelation($check_relation_exists['company_id'], $check_relation_exists['job_seeker_id'], $check_relation_exists['channel_id'])) {
        // 1. Not entering inside this as (see comment 2 below)
        $update_data = array(
            'number_allowed' => $check_chat_allowed_count->number_allowed + $packageDetails["num_careerseeker_contact_allowed"],
            'already_availed' => $check_chat_allowed_count->already_availed + 1,
        );      
        $this->Company_career_seeker_contact_allowed_model->updateChatAllowedCount($update_data, $check_relation_exists['company_id']);
    }
    die;
}

// Function used to make chat users & channels
public function makeChatKitRelation($companyId, $userId, $channel_id) {
  $companyDetails = $this->Companybasic_model->checkChatKitStatus($companyId); // fetch user details from DB (if user doesn't exists in twilio)
  $userDetails = $this->Jobseekerbasic_model->checkChatKitStatus($userId); // fetch user details from DB (if user doesn't exists in twilio)

  require_once('vendor/autoload.php');

  $twilio = new Twilio\Rest\Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

  if ($companyDetails) { // if user doesn't exist in twilio
      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->users
              ->create('company-' . $companyId);     // 2. Working till this line in the first IPN call

      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
               ->users('company-' . $companyId)
               ->update([
                   'friendlyName' => $companyDetails->company_name
               ]);
  }

  if ($userDetails) { // if user doesn't exist in twilio
      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->users
              ->create('user-' . $userId);

      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
               ->users('user-' . $userId)
               ->update([
                   'friendlyName' => $userDetails->full_name
               ]);
  }

  // Fetch channel or create a new one if it doesn't exist
  try {
      $channel = $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels($channel_id)
              ->fetch();
  } catch (\Twilio\Exceptions\RestException $e) {
      $channel = $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels
              ->create([
                  'createdBy' => 'company-' . $companyId,
                  'uniqueName' => $channel_id,
                  'type' => 'private'
              ]);

      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels($channel_id)
              ->members
              ->create('company-' . $companyId);

      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels($channel_id)
              ->members
              ->create('user-' . $userId);

  }      

  return $channel->sid;
}

Technical details:

AbhijitAQB commented 3 years ago

Hi, I really need some help regarding this issue. Actually client is ready to launch this project soon. Feel free to ask if more information is needed.

JenniferMah commented 3 years ago

Hi @AbhijitAQB Are any errors or other logs that are produced when you reach the line ->create('company-' . $companyId);?

AbhijitAQB commented 3 years ago

I created a new chat service but got this error in application/logs/ directory. ERROR - 2021-03-16 12:51:32 --> Severity: error --> Exception: [HTTP 409] Unable to create record: User already exists /xd1/homes/hash/54/82/a18254/23/97/u149723/ethicalclinicalcareers.com/www/vendor/twilio/sdk/src/Twilio/Version.php 88

image Only this user is created but FriendlyName not updated.

image No channel is created.

shwetha-manvinkurke commented 3 years ago

@AbhijitAQB Few comments on your code:

$twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->users
              ->create('company-' . $companyId);

This code creates a user for your chat service, which will then be assigned a unique SID. Please note that this SID is not the same as your companyId

In your next piece of code

$twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
               ->users('company-' . $companyId)
               ->update([
                   'friendlyName' => $companyDetails->company_name
               ]);

you are querying the users by 'company-' . $companyId. This will not work. It expects the unique SID of the user, not your user defined id.

Same comments for your next few lines of code:

$twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->users
              ->create('user-' . $userId);

$twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
               ->users('user-' . $userId)
               ->update([
                   'friendlyName' => $userDetails->full_name
               ]);
$channel = $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels($channel_id)
              ->fetch();

which beings me to the final comment - this is not in the scope of this ticket, but it's not a good idea to execute required logic in your catch block. I would change that.

AbhijitAQB commented 3 years ago
$user = $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->users
              ->create('company-' . $companyId);

$twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
               ->users($user->sid)
               ->update([
                   'friendlyName' => $companyDetails->company_name
               ]);

@shwetha-manvinkurke sorry, I got engaged in other tasks so replying bit late. I updated my code as you suggested but it's still not working.

Please note this issue is occurring only for Paypal IPN. It's never working when the IPN hits first time but the same code is working for further hits (though sometimes it's not working).

Will it be fixed if I move makeChatKitRelation method from IPN to somewhere else? But then I need to modify the existing flow. Surpriseingly, this issue didn't exist when I implemented Twilio chat last year.

Please suggest a solution!

JenniferMah commented 3 years ago

Hi @AbhijitAQB can you post your updated code samples?

AbhijitAQB commented 3 years ago

Please check the updated code :

// Paypal IPN
public function paypal_ipn() {
   ...............[code]...............
   if ($this->makeChatKitRelation($check_relation_exists['company_id'], $check_relation_exists['job_seeker_id'], $check_relation_exists['channel_id'])) {
        // 1. Not entering inside this as (see comment 2 below)
        $update_data = array(
            'number_allowed' => $check_chat_allowed_count->number_allowed + $packageDetails["num_careerseeker_contact_allowed"],
            'already_availed' => $check_chat_allowed_count->already_availed + 1,
        );      
        $this->Company_career_seeker_contact_allowed_model->updateChatAllowedCount($update_data, $check_relation_exists['company_id']);
    }
    die;
}

// Function used to make chat users & channels
public function makeChatKitRelation($companyId, $userId, $channel_id) {
  $companyDetails = $this->Companybasic_model->checkChatKitStatus($companyId); // fetch user details from DB (if user doesn't exists in twilio)
  $userDetails = $this->Jobseekerbasic_model->checkChatKitStatus($userId); // fetch user details from DB (if user doesn't exists in twilio)

  require_once('vendor/autoload.php');

  $twilio = new Twilio\Rest\Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

  if ($companyDetails) { // if user doesn't exist in twilio
      $user1 = $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->users
              ->create('company-' . $companyId);     // 2. Working till this line in the first IPN call

      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
               ->users($user1->sid)
               ->update([
                   'friendlyName' => $companyDetails->company_name
               ]);
  }

  if ($userDetails) { // if user doesn't exist in twilio
      $user2 = $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->users
              ->create('user-' . $userId);

      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
               ->users($user2->sid)
               ->update([
                   'friendlyName' => $userDetails->full_name
               ]);
  }

  // Fetch channel or create a new one if it doesn't exist
  try {
      $channel = $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels($channel_id)
              ->fetch();
  } catch (\Twilio\Exceptions\RestException $e) {
      $channel = $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels
              ->create([
                  'createdBy' => 'company-' . $companyId,
                  'uniqueName' => $channel_id,
                  'type' => 'private'
              ]);

      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels($channel_id)
              ->members
              ->create('company-' . $companyId);

      $twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
              ->channels($channel_id)
              ->members
              ->create('user-' . $userId);

  }      

  return $channel->sid;
}
shwetha-manvinkurke commented 3 years ago

@AbhijitAQB My guess is the call to update the resource is happening too soon when the resource isn't created just yet. What error are you seeing this time around?

AbhijitAQB commented 3 years ago

@AbhijitAQB My guess is the call to update the resource is happening too soon when the resource isn't created just yet. What error are you seeing this time around?

I'm getting this error in application/logs/ directory. Severity: error --> Exception: [HTTP 409] Unable to create record: User already exists /xd1/homes/hash/54/82/a18254/23/97/u149723/ethicalclinicalcareers.com/www/vendor/twilio/sdk/src/Twilio/Version.php 88

As there is no other error except this in the log file, I think it's generated from the below piece of code.

$twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
               ->users($user1->sid)
               ->update([
                   'friendlyName' => $companyDetails->company_name
               ]);

I think you are right @shwetha-manvinkurke - The call to update the resource is happening too soon when the resource isn't created just yet. So I have to call this asynchronously. In JavaScript we have promises or async/await but I don't how to do this in PHP.

childish-sambino commented 3 years ago

You you create the user with the friendly name in 1 call. May help:

$twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
                  ->users
                  ->create('company-' . $companyId,
                      [
                          'friendlyName' => $companyDetails->company_name
                      ]
                  );
AbhijitAQB commented 3 years ago

You you create the user with the friendly name in 1 call. May help:

$twilio->chat->v2->services(TWILIO_CHAT_SERVICE_SID)
                  ->users
                  ->create('company-' . $companyId,
                      [
                          'friendlyName' => $companyDetails->company_name
                      ]
                  );

Hi, sorry for the late reply. Unfortunately this code is not working either. I'm getting the same error. I think we need to put makeChatKitRelation method somewhere else rather than IPN. Not sure what's the issue!

thinkingserious commented 3 years ago

Hello @AbhijitAQB,

Based on the exception you captured in the logs: Exception: [HTTP 409] Unable to create record: User already exists, it seems you need a try/catch that first checks if the record you are trying to create exists.

With best regards,

Elmer

eshanholtz commented 3 years ago

I'm closing this issue due to an extended period of inactivity. Feel free to reopen this issue and tag one of us if you require further assistance.