When you sign up for services like insurance policies, financing assets like a mortgage, or even something like a mobile phone plan, the company you’re applying for needs to make sure you are a real person. On top of that, it would be valuable for these companies to have a way to verify if a phone number a prospective client gives them is a real and active number that can be reached in the future.
Vonage has a fantastic Phone Number Insights API that can help support these kinds of scenarios. As a developer, you might be asked to build an isolated internal microservice that handles this use case.
In this article, you’ll learn how to use the Vonage Number Insights API and .NET to build a small distributed service that can supply this kind of functionality.
Code
If you wish to skip ahead, you can view the code for this tutorial on GitHub.
Prerequisites
To get started, you’ll need:
An IDE for .NET development like Visual Studio, Visual Studio Code, or JetBrains Rider
The latest version of .NET installed
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
Starting Your .NET Worker Service
In .NET, a worker service is akin to a background process that will typically be subscribed to a message bus/queue or poll some other service. In this tutorial, you’ll build a microservice that accepts messages from a message queue, process them by calling the Vonage API, and then send a response message back to the queue.
Let’s create a .NET worker service by executing the following in a terminal:
dotnet new worker -o FraudService
Next, execute cd ./FraudService
to navigate to the folder the dotnet
command created for you.
You’ll need to install Coravel which will help with scheduling jobs in our worker service:
dotnet add package Coravel
You’ll also need the Vonage C# SDK client installed:
dotnet add package Vonage
Finally, delete the file Worker.cs
from the project as you won’t be needing it.
Building The Main Fraud Detection Logic
The small (micro) service you’ll build will simulate interacting with inputs and outputs via an event bus or queue. Exposing functionality directly via a REST API or another RPC method can lead to issues like chatty services, less resilient systems, etc.
To keep things simple, this tutorial will store messages/events using the file system.
Create a file called MockEventBus.cs
and replace it with the following code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
namespace FraudService
{
public class MockEventBus
{
public class Event
{
public string Data { get; set; }
}
public async Task SendTo(string queueName, Event @event)
{
await UsingStoredEvents(queueName, events =>
{
events.Add(@event);
});
}
public async Task<list<event>> RemoveFrom(string queueName)
{
var next = new List<event>(0);
await UsingStoredEvents(queueName, events =>
{
if (events.Any())
{
next.AddRange(events);
events.Clear();
}
});
return next;
}
private async Task UsingStoredEvents(string queueName, Action<list<event>> action)
{
await using var fileRead = File.Open($"./mock_event_bus_{queueName}.json", FileMode.OpenOrCreate);
var events = fileRead.Length > 0
? await JsonSerializer.DeserializeAsync<list<event>>(fileRead)
: new List<event>();
fileRead.Close();
action(events);
await using var fileWrite = File.Open($"./mock_event_bus_{queueName}.json", FileMode.Truncate);
await JsonSerializer.SerializeAsync(fileWrite, events);
fileWrite.Close();
}
}
}
</event></list<event></list<event></event></list<event>
This class will give us the ability to simulate sending and receiving mock distributed messages. It has two public methods: one to “send” a message to a specific queue — implemented as a file — and another to remove all messages from a given queue/file.
Next, you’ll create a file called Constants.cs
and fill it with the following:
namespace FraudService
{
public class Constants
{
public static readonly string NumbersToValidateQueue = "phone_numbers_to_validate";
public static readonly string ValidatedNumbersQueue = "validated_phone_numbers";
}
}
These are the names of the two queues that we’ll need to send/receive our messages to.
Next, create a file CheckPhoneNumberInvocable.cs
. This file is where the meat of our application will live. The logic for pulling messages off our queue, calling the Vonage Number Insight API, and storing the results will all be encapsulated here.
Replace the contents of CheckPhoneNumberInvocable.cs
with the following:
using System.Threading.Tasks;
using Coravel.Invocable;
using Vonage;
using Vonage.NumberInsights;
namespace FraudService
{
public class CheckPhoneNumberInvocable : IInvocable
{
private readonly MockEventBus _bus;
private readonly VonageClient _client;
public CheckPhoneNumberInvocable(MockEventBus bus, VonageClient client)
{
_bus = bus;
_client = client;
}
public async Task Invoke()
{
var events = await _bus.RemoveFrom(Constants.NumbersToValidateQueue);
foreach (var @event in events)
{
var phoneNumber = @event.Data;
var request = new StandardNumberInsightRequest()
{
Country = "",
Number = phoneNumber
};
var response = await _client.NumberInsightClient.GetNumberInsightStandardAsync(request);
var carrierName = response.CurrentCarrier.Name ?? "Carrier not available";
var country = response.CountryName ?? "Country not available";
await _bus.SendTo(Constants.ValidatedNumbersQueue, new MockEventBus.Event
{
Data = $"{phoneNumber}: {country}: {carrierName}"
});
}
}
}
}
In this fake scenario, other services may want to know if a given phone number has a legit carrier and verify what country the number is registered in. Having this information could help to make decisions around fraudulent accounts, customers, etc.
Let's have a closer look at the code! It pulls messages/events from a queue where each message is a phone number to be verified. The results sent by the Numbers Insight API for that number are then stored in another queue.
This is common in distributed systems: a microservice will publish its final results to an asynchronous message queue. Other systems can subscribe to this queue and receive these results and do whatever they need to do.
This approach allows each service to be autonomous and resilient. For example, if a particular service goes down, it can begin processing messages placed in the queue while it was down once it's back up. With the typical REST/RPC approach, when a service is down, it cannot receive messages or requests at all - making the asynchronous message-driven approach more resilient.
Getting back to the code, you’ll need to configure and glue the different pieces of this application together. Replace Program.cs
with the following:
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Coravel;
using Microsoft.Extensions.DependencyInjection;
using Vonage;
using Vonage.Request;
namespace FraudService
{
public class Program
{
public static async Task Main(string[] args)
{
var provider = CreateHostBuilder(args).Build();
provider.Services.UseScheduler(scheduler =>
{
scheduler
.Schedule<checkphonenumberinvocable>()
.EveryTenSeconds()
.PreventOverlapping(nameof(CheckPhoneNumberInvocable));
});
await ConfigureTestPhoneNumbers(provider);
provider.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
var configuration = Credentials.FromApiKeyAndSecret(
"your_api_key",
"your_api_secret"
);
var vonageClient = new VonageClient(configuration);
services.AddSingleton(vonageClient);
services.AddTransient<mockeventbus>();
services.AddTransient<checkphonenumberinvocable>();
services.AddScheduler();
});
private static async Task ConfigureTestPhoneNumbers(IHost provider)
{
var bus = provider.Services.GetRequiredService<mockeventbus>();
await bus.SendTo(Constants.NumbersToValidateQueue, new MockEventBus.Event
{
Data = "15555555555" // Add your phone number here!
});
}
}
}
</mockeventbus></checkphonenumberinvocable></mockeventbus></checkphonenumberinvocable>
This tutorial uses Coravel to configure an easy way to run our CheckPhoneNumberInvocable
class once every ten seconds. It simulates polling or subscribing to a message queue, as you’ll see in a minute.
Testing Your Microservice
After you’ve replaced your API key and secret in Program.cs
, try running the application by executing the following in a terminal:
dotnet run
As the program runs, you’ll see two new .json
files generated by the application. You can check mock_event_bus_validated_phone_numbers.json
to see the messages for all validated phone numbers.
In Program.cs
, there’s a method called ConfigureTestPhoneNumbers()
where you can hard-code some phone numbers to test immediately upon application startup. However, as the application is running, you can also modify mock_event_bus_phone_numbers_to_validate.json
and add new numbers on the fly. Doing so can stimulate new messages coming in from a queue.
You might replace the contents of mock_event_bus_phone_numbers_to_validate.json
with the following:
[ { "Data": "19055555555" } ]
After a few seconds, take a look at mock_event_bus_validated_phone_numbers.json
to see the results of whatever phone number you chose to verify!
Conclusion
You’ve successfully created a .NET microservice that can verify phone numbers and send that information back to multiple consumers/services. Each consumer can make their own decision about how they want to use the results. You also understand how using an event-driven approach can keep the various parts of your system decoupled and autonomous.
The Numbers Insight’s API has many other fantastic abilities. While this tutorial used the “Standard” Numbers Insights API, there is a more advanced feature where you can get a bunch more information about a given phone number. Try it out sometime!
James is a Microsoft MVP with a background in fintech & insurance industries building web and mobile applications. He's the author of Refactoring TypeScript and the creator of some open-source tools for .NET called Coravel. He lives in eastern Canada and is a 10-minute drive from the highest tides in the world!