Send an SMS using Nexmo API

In order to protect the Nexmo API credentials the calls to the API are done server side, but wanted to use a web page to create the parameters to pass to the API. (There is a simple PIN code that has to be entered and match - its not very sophisticated but there is only a small balance on the Nexmo account so there is little to lose and still early days to implement authentication).

Follows the usual PHP pattern that the file is an HTML file with bits of php code inserted where required for logic purposes or to insert the contents of a variable. The form on the page submits to the same php script:

<form method="post" class="gridform" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>"></form>
1

Inside the form is a div where Vue is bound. The project started out without Vue and I did not take out the php echo into the input value, but am pretty sure this is not needed since later in the script I echo the variable into the data object of the vue instance.

<input v-model="telephone" type="text" name="dest" value="<?php echo $dest;?>">

1
2
data: {
    messageText: '<?php echo $message;?>',
    messageLength: '',
    fromText: '<?php echo $from;?>',
    truncatedFrom: '',
    telephone: '<?php echo $dest;?>',
    answer: ''
},
1
2
3
4
5
6
7
8

The logic of the PHP script is simple enough. Define empty variables for the form items and error messages. If nothing has been submitted then the form is empty as expected. If the form has been posted then validate the values submitted and if they are ok attempt to send the message via Nexmo. Feedback result.

For example

if (empty($_POST["dest"])) {
    $destErr = "Destination Telephone number is required.";
    $cansend = 0;
} else {
    $dest = test_input($_POST["dest"]);
    // check if 0 to 9 in any order at least 8 digits long but no more than 16
    if (!preg_match("/^[0-9]{7,15}$/",$dest)) {
      $destErr = "Only numbers allowed, no spaces, at least 8 digits long";
      $cansend = 0;
    }
}
1
2
3
4
5
6
7
8
9
10
11

The error message can then be displayed on the form input element later (if there was an error):

<span class="error"><?php echo $destErr;?></span>
1

Having installed the Nexmo PHP helper library in the directory on the web server using composer, sending the message is simple.

  if ($cansend){
    require_once __DIR__ . '/vendor/autoload.php';
    $basic  = new \Nexmo\Client\Credentials\Basic('xxx','xxxxxx');
    $client = new \Nexmo\Client($basic);
    $msg = $client->message()->send([
        'to' => $dest,
        'from' => $from,
        'text' => $message
    ]);
    $responseData = $msg->getResponseData();
    $result = '<p>Message Status: ' . $responseData['messages'][0]['status'] . '</p>';
    $result .= '<p>Message Price: ' . $responseData['messages'][0]['message-price'] . '</p>';
    $result .= '<p>Remaining Balance: ' . $responseData['messages'][0]['remaining-balance'] . '</p>';
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

The documentation has been updated since I implemented this.

The use of Vue in this project was minimal really. There is the data binding that is described above - the input values are bound to reactive data variables in the Vue instance.

This allows watchers to be set, for example as the message text is typed the watcher calculates the length of the message in characters and updates the messageLength property of the data object which is bound to a span below the input field on the form (so much better than JQuery days and simpler than vanilla JS).

There is a single method in the Vue instance which is not leveraging anything about Vue as far as I can tell other than using the data binding again. A button on the form send an Ajax request to a different PHP script on the server. It was not possible to implement this directly from Axios on the web page from the client because the page is using https and all calls must therefore be https, but the free version of this API does not support https. The method passes the telephone number to the simple PHP script which then does the http call to the API.

<?php

$phone_number = htmlspecialchars($_GET["t"]);
// set API Access Key
$access_key = 'xxxxxx';

// Initialize CURL:
$ch = curl_init('http://apilayer.net/api/validate?access_key='.$access_key.'&number='.$phone_number.'');  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Store the data:
$json = curl_exec($ch);
curl_close($ch);
echo($json);

?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

The json returned by the API is delivered to the method as an object by Axios and so it is easy to parse. I got caught out by the catch property of Axios as it catches not just errors in the ajax request but also in the parsing of the response data object, hence the last else here to report an error from the API itself.













 








methods: {
    checkNumber: function(event) {
        var vm = this;
        axios.get('ping.php?t=' + this.telephone)
            .then(function (response){
                if (response.data.valid == true) {
                    vm.answer = 'valid ' + response.data.line_type;
                }
                else if (response.data.valid == false) {
                    vm.answer = 'invalid number';
                }
                else {
                    vm.answer = response.data.error.info;
                }
            })
            .catch(function (error){
                vm.answer = 'Something went wrong';
        })
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

The layout was taken from a codepen by Rachel Andrew and adapted a little bit. The body uses grid to centre the form and uses a media query to apply a grid to the divs inside the form to change the layout on wider screens. There is a flex fallback for browsers which don't support grid.