Feb 01, 2016
Table of contents:
An important part of building a business online is being able to offer reliable customer support. Customer support is often a differentiating factor when it comes to choosing a service provider in a competitive market.
Customers want to know that if they have issues, contacting you and getting them resolved will be easy and transparent.
There are many ways to offer customer support, from simply an email address, to a fully blown support help-desk and telephone number.
It can also be tempting to roll your own solution for customer support.
However, building your own implementation when there are already suitable products on the market will be a massive waste of resources.
Intercom is a service that makes it very easy to track customer details, offer support, and communicate effectively with your users.
In today’s tutorial we are going to be looking at how to get started with integrating Intercom into your application.
Intercom offer a number of different services and products for communicating and supporting your customers.
However, to keep things relatively simple, I’m going to concentrate on the initial step of your integration with Intercom.
This will be concerned with adding the details of new users and companies to Intercom when they sign up for your application.
Without this first step, Intercom isn’t going to be particularly useful, so it’s something you’re going to have to do at some point.
Whenever a new user or company registers with our application, we are going to need to send that data to Intercom.
To do this, we will raise an event whenever such an action occurs. For example, when a new user signs up, we will raise a UserSignedUp
event.
We will then register a Listener for this event that will push the data to Intercom. This is going to require an HTTP request to the Intercom API, so we can push it onto the queue to be processed in the background.
In order to communicate with Intercom we need to create a service to make the HTTP requests.
During the production
and development
environments we want to be able to send the data to intercom.
But when running the application locally or during the tests, we need to just log that the request to Intercom was made.
We’re going to need two implementations for making requests, one that will make the HTTP request to Intercom and one that will simply log the request.
So because we have to implementations of the same thing, first we need to define an interface:
interface Agent
{
/**
* Create or update user
*
* @param array $data
* @return void
*/
public function createOrUpdateUser(array $data);
/**
* Create or update company
*
* @param array $data
* @return void
*/
public function createOrUpdateCompany(array $data);
/**
* Bulk create or update users
*
* @param array $data
* @return void
*/
public function bulkCreateOrUpdateUsers(array $data);
}
As you can see, we will require three methods, one for creating or updating a user, one for creating or updating a company, and one that will allow us to create or update many users at the same time.
We’re basically working with the constraints of the Intercom API and so this interface basically just mirrors it.
If we were making a generic service that could post to many different types of help desk provider, the API of this interface would be a lot different, but we’ll leave that for another day.
Next I will create the LogAgent
implementation. This implementation will be set as the default so any requests to the support service that are triggered locally or when running tests won’t inadvertently send requests to Intercom.
First I will inject an object that implements the LogInterface
contract:
use Psr\Log\LoggerInterface;
class LogAgent implements Agent
{
/**
* @var LoggerInterface
*/
private $logger;
/**
* @param LoggerInterface $logger
* @return void
*/
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
Next I will implement each of the methods:
/**
* Create or update user
*
* @param array $data
* @return void
*/
public function createOrUpdateUser(array $data)
{
$this->logger->debug('Create or update user:', $data);
}
/**
* Create or update company
*
* @param array $data
* @return void
*/
public function createOrUpdateCompany(array $data)
{
$this->logger->debug('Create or update company:', $data);
}
/**
* Bulk create or update users
*
* @param array $data
* @return void
*/
public function bulkCreateOrUpdateUsers(array $data)
{
$this->logger->debug('Bulk create or update users:', $data);
}
As you can see I’m simply logging the request and it’s data in each method.
Next I can create the Intercom implementation. To make this easier I’m going to be simply wrapping the Intercom PHP SDK.
As I’ve mentioned a few times in previous tutorials, I will usually use the SDK at first to get things working, then I will switch to just using Guzzle to make the HTTP requests.
If you follow this set up it should be really easy to switch to a Guzzle implementation in the future without touching any of your consuming code.
So first I will inject the Intercom SDK:
use Intercom\IntercomBasicAuthClient;
class IntercomAgent implements Agent
{
/**
* @var IntercomBasicAuthClient
*/
private $client;
/**
* @param IntercomBasicAuthClient $client
* @return void
*/
public function __construct(IntercomBasicAuthClient $client)
{
$this->client = $client;
}
}
Next I will implement each of the methods of the Interface:
/**
* Create or update user
*
* @param array $data
* @return void
*/
public function createOrUpdateUser(array $data)
{
$this->client->updateUser($data);
}
/**
* Create or update company
*
* @param array $data
* @return void
*/
public function createOrUpdateCompany(array $data)
{
$this->client->updateCompany($data);
}
/**
* Bulk create or update users
*
* @param array $data
* @return void
*/
public function bulkCreateOrUpdateUsers(array $data)
{
return $this->client->bulkUsers(['items' => $data]);
}
As you can see, we’re just wrapping the Intercom API and so there isn’t a great deal to do. We can simply pass the request to the appropriate method of the Intercom SDK.
Now that we have both implementations created we need to create the Manager class to automatically resolve the correct version based upon the configuration option.
We previously looked at this in How to resolve environment specific implementations from Laravel’s IoC Container.
First we need to create a new AgentManager
class:
use Illuminate\Support\Manager;
class AgentManager extends Manager
{
}
This class should extend Laravel’s Manager
class.
Next we can define a method to get the default implementation from the configuration options:
/**
* Get the default driver
*
* @return string
*/
public function getDefaultDriver()
{
return $this->app['config']['support.default'];
}
We can add a new file called support.php
to the config
directory and then set the default implementation in there:
return [
"default" => env("SUPPORT_AGENT", "log"),
"agents" => [
"intercom" => [
"app_id" => env(
"INTERCOM_APP_ID",
"),
'api_key' => env('INTERCOM_API_KEY', "
),
],
],
];
Finally we can add the two methods to create each implementation:
/**
* Create an instance of the LogAgent
*
* @return LogTransport
*/
protected function createLogDriver()
{
return new LogAgent($this->app->make(LoggerInterface::class));
}
/**
* Create an instance of the IntercomAgent
*
* @return IntercomAgent
*/
protected function createIntercomDriver()
{
return new IntercomAgent(IntercomBasicAuthClient::factory([
'app_id' => config('support.agents.intercom.app_id'),
'api_key' => config('support.agents.intercom.api_key')
]));
}
As you can see, we simply need to instantiate each implementation and return it from the method.
The LogAgent
requires an instance of an object that implements the LogInterface
contract.
And the IntercomAgent
requires the IntercomBasicAuthClient
from the Intercom SDK.
Now that we have everything set up we can do the final bits of Laravel configuration.
First we can create a Facade that will make the service feel at home in a Laravel application:
use Illuminate\Support\Facades\Facade;
class Support extends Facade
{
/**
* Get the registered name of the component
*
* @return string
*/
protected static function getFacadeAccessor()
{
return "support";
}
}
Next we can add a Service Provider to register the service in Laravel’s IoC Container:
class SupportServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @param Dispatcher $dispatcher
* @return void
*/
public function boot(Dispatcher $dispatcher)
{
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->bind(Agent::class, function ($app) {
return (new AgentManager($app))->driver();
});
$this->app->singleton("support", function ($app) {
return $app->make(Agent::class);
});
}
}
Don’t forget to add the Service Provider to your app.php
configuration file under the list of existing Service Providers.
Now that we have everything in place we can start sending data to Intercom.
Whenever a new user registers for your application you can raise a new Event such as:
event(new UserWasCreated($user));
Next you can add a listener that will listen for the event and send the data to Intercom:
class AddUserToIntercom implements ShouldQueue
{
/**
* @var Agent
*/
private $agent;
/**
* @param Agent $agent
* @return void
*/
public function __construct(Agent $agent)
{
$this->agent = $agent;
}
/**
* Handle the event
*
* @param UserWasCreated $event
* @return void
*/
public function handle(UserWasCreated $event)
{
// Send the data via the Agent
}
}
Whenever this event occurs the Listener will be pushed onto the queue and dealt with in the background.
And because we’ve set up the Log implementation, you don’t have to worry about accidentally triggering this service when running your tests or developing locally.
I really like the pattern of developing a “log” implementation and a real implementation for services like this.
Firstly, it makes writing tests really easy. You don’t have to do any crazy mocks to prevent making requests during your tests, you can just let them run as the worst thing that is going to happen is an entry in your log.
Secondly, you don’t have to think about it when developing locally. You’ve probably got enough to think about without having to be worried about rinsing all of your customer data from a third-party service like Intercom.
And thirdly, I think it promotes good practices. If you’ve invested in Intercom, you’re probably not going to just switch to a different platform, but having two implementations ensures you don’t let yourself be lazy by coupling your code to that one implementation.