Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Masked Phone Numbers with PHP and Laravel


This PHP Laravel(link takes you to an external page) sample application is modeled after the amazing rental experience created by AirBnB(link takes you to an external page), but with more Klingons(link takes you to an external page).

Host users can offer rental properties which other guest users can reserve. The guest and the host can then anonymously communicate via a disposable Twilio phone number created just for a reservation. In this tutorial, we'll show you the key bits of code to make this work.

To run this sample app yourself, download the code and follow the instructions on GitHub(link takes you to an external page).

(warning)

Warning

Read how Lyft uses masked phone numbers to let customers securely contact drivers.(link takes you to an external page)


Create a Reservation

create-a-reservation page anchor

The first step in connecting a guest and host is creating a reservation. Here, we handle a form submission for a new reservation which contains the guest's name and phone number.

app/Http/Controllers/ReservationController.php


_234
<?php
_234
namespace App\Http\Controllers;
_234
use Illuminate\Http\Request;
_234
use App\Http\Requests;
_234
use App\Http\Controllers\Controller;
_234
use Illuminate\Contracts\Auth\Authenticatable;
_234
use App\Reservation;
_234
use App\User;
_234
use App\VacationProperty;
_234
use DB;
_234
use Twilio\Rest\Client;
_234
use Twilio\Twiml;
_234
use Log;
_234
_234
class ReservationController extends Controller
_234
{
_234
public function index(Authenticatable $user)
_234
{
_234
$reservations = array();
_234
_234
foreach ($user->propertyReservations as $reservation)
_234
{
_234
array_push($reservations, $reservation->fresh());
_234
}
_234
return view(
_234
'reservation.index',
_234
[
_234
'hostReservations' => $reservations,
_234
'guestReservations' => $user->reservations
_234
]
_234
);
_234
}
_234
_234
/**
_234
* Store a new reservation
_234
*
_234
* @param \Illuminate\Http\Request $request
_234
* @return \Illuminate\Http\Response
_234
*/
_234
public function create(Client $client, Request $request, Authenticatable $user, $id)
_234
{
_234
$this->validate(
_234
$request, [
_234
'message' => 'required|string'
_234
]
_234
);
_234
$property = VacationProperty::find($id);
_234
$reservation = new Reservation($request->all());
_234
_234
$reservation->user()->associate($user);
_234
_234
$property->reservations()->save($reservation);
_234
_234
$this->notifyHost($client, $reservation);
_234
_234
$request->session()->flash(
_234
'status',
_234
"Sending your reservation request now."
_234
);
_234
return redirect()->route('reservation-index', ['id' => $property->id]);
_234
}
_234
_234
public function acceptReject(Client $client, Request $request)
_234
{
_234
$hostNumber = $request->input('From');
_234
$smsInput = strtolower($request->input('Body'));
_234
$host = User::where(DB::raw("CONCAT('+',country_code::text, phone_number::text)"), 'LIKE', "%".$hostNumber."%")
_234
->get()
_234
->first();
_234
_234
$reservation = $host->pendingReservations()->first();
_234
$smsResponse = null;
_234
if (!is_null($reservation))
_234
{
_234
$reservation = $reservation->fresh();
_234
_234
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_234
{
_234
$reservation->confirm($this->getNewTwilioNumber($client, $host));
_234
}
_234
else
_234
{
_234
$reservation->reject();
_234
}
_234
_234
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_234
}
_234
else
_234
{
_234
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_234
}
_234
_234
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectSms(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
$messageBody = $request->input('Body');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectSmsResponse($messageBody, $outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectVoice(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectVoiceResponse($outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
private function getReservationFromNumber($twilioNumber)
_234
{
_234
return Reservation::where('twilio_number', '=', $twilioNumber)->first();
_234
}
_234
_234
private function connectVoiceResponse($outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->play('http://howtodocs.s3.amazonaws.com/howdy-tng.mp3');
_234
$response->dial($outgoingNumber);
_234
_234
return $response;
_234
}
_234
_234
private function connectSmsResponse($messageBody, $outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->message(
_234
$messageBody,
_234
['to' => $outgoingNumber]
_234
);
_234
_234
return $response;
_234
}
_234
_234
private function respond($smsResponse, $reservation)
_234
{
_234
$response = new Twiml();
_234
$response->message($smsResponse);
_234
_234
if (!is_null($reservation))
_234
{
_234
$response->message(
_234
'Your reservation has been ' . $reservation->status . '.',
_234
['to' => $reservation->user->fullNumber()]
_234
);
_234
}
_234
return $response;
_234
}
_234
_234
private function notifyHost($client, $reservation)
_234
{
_234
$host = $reservation->property->user;
_234
_234
$twilioNumber = config('services.twilio')['number'];
_234
_234
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_234
_234
try {
_234
$client->messages->create(
_234
$host->fullNumber(), // Text any number
_234
[
_234
'from' => $twilioNumber, // From a Twilio number in your account
_234
'body' => $messageBody
_234
]
_234
);
_234
} catch (Exception $e) {
_234
Log::error($e->getMessage());
_234
}
_234
}
_234
_234
private function getNewTwilioNumber($client, $host)
_234
{
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'areaCode' => $host->areaCode(),
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
_234
if (empty($numbers)) {
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
}
_234
$twilioNumber = $numbers[0]->phoneNumber;
_234
_234
$newNumber = $client->incomingPhoneNumbers->create(
_234
[
_234
"phoneNumber" => $twilioNumber,
_234
"smsApplicationSid" => config('services.twilio')['applicationSid'],
_234
"voiceApplicationSid" => config('services.twilio')['applicationSid']
_234
]
_234
);
_234
_234
if ($newNumber) {
_234
return $twilioNumber;
_234
} else {
_234
return 0;
_234
}
_234
}
_234
}

Part of our reservation system is receiving reservation requests from potential renters. However, these reservations need to be confirmed. Let's see how we would handle this step.


Before the reservation is finalized, the host needs to confirm that the property is still available. Learn how to automate this process in our first AirTNG tutorial, Workflow Automation(link takes you to an external page).

Once the reservation is confirmed, we need to create a Twilio number that the guest and host can use to communicate in the getNewTwilioNumber method.

Confirm Reservation Method

confirm-reservation-method page anchor

app/Http/Controllers/ReservationController.php


_234
<?php
_234
namespace App\Http\Controllers;
_234
use Illuminate\Http\Request;
_234
use App\Http\Requests;
_234
use App\Http\Controllers\Controller;
_234
use Illuminate\Contracts\Auth\Authenticatable;
_234
use App\Reservation;
_234
use App\User;
_234
use App\VacationProperty;
_234
use DB;
_234
use Twilio\Rest\Client;
_234
use Twilio\Twiml;
_234
use Log;
_234
_234
class ReservationController extends Controller
_234
{
_234
public function index(Authenticatable $user)
_234
{
_234
$reservations = array();
_234
_234
foreach ($user->propertyReservations as $reservation)
_234
{
_234
array_push($reservations, $reservation->fresh());
_234
}
_234
return view(
_234
'reservation.index',
_234
[
_234
'hostReservations' => $reservations,
_234
'guestReservations' => $user->reservations
_234
]
_234
);
_234
}
_234
_234
/**
_234
* Store a new reservation
_234
*
_234
* @param \Illuminate\Http\Request $request
_234
* @return \Illuminate\Http\Response
_234
*/
_234
public function create(Client $client, Request $request, Authenticatable $user, $id)
_234
{
_234
$this->validate(
_234
$request, [
_234
'message' => 'required|string'
_234
]
_234
);
_234
$property = VacationProperty::find($id);
_234
$reservation = new Reservation($request->all());
_234
_234
$reservation->user()->associate($user);
_234
_234
$property->reservations()->save($reservation);
_234
_234
$this->notifyHost($client, $reservation);
_234
_234
$request->session()->flash(
_234
'status',
_234
"Sending your reservation request now."
_234
);
_234
return redirect()->route('reservation-index', ['id' => $property->id]);
_234
}
_234
_234
public function acceptReject(Client $client, Request $request)
_234
{
_234
$hostNumber = $request->input('From');
_234
$smsInput = strtolower($request->input('Body'));
_234
$host = User::where(DB::raw("CONCAT('+',country_code::text, phone_number::text)"), 'LIKE', "%".$hostNumber."%")
_234
->get()
_234
->first();
_234
_234
$reservation = $host->pendingReservations()->first();
_234
$smsResponse = null;
_234
if (!is_null($reservation))
_234
{
_234
$reservation = $reservation->fresh();
_234
_234
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_234
{
_234
$reservation->confirm($this->getNewTwilioNumber($client, $host));
_234
}
_234
else
_234
{
_234
$reservation->reject();
_234
}
_234
_234
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_234
}
_234
else
_234
{
_234
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_234
}
_234
_234
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectSms(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
$messageBody = $request->input('Body');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectSmsResponse($messageBody, $outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectVoice(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectVoiceResponse($outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
private function getReservationFromNumber($twilioNumber)
_234
{
_234
return Reservation::where('twilio_number', '=', $twilioNumber)->first();
_234
}
_234
_234
private function connectVoiceResponse($outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->play('http://howtodocs.s3.amazonaws.com/howdy-tng.mp3');
_234
$response->dial($outgoingNumber);
_234
_234
return $response;
_234
}
_234
_234
private function connectSmsResponse($messageBody, $outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->message(
_234
$messageBody,
_234
['to' => $outgoingNumber]
_234
);
_234
_234
return $response;
_234
}
_234
_234
private function respond($smsResponse, $reservation)
_234
{
_234
$response = new Twiml();
_234
$response->message($smsResponse);
_234
_234
if (!is_null($reservation))
_234
{
_234
$response->message(
_234
'Your reservation has been ' . $reservation->status . '.',
_234
['to' => $reservation->user->fullNumber()]
_234
);
_234
}
_234
return $response;
_234
}
_234
_234
private function notifyHost($client, $reservation)
_234
{
_234
$host = $reservation->property->user;
_234
_234
$twilioNumber = config('services.twilio')['number'];
_234
_234
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_234
_234
try {
_234
$client->messages->create(
_234
$host->fullNumber(), // Text any number
_234
[
_234
'from' => $twilioNumber, // From a Twilio number in your account
_234
'body' => $messageBody
_234
]
_234
);
_234
} catch (Exception $e) {
_234
Log::error($e->getMessage());
_234
}
_234
}
_234
_234
private function getNewTwilioNumber($client, $host)
_234
{
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'areaCode' => $host->areaCode(),
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
_234
if (empty($numbers)) {
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
}
_234
$twilioNumber = $numbers[0]->phoneNumber;
_234
_234
$newNumber = $client->incomingPhoneNumbers->create(
_234
[
_234
"phoneNumber" => $twilioNumber,
_234
"smsApplicationSid" => config('services.twilio')['applicationSid'],
_234
"voiceApplicationSid" => config('services.twilio')['applicationSid']
_234
]
_234
);
_234
_234
if ($newNumber) {
_234
return $twilioNumber;
_234
} else {
_234
return 0;
_234
}
_234
}
_234
}

Once the reservation is confirmed, we need to purchase a Twilio number that the guest and host can use to communicate.


Purchase a Twilio Number

purchase-a-twilio-number page anchor

Here we use a Twilio REST API Client(link takes you to an external page) to search for and buy a new phone number to associate with the reservation. When we buy the number, we designate a Twilio application that will handle webhook(link takes you to an external page) requests; when the new number receives an incoming call or text.

We then save the new phone number on our Reservation model, so when our app receives calls or texts to this number, we'll know which reservation the call or text belongs to.

app/Http/Controllers/ReservationController.php


_234
<?php
_234
namespace App\Http\Controllers;
_234
use Illuminate\Http\Request;
_234
use App\Http\Requests;
_234
use App\Http\Controllers\Controller;
_234
use Illuminate\Contracts\Auth\Authenticatable;
_234
use App\Reservation;
_234
use App\User;
_234
use App\VacationProperty;
_234
use DB;
_234
use Twilio\Rest\Client;
_234
use Twilio\Twiml;
_234
use Log;
_234
_234
class ReservationController extends Controller
_234
{
_234
public function index(Authenticatable $user)
_234
{
_234
$reservations = array();
_234
_234
foreach ($user->propertyReservations as $reservation)
_234
{
_234
array_push($reservations, $reservation->fresh());
_234
}
_234
return view(
_234
'reservation.index',
_234
[
_234
'hostReservations' => $reservations,
_234
'guestReservations' => $user->reservations
_234
]
_234
);
_234
}
_234
_234
/**
_234
* Store a new reservation
_234
*
_234
* @param \Illuminate\Http\Request $request
_234
* @return \Illuminate\Http\Response
_234
*/
_234
public function create(Client $client, Request $request, Authenticatable $user, $id)
_234
{
_234
$this->validate(
_234
$request, [
_234
'message' => 'required|string'
_234
]
_234
);
_234
$property = VacationProperty::find($id);
_234
$reservation = new Reservation($request->all());
_234
_234
$reservation->user()->associate($user);
_234
_234
$property->reservations()->save($reservation);
_234
_234
$this->notifyHost($client, $reservation);
_234
_234
$request->session()->flash(
_234
'status',
_234
"Sending your reservation request now."
_234
);
_234
return redirect()->route('reservation-index', ['id' => $property->id]);
_234
}
_234
_234
public function acceptReject(Client $client, Request $request)
_234
{
_234
$hostNumber = $request->input('From');
_234
$smsInput = strtolower($request->input('Body'));
_234
$host = User::where(DB::raw("CONCAT('+',country_code::text, phone_number::text)"), 'LIKE', "%".$hostNumber."%")
_234
->get()
_234
->first();
_234
_234
$reservation = $host->pendingReservations()->first();
_234
$smsResponse = null;
_234
if (!is_null($reservation))
_234
{
_234
$reservation = $reservation->fresh();
_234
_234
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_234
{
_234
$reservation->confirm($this->getNewTwilioNumber($client, $host));
_234
}
_234
else
_234
{
_234
$reservation->reject();
_234
}
_234
_234
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_234
}
_234
else
_234
{
_234
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_234
}
_234
_234
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectSms(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
$messageBody = $request->input('Body');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectSmsResponse($messageBody, $outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectVoice(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectVoiceResponse($outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
private function getReservationFromNumber($twilioNumber)
_234
{
_234
return Reservation::where('twilio_number', '=', $twilioNumber)->first();
_234
}
_234
_234
private function connectVoiceResponse($outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->play('http://howtodocs.s3.amazonaws.com/howdy-tng.mp3');
_234
$response->dial($outgoingNumber);
_234
_234
return $response;
_234
}
_234
_234
private function connectSmsResponse($messageBody, $outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->message(
_234
$messageBody,
_234
['to' => $outgoingNumber]
_234
);
_234
_234
return $response;
_234
}
_234
_234
private function respond($smsResponse, $reservation)
_234
{
_234
$response = new Twiml();
_234
$response->message($smsResponse);
_234
_234
if (!is_null($reservation))
_234
{
_234
$response->message(
_234
'Your reservation has been ' . $reservation->status . '.',
_234
['to' => $reservation->user->fullNumber()]
_234
);
_234
}
_234
return $response;
_234
}
_234
_234
private function notifyHost($client, $reservation)
_234
{
_234
$host = $reservation->property->user;
_234
_234
$twilioNumber = config('services.twilio')['number'];
_234
_234
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_234
_234
try {
_234
$client->messages->create(
_234
$host->fullNumber(), // Text any number
_234
[
_234
'from' => $twilioNumber, // From a Twilio number in your account
_234
'body' => $messageBody
_234
]
_234
);
_234
} catch (Exception $e) {
_234
Log::error($e->getMessage());
_234
}
_234
}
_234
_234
private function getNewTwilioNumber($client, $host)
_234
{
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'areaCode' => $host->areaCode(),
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
_234
if (empty($numbers)) {
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
}
_234
$twilioNumber = $numbers[0]->phoneNumber;
_234
_234
$newNumber = $client->incomingPhoneNumbers->create(
_234
[
_234
"phoneNumber" => $twilioNumber,
_234
"smsApplicationSid" => config('services.twilio')['applicationSid'],
_234
"voiceApplicationSid" => config('services.twilio')['applicationSid']
_234
]
_234
);
_234
_234
if ($newNumber) {
_234
return $twilioNumber;
_234
} else {
_234
return 0;
_234
}
_234
}
_234
}

Now that each reservation has a Twilio Phone Number, we can see how the application will look up reservations as guest or host calls come in.


When someone sends an SMS or calls one of the Twilio numbers you have configured, Twilio makes a request to the URL you set in the TwiML app. In this request, Twilio includes some useful information including:

  • the From number that originally called or sent an SMS
  • the To Twilio number that triggered this request

Take a look at Twilio's SMS Documentation and Twilio's Voice Documentation for a full list of the parameters you can use.

In our controller we use the To parameter sent by Twilio to find a reservation that has the number we bought stored in it, as this is the number both hosts and guests will call and send sms to.

app/Http/Controllers/ReservationController.php


_234
<?php
_234
namespace App\Http\Controllers;
_234
use Illuminate\Http\Request;
_234
use App\Http\Requests;
_234
use App\Http\Controllers\Controller;
_234
use Illuminate\Contracts\Auth\Authenticatable;
_234
use App\Reservation;
_234
use App\User;
_234
use App\VacationProperty;
_234
use DB;
_234
use Twilio\Rest\Client;
_234
use Twilio\Twiml;
_234
use Log;
_234
_234
class ReservationController extends Controller
_234
{
_234
public function index(Authenticatable $user)
_234
{
_234
$reservations = array();
_234
_234
foreach ($user->propertyReservations as $reservation)
_234
{
_234
array_push($reservations, $reservation->fresh());
_234
}
_234
return view(
_234
'reservation.index',
_234
[
_234
'hostReservations' => $reservations,
_234
'guestReservations' => $user->reservations
_234
]
_234
);
_234
}
_234
_234
/**
_234
* Store a new reservation
_234
*
_234
* @param \Illuminate\Http\Request $request
_234
* @return \Illuminate\Http\Response
_234
*/
_234
public function create(Client $client, Request $request, Authenticatable $user, $id)
_234
{
_234
$this->validate(
_234
$request, [
_234
'message' => 'required|string'
_234
]
_234
);
_234
$property = VacationProperty::find($id);
_234
$reservation = new Reservation($request->all());
_234
_234
$reservation->user()->associate($user);
_234
_234
$property->reservations()->save($reservation);
_234
_234
$this->notifyHost($client, $reservation);
_234
_234
$request->session()->flash(
_234
'status',
_234
"Sending your reservation request now."
_234
);
_234
return redirect()->route('reservation-index', ['id' => $property->id]);
_234
}
_234
_234
public function acceptReject(Client $client, Request $request)
_234
{
_234
$hostNumber = $request->input('From');
_234
$smsInput = strtolower($request->input('Body'));
_234
$host = User::where(DB::raw("CONCAT('+',country_code::text, phone_number::text)"), 'LIKE', "%".$hostNumber."%")
_234
->get()
_234
->first();
_234
_234
$reservation = $host->pendingReservations()->first();
_234
$smsResponse = null;
_234
if (!is_null($reservation))
_234
{
_234
$reservation = $reservation->fresh();
_234
_234
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_234
{
_234
$reservation->confirm($this->getNewTwilioNumber($client, $host));
_234
}
_234
else
_234
{
_234
$reservation->reject();
_234
}
_234
_234
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_234
}
_234
else
_234
{
_234
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_234
}
_234
_234
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectSms(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
$messageBody = $request->input('Body');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectSmsResponse($messageBody, $outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectVoice(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectVoiceResponse($outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
private function getReservationFromNumber($twilioNumber)
_234
{
_234
return Reservation::where('twilio_number', '=', $twilioNumber)->first();
_234
}
_234
_234
private function connectVoiceResponse($outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->play('http://howtodocs.s3.amazonaws.com/howdy-tng.mp3');
_234
$response->dial($outgoingNumber);
_234
_234
return $response;
_234
}
_234
_234
private function connectSmsResponse($messageBody, $outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->message(
_234
$messageBody,
_234
['to' => $outgoingNumber]
_234
);
_234
_234
return $response;
_234
}
_234
_234
private function respond($smsResponse, $reservation)
_234
{
_234
$response = new Twiml();
_234
$response->message($smsResponse);
_234
_234
if (!is_null($reservation))
_234
{
_234
$response->message(
_234
'Your reservation has been ' . $reservation->status . '.',
_234
['to' => $reservation->user->fullNumber()]
_234
);
_234
}
_234
return $response;
_234
}
_234
_234
private function notifyHost($client, $reservation)
_234
{
_234
$host = $reservation->property->user;
_234
_234
$twilioNumber = config('services.twilio')['number'];
_234
_234
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_234
_234
try {
_234
$client->messages->create(
_234
$host->fullNumber(), // Text any number
_234
[
_234
'from' => $twilioNumber, // From a Twilio number in your account
_234
'body' => $messageBody
_234
]
_234
);
_234
} catch (Exception $e) {
_234
Log::error($e->getMessage());
_234
}
_234
}
_234
_234
private function getNewTwilioNumber($client, $host)
_234
{
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'areaCode' => $host->areaCode(),
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
_234
if (empty($numbers)) {
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
}
_234
$twilioNumber = $numbers[0]->phoneNumber;
_234
_234
$newNumber = $client->incomingPhoneNumbers->create(
_234
[
_234
"phoneNumber" => $twilioNumber,
_234
"smsApplicationSid" => config('services.twilio')['applicationSid'],
_234
"voiceApplicationSid" => config('services.twilio')['applicationSid']
_234
]
_234
);
_234
_234
if ($newNumber) {
_234
return $twilioNumber;
_234
} else {
_234
return 0;
_234
}
_234
}
_234
}

Next, let's see how to connect the guest and the host via SMS.


Our Twilio application should be configured to send HTTP requests to this controller method on any incoming text message. Our app responds with TwiML to tell Twilio what to do in response to the message.

If the initial message sent to the anonymous number was sent by the host, we forward it on to the guest. Conversely, if the original message was sent by the guest, we forward it to the host.

app/Http/Controllers/ReservationController.php


_234
<?php
_234
namespace App\Http\Controllers;
_234
use Illuminate\Http\Request;
_234
use App\Http\Requests;
_234
use App\Http\Controllers\Controller;
_234
use Illuminate\Contracts\Auth\Authenticatable;
_234
use App\Reservation;
_234
use App\User;
_234
use App\VacationProperty;
_234
use DB;
_234
use Twilio\Rest\Client;
_234
use Twilio\Twiml;
_234
use Log;
_234
_234
class ReservationController extends Controller
_234
{
_234
public function index(Authenticatable $user)
_234
{
_234
$reservations = array();
_234
_234
foreach ($user->propertyReservations as $reservation)
_234
{
_234
array_push($reservations, $reservation->fresh());
_234
}
_234
return view(
_234
'reservation.index',
_234
[
_234
'hostReservations' => $reservations,
_234
'guestReservations' => $user->reservations
_234
]
_234
);
_234
}
_234
_234
/**
_234
* Store a new reservation
_234
*
_234
* @param \Illuminate\Http\Request $request
_234
* @return \Illuminate\Http\Response
_234
*/
_234
public function create(Client $client, Request $request, Authenticatable $user, $id)
_234
{
_234
$this->validate(
_234
$request, [
_234
'message' => 'required|string'
_234
]
_234
);
_234
$property = VacationProperty::find($id);
_234
$reservation = new Reservation($request->all());
_234
_234
$reservation->user()->associate($user);
_234
_234
$property->reservations()->save($reservation);
_234
_234
$this->notifyHost($client, $reservation);
_234
_234
$request->session()->flash(
_234
'status',
_234
"Sending your reservation request now."
_234
);
_234
return redirect()->route('reservation-index', ['id' => $property->id]);
_234
}
_234
_234
public function acceptReject(Client $client, Request $request)
_234
{
_234
$hostNumber = $request->input('From');
_234
$smsInput = strtolower($request->input('Body'));
_234
$host = User::where(DB::raw("CONCAT('+',country_code::text, phone_number::text)"), 'LIKE', "%".$hostNumber."%")
_234
->get()
_234
->first();
_234
_234
$reservation = $host->pendingReservations()->first();
_234
$smsResponse = null;
_234
if (!is_null($reservation))
_234
{
_234
$reservation = $reservation->fresh();
_234
_234
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_234
{
_234
$reservation->confirm($this->getNewTwilioNumber($client, $host));
_234
}
_234
else
_234
{
_234
$reservation->reject();
_234
}
_234
_234
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_234
}
_234
else
_234
{
_234
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_234
}
_234
_234
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectSms(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
$messageBody = $request->input('Body');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectSmsResponse($messageBody, $outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectVoice(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectVoiceResponse($outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
private function getReservationFromNumber($twilioNumber)
_234
{
_234
return Reservation::where('twilio_number', '=', $twilioNumber)->first();
_234
}
_234
_234
private function connectVoiceResponse($outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->play('http://howtodocs.s3.amazonaws.com/howdy-tng.mp3');
_234
$response->dial($outgoingNumber);
_234
_234
return $response;
_234
}
_234
_234
private function connectSmsResponse($messageBody, $outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->message(
_234
$messageBody,
_234
['to' => $outgoingNumber]
_234
);
_234
_234
return $response;
_234
}
_234
_234
private function respond($smsResponse, $reservation)
_234
{
_234
$response = new Twiml();
_234
$response->message($smsResponse);
_234
_234
if (!is_null($reservation))
_234
{
_234
$response->message(
_234
'Your reservation has been ' . $reservation->status . '.',
_234
['to' => $reservation->user->fullNumber()]
_234
);
_234
}
_234
return $response;
_234
}
_234
_234
private function notifyHost($client, $reservation)
_234
{
_234
$host = $reservation->property->user;
_234
_234
$twilioNumber = config('services.twilio')['number'];
_234
_234
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_234
_234
try {
_234
$client->messages->create(
_234
$host->fullNumber(), // Text any number
_234
[
_234
'from' => $twilioNumber, // From a Twilio number in your account
_234
'body' => $messageBody
_234
]
_234
);
_234
} catch (Exception $e) {
_234
Log::error($e->getMessage());
_234
}
_234
}
_234
_234
private function getNewTwilioNumber($client, $host)
_234
{
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'areaCode' => $host->areaCode(),
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
_234
if (empty($numbers)) {
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
}
_234
$twilioNumber = $numbers[0]->phoneNumber;
_234
_234
$newNumber = $client->incomingPhoneNumbers->create(
_234
[
_234
"phoneNumber" => $twilioNumber,
_234
"smsApplicationSid" => config('services.twilio')['applicationSid'],
_234
"voiceApplicationSid" => config('services.twilio')['applicationSid']
_234
]
_234
);
_234
_234
if ($newNumber) {
_234
return $twilioNumber;
_234
} else {
_234
return 0;
_234
}
_234
}
_234
}

Let's see how to connect the guest and the host via phone call next.


Our Twilio application will send HTTP requests to this method on any incoming voice call. Our app responds with TwiML instructions that tell Twilio to Play an introductory MP3 audio file and thenDial either the guest or host, depending on who initiated the call.

app/Http/Controllers/ReservationController.php


_234
<?php
_234
namespace App\Http\Controllers;
_234
use Illuminate\Http\Request;
_234
use App\Http\Requests;
_234
use App\Http\Controllers\Controller;
_234
use Illuminate\Contracts\Auth\Authenticatable;
_234
use App\Reservation;
_234
use App\User;
_234
use App\VacationProperty;
_234
use DB;
_234
use Twilio\Rest\Client;
_234
use Twilio\Twiml;
_234
use Log;
_234
_234
class ReservationController extends Controller
_234
{
_234
public function index(Authenticatable $user)
_234
{
_234
$reservations = array();
_234
_234
foreach ($user->propertyReservations as $reservation)
_234
{
_234
array_push($reservations, $reservation->fresh());
_234
}
_234
return view(
_234
'reservation.index',
_234
[
_234
'hostReservations' => $reservations,
_234
'guestReservations' => $user->reservations
_234
]
_234
);
_234
}
_234
_234
/**
_234
* Store a new reservation
_234
*
_234
* @param \Illuminate\Http\Request $request
_234
* @return \Illuminate\Http\Response
_234
*/
_234
public function create(Client $client, Request $request, Authenticatable $user, $id)
_234
{
_234
$this->validate(
_234
$request, [
_234
'message' => 'required|string'
_234
]
_234
);
_234
$property = VacationProperty::find($id);
_234
$reservation = new Reservation($request->all());
_234
_234
$reservation->user()->associate($user);
_234
_234
$property->reservations()->save($reservation);
_234
_234
$this->notifyHost($client, $reservation);
_234
_234
$request->session()->flash(
_234
'status',
_234
"Sending your reservation request now."
_234
);
_234
return redirect()->route('reservation-index', ['id' => $property->id]);
_234
}
_234
_234
public function acceptReject(Client $client, Request $request)
_234
{
_234
$hostNumber = $request->input('From');
_234
$smsInput = strtolower($request->input('Body'));
_234
$host = User::where(DB::raw("CONCAT('+',country_code::text, phone_number::text)"), 'LIKE', "%".$hostNumber."%")
_234
->get()
_234
->first();
_234
_234
$reservation = $host->pendingReservations()->first();
_234
$smsResponse = null;
_234
if (!is_null($reservation))
_234
{
_234
$reservation = $reservation->fresh();
_234
_234
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_234
{
_234
$reservation->confirm($this->getNewTwilioNumber($client, $host));
_234
}
_234
else
_234
{
_234
$reservation->reject();
_234
}
_234
_234
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_234
}
_234
else
_234
{
_234
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_234
}
_234
_234
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectSms(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
$messageBody = $request->input('Body');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectSmsResponse($messageBody, $outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
public function connectVoice(Request $request)
_234
{
_234
$twilioNumber = $request->input('To');
_234
$incomingNumber = $request->input('From');
_234
_234
$reservation = $this->getReservationFromNumber($twilioNumber);
_234
$host = $reservation->property->user;
_234
$guest = $reservation->user;
_234
_234
if ($incomingNumber === $host->fullNumber())
_234
{
_234
$outgoingNumber = $guest->fullNumber();
_234
}
_234
else
_234
{
_234
$outgoingNumber = $host->fullNumber();
_234
}
_234
_234
return response($this->connectVoiceResponse($outgoingNumber))->header('Content-Type', 'application/xml');
_234
}
_234
_234
private function getReservationFromNumber($twilioNumber)
_234
{
_234
return Reservation::where('twilio_number', '=', $twilioNumber)->first();
_234
}
_234
_234
private function connectVoiceResponse($outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->play('http://howtodocs.s3.amazonaws.com/howdy-tng.mp3');
_234
$response->dial($outgoingNumber);
_234
_234
return $response;
_234
}
_234
_234
private function connectSmsResponse($messageBody, $outgoingNumber)
_234
{
_234
$response = new Twiml();
_234
$response->message(
_234
$messageBody,
_234
['to' => $outgoingNumber]
_234
);
_234
_234
return $response;
_234
}
_234
_234
private function respond($smsResponse, $reservation)
_234
{
_234
$response = new Twiml();
_234
$response->message($smsResponse);
_234
_234
if (!is_null($reservation))
_234
{
_234
$response->message(
_234
'Your reservation has been ' . $reservation->status . '.',
_234
['to' => $reservation->user->fullNumber()]
_234
);
_234
}
_234
return $response;
_234
}
_234
_234
private function notifyHost($client, $reservation)
_234
{
_234
$host = $reservation->property->user;
_234
_234
$twilioNumber = config('services.twilio')['number'];
_234
_234
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_234
_234
try {
_234
$client->messages->create(
_234
$host->fullNumber(), // Text any number
_234
[
_234
'from' => $twilioNumber, // From a Twilio number in your account
_234
'body' => $messageBody
_234
]
_234
);
_234
} catch (Exception $e) {
_234
Log::error($e->getMessage());
_234
}
_234
}
_234
_234
private function getNewTwilioNumber($client, $host)
_234
{
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'areaCode' => $host->areaCode(),
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
_234
if (empty($numbers)) {
_234
$numbers = $client->availablePhoneNumbers('US')->local->read(
_234
[
_234
'voiceEnabled' => 'true',
_234
"smsEnabled" => 'true'
_234
]
_234
);
_234
}
_234
$twilioNumber = $numbers[0]->phoneNumber;
_234
_234
$newNumber = $client->incomingPhoneNumbers->create(
_234
[
_234
"phoneNumber" => $twilioNumber,
_234
"smsApplicationSid" => config('services.twilio')['applicationSid'],
_234
"voiceApplicationSid" => config('services.twilio')['applicationSid']
_234
]
_234
);
_234
_234
if ($newNumber) {
_234
return $twilioNumber;
_234
} else {
_234
return 0;
_234
}
_234
}
_234
}

That's it! We've just implemented anonymous communications that allow your customers to connect while protecting their privacy.


If you're a PHP developer working with Twilio, you might want to check out these other tutorials:

Click-To-Call

Put a button on your web page that connects visitors to live support or sales people via telephone.

Automated Survey

Instantly collect structured data from your users with a survey conducted over a voice call or SMS text messages.

Did this help?

did-this-help page anchor

Thanks for checking out this tutorial! If you have any feedback to share with us, we'd love to hear it. Tweet @twilio(link takes you to an external page) to let us know what you think.


Rate this page: