Build modified streamline images

The official image from streamline does not currently work for the arm64
platform. As a temporary measure, the source code and docker build scripts
have been lifted from the official images and are used to build locally.

Some additional modifications are made to reduce overall image size, these
are documented in docker/README.md
This commit is contained in:
2024-03-10 16:17:50 -07:00
parent 2ffc3c408a
commit a424394109
13506 changed files with 1860172 additions and 6 deletions
@@ -0,0 +1,5 @@
vendor/
tests/Fixtures.php
.idea/
/.phpunit.result.cache
.phpunit.cache/
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Africa's Talking
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,246 @@
# Africa's Talking PHP SDK
[![Latest Stable Version](https://img.shields.io/packagist/v/africastalking/africastalking)](https://packagist.org/packages/africastalking/africastalking)
> This SDK provides convenient access to the Africa's Talking API for applications written in PHP.
## Documentation
Take a look at the [API docs here](https://developers.africastalking.com).
## Install
You can install the PHP SDK via composer or by downloading the source
#### Via Composer
The recommended way to install the SDK is with [Composer](http://getcomposer.org/).
```bash
composer require africastalking/africastalking
```
## Usage
The SDK needs to be instantiated using your username and API key, which you can get from the [dashboard](https://account.africastalking.com).
> You can use this SDK for either production or sandbox apps. For sandbox, the app username is **ALWAYS** `sandbox`
```php
use AfricasTalking\SDK\AfricasTalking;
$username = 'YOUR_USERNAME'; // use 'sandbox' for development in the test environment
$apiKey = 'YOUR_API_KEY'; // use your sandbox app API key for development in the test environment
$AT = new AfricasTalking($username, $apiKey);
// Get one of the services
$sms = $AT->sms();
// Use the service
$result = $sms->send([
'to' => '+2XXYYYOOO',
'message' => 'Hello World!'
]);
print_r($result);
```
See [example](example/) for more usage examples.
## Instantiation
Instantiating the class will give you an object with available methods
- `$AT = new AfricasTalking($username, $apiKey)`: Instantiate the class
- Get available service
- [SMS Service](#sms): `$sms = $AT->sms()`
- [Content Service](#content): `$content = $AT->content()`
- [Airtime Service](#airtime): `$airtime = $AT->airtime()`
- [Mobile Data Service](#mobiledata): `$mobileData = $AT->mobileData()`
- [Voice Service](#voice): `$voice = $AT->voice()`
- [Token Service](#token): `$token = $AT->token()`
- [Application Service](#application): `$application = $AT->application()`
### Application
- `fetchApplicationData()`: Get app information. e.g balance
### Airtime
- `send($parameters, $options)`: Send airtime
- **$parameters:** associative array with the following keys:
- `recipients`: An array of arrays containing the following keys
- `phoneNumber`: Recipient of airtime. `REQUIRED`
- `currencyCode`: 3-digit ISO format currency code (e.g `KES`, `USD`, `UGX` etc). `REQUIRED`
- `amount`: Amount to send. `REQUIRED`
- **$options:** optional associative array with the following keys:
- `idempotencyKey`: Key to use when making idempotent requests
- `maxNumRetry`: Maximum number of retries in case of failed airtime deliveries due to telco unavailability or any other reason.
### SMS
- `send($options)`: Send a message
- `message`: SMS content. `REQUIRED`
- `to`: An array of phone numbers. `REQUIRED`
- `from`: Shortcode or alphanumeric ID that is registered with your Africa's Talking account.
- `enqueue`: Set to `true` if you would like to deliver as many messages to the API without waiting for an acknowledgement from telcos.
- `fetchMessages($options)`: Fetch your messages
- `lastReceivedId`: This is the id of the message you last processed. Defaults to `0`
***The followoing methods have been moved to the content service, but, have been maintained on SMS for backwards compatibility:***
- `sendPremium($options)`: Send a premium SMS. Calls `$content->send($options)`
- `createSubscription($options)`: Create a premium subscription. Calls `$content->createSubscription($options)`
- `fetchSubscriptions($options)`: Fetch your premium subscription data. Calls `$content->fetchSubscriptions($options)`
- `deleteSubscription($options)`: Delete a phone number from a premium subscription. Calls `$content->$deleteSubscription($options)`
### Content
- `send($options)`: Send a premium SMS
- `message`: SMS content. `REQUIRED`
- `to`: An array of phone numbers. `REQUIRED`
- `from`: Shortcode that is registered with your Africa's Talking account. `REQUIRED`
- `keyword`: Your premium product keyword
- `linkId`: "[...] We forward the `linkId` to your application when a user sends a message to your onDemand service"
- `retryDurationInHours`: "This specifies the number of hours your subscription message should be retried in case it's not delivered to the subscriber"
- `createSubscription($options)`: Create a premium subscription
- `shortCode`: Premium short code mapped to your account. `REQUIRED`
- `keyword`: Premium keyword under the above short code and is also mapped to your account. `REQUIRED`
- `phoneNumber`: PhoneNumber to be subscribed `REQUIRED`
- `fetchSubscriptions($options)`: Fetch your premium subscription data
- `shortCode`: Premium short code mapped to your account. `REQUIRED`
- `keyword`: Premium keyword under the above short code and mapped to your account. `REQUIRED`
- `lastReceivedId`: ID of the subscription you believe to be your last. Defaults to `0`
- `deleteSubscription($options)`: Delete a phone number from a premium subscription
- `shortCode`: Premium short code mapped to your account. `REQUIRED`
- `keyword`: Premium keyword under the above short code and is also mapped to your account. `REQUIRED`
- `phoneNumber`: PhoneNumber to be subscribed `REQUIRED`
### Mobile Data
- `send($parameters, $options)`: Send mobile data to customers
- **$parameters:** associative array with the following keys:
- `productName`: Payment product on Africa's Talking. `REQUIRED`
- `recipients`: A list of recipients. Each recipient has:
- `phoneNumber`: Customer phone number (in international format). `REQUIRED`
- `quantity`: Mobile data amount. `REQUIRED`
- `unit`: Mobile data unit. Can either be `MB` or `GB`. `REQUIRED`
- `validity`: How long the mobile data is valid for. Must be one of `Day`, `Week` and `Month`. `REQUIRED`
- `metadata`: Additional data to associate with the tranasction. `REQUIRED`
- `isPromoBundle`: This is an optional field that can be either `true` or `false`.
- **$options:** optional associative array with the following keys:
- `idempotencyKey`: Key to use when making idempotent requests
- `findTransaction($parameters)`: Find a particular transaction
- `transactionId`: ID of trancation to find. `REQUIRED`
- `fetchWalletBalance()`: Fetch your payment wallet balance
### Voice
- `call($options)`: Initiate a phone call
- `to`: Phone number that you wish to dial (in international format). `REQUIRED`
- `from`: Phone number on Africa's Talking (in international format). `REQUIRED`
- `clientRequestId`: Variable sent to your Events Callback URL that can be used to tag the call. `OPTIONAL`
- `fetchQueuedCalls($options)`: Fetch queued calls on a phone number
- `phoneNumber`: Phone number mapped to your Africa's Talking account (in international format). `REQUIRED`
- `name`: Fetch calls for a specific queue.
- `uploadMediaFile($options)`: Upload a voice media file
- `phoneNumber`: phone number mapped to your Africa's Talking account (in international format). `REQUIRED`
- `url`: The url of the file to upload. Should start with `http(s)://`. `REQUIRED`
#### MessageBuilder
Build voice xml when callback URL receives a POST from the voice API. Actions can be chained to create an XML string.
```php
$voiceActions = $voice->messageBuilder();
$xmlresponse = $voiceActions
->getDigits($options)
->say($text)
->record()
->build();
```
- `say($text)`: Add a `Say` action
- `text`: Text (in English) that will be read out to the user.
- `play($url)`: Add a `Play` action
- `url`: Public url to an audio file. This file will be played back to user.
- `getDigits($options)`: Add a `GetDigits` action
- `numDigits`: Number of digits should be gotten from the user
- `timeout`: Timeout (in seconds) for getting digits from a user.
- `finishOnKey`: key which will terminate the action of getting digits.
- `callbackUrl`: URL to forward the results of the GetDigits action.
- `dial($options)`: Add a `Dial` action
- `phoneNumbers`: An array of phone numbers (in international format) to call. `REQUIRED`
- `record`: Boolean - Whether to record the conversation.
- `sequenntial`: Boolean - If many numbers provided for `phoneNumbers`, determines whether the phone numbers will be dialed one after the other or at the same time.
- `callerId`: Africa's Talking number you want to dial out with.
- `ringBackTone`: URL location of a media playback you would want the user to listen to when the call has been placed before its picked up.
- `maxDuration`: maximum amount of time in seconds a call should take.
- `conference()`: Add a `Conference` action
- `record($options)`: Add a `Record` action
- `finishOnKey`: Key which will terminate the action of recording.
- `maxLength`: Maximum amount of time in seconds a recording should take.
- `timeout`: Timeout (in seconds) for getting a recording from a user.
- `trimSilence`: Boolean - Specifies whether you want to remove the initial and final parts of a recording where user was silent.
- `playBeep`: Boolean - Specifies whether the API should play a beep when recording starts.
- `callbackUrl`: URL to forward the results of the Recording action.
- `enqueue($options)`: Add an `Enqueue` action
- `holdMusic`: URL to the file to be played while the user is on hold.
- `name`: Name of queue to put call on.
- `deqeue($options)`: Add a `Dequeue` acton
- `phoneNumber`: Phone number mapped to your Africa's Talking account which a user called to join the queue. `REQUIRED`
- `name`: Name of queue you want to dequeue from.
- `reject()`: Add a `Reject` action
- `redirect($url)`: Add a `Redirect` action
- `url`: URL to transfer control of the call to
- `build()`: Build the xml after chaining some of the above actions
### Token
- `generateAuthToken()`: Generate an auth token to use for authenticating API requests instead of your API key.
## Testing the SDK
The SDK uses [PHPUnit](https://phpunit.de/manual/current/en/index.html) as the test runner.
To run available tests, from the root of the project run:
```bash
# Configure needed fixtures, e.g sandbox api key, Africa's Talking products
cp tests/Fixtures.php.tpl tests/Fixtures.php
# Run tests
phpunit --testdox
```
## Issues
If you find a bug, please file an issue on [our issue tracker on GitHub](https://github.com/AfricasTalkingLtd/africastalking-php/issues).
@@ -0,0 +1,28 @@
{
"name": "africastalking/africastalking",
"description": "Official Africa's Talking PHP SDK",
"keywords": ["sms", "voice", "ussd", "text message", "airtime", "api", "africastalking"],
"homepage": "http://github.com/AfricasTalkingLtd/africastalking-php",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Africas's Talking",
"email": "support@africastalking.com",
"homepage": "https://www.africastalking.com"
}
],
"require": {
"php": ">=7.1",
"guzzlehttp/guzzle": "^6.0 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"autoload": {
"psr-4": {
"AfricasTalking\\SDK\\": "src/",
"AfricasTalking\\SDK\\Tests\\": "tests"
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
# Example
**Run**
First, make sure to put your sandbox api key in `index.php`, then:
```bash
$ composer install
$ php -S localhost:9090
$ # open http://localhost:9090
```
@@ -0,0 +1,9 @@
{
"name": "africastalking-example",
"description": "Simple SDK usage",
"require": {
"php": ">=5.3.0",
"africastalking/africastalking": "dev-develop",
"altorouter/altorouter": "1.1.0"
}
}
@@ -0,0 +1,50 @@
<?php
require 'vendor/autoload.php';
use AfricasTalking\SDK\AfricasTalking;
$username = "sandbox";
$apiKey = getenv("API_KEY");
$AT = new AfricasTalking($username, $apiKey);
// Router
$router = new AltoRouter();
$router->map( 'GET', '/', function() {
require __DIR__ . '/views/index.php';
});
$router->map( 'POST', '/auth/register/[*:phone]', function ($phone) {
global $AT;
$sms = $AT->sms();
$response = $sms->send(array(
"to" => $phone,
"from" => "AT2FA",
"message" => "Welcome to Awesome Company",
));
header("Content-Type: application/json; charset=UTF-8");
echo json_encode($response);
});
$router->map( 'POST', '/airtime/[*:phone]', function ($phone) {
global $AT;
$airtime = $AT->airtime();
$response = $airtime->send(array(
"recipients" => array(
array(
"phoneNumber" => $phone,
"amount" => $_GET['amount'],
)
)
));
header("Content-Type: application/json; charset=UTF-8");
echo json_encode($response);
});
$match = $router->match();
if( $match && is_callable( $match['target'] ) ) {
call_user_func_array( $match['target'], $match['params'] );
} else {
header( $_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
}
@@ -0,0 +1,75 @@
/* Space out content a bit */
body {
padding-top: 20px;
padding-bottom: 20px;
}
/* Everything but the jumbotron gets side spacing for mobile first views */
.header,
.marketing,
.footer {
padding-right: 15px;
padding-left: 15px;
}
/* Custom page header */
.header {
padding-bottom: 20px;
border-bottom: 1px solid #e5e5e5;
}
/* Make the masthead heading the same height as the navigation */
.header h3 {
margin-top: 0;
margin-bottom: 0;
line-height: 40px;
}
/* Custom page footer */
.footer {
padding-top: 19px;
color: #777;
}
/* Customize container */
@media (min-width: 768px) {
/*.container {
max-width: 1200px;
}*/
}
.container-narrow > hr {
margin: 30px 0;
}
/* Main marketing message and sign up button */
.jumbotron {
text-align: center;
border-bottom: 1px solid #e5e5e5;
}
/*.jumbotron .btn {
padding: 14px 24px;
font-size: 21px;
}*/
/* Supporting marketing content */
.marketing {
margin: 40px 0;
}
.marketing p + h4 {
margin-top: 28px;
}
/* Responsive: Portrait tablets and up */
@media screen and (min-width: 768px) {
/* Remove the padding we set earlier */
.header,
.marketing,
.footer {
padding-right: 0;
padding-left: 0;
}
/* Space out the masthead */
.header {
margin-bottom: 30px;
}
/* Remove the bottom border on the jumbotron for visual effect */
.jumbotron {
border-bottom: 0;
}
}
pre {
background: white;
}
@@ -0,0 +1,123 @@
"use strict";
$(function () {
function log(message) {
$("#response").text(message);
$('pre span').each(function(i, block) {
hljs.highlightBlock(block);
});
}
$("#signUp").click(() => {
let phone = $("#phone").val();
if (!phone) {
log(JSON.stringify({ error: "Enter a phone number"}, null, 2));
return;
}
log("Sending SMS...");
$.ajax({
type: "POST",
url: `/auth/register/${phone}`,
success: (resp) => {
try {
log(JSON.stringify(resp, null, 2));
} catch (ex) {
log(resp);
}
}
});
});
$("#airtime").click(() => {
const phone = $("#phone").val();
const amount = $("#amount").val();
if (!phone) {
log(JSON.stringify({ error: "Enter a phone number"}, null, 2));
return;
}
if (!amount) {
log(JSON.stringify({ error: "Enter an amount (with currency) e,g, KES 334"}, null, 2));
return;
}
log("Sending Airtime...");
$.ajax({
type: "POST",
url: `/airtime/${phone}?amount=${amount}`,
success: (resp) => {
try {
log(JSON.stringify(resp, null, 2));
} catch (ex) {
log(resp);
}
}
});
});
$("#mobileCheckout").click(() => {
const phone = $("#phone").val();
const amount = $("#mobileCheckoutAmount").val();
if (!phone) {
log(JSON.stringify({ error: "Enter a phone number"}, null, 2));
return;
}
if (!amount) {
log(JSON.stringify({ error: "Enter an amount (with currency) e,g, KES 334"}, null, 2));
return;
}
log("Initiating Mobile Checkout...");
$.ajax({
type: "POST",
url: `/mobile/checkout/${phone}?amount=${amount}`,
success: (resp) => {
try {
log(JSON.stringify(resp, null, 2));
} catch (ex) {
log(resp);
}
}
});
});
$("#mobileB2C").click(() => {
const phone = $("#phone").val();
const amount = $("#mobileB2CAmount").val();
if (!phone) {
log(JSON.stringify({ error: "Enter a phone number"}, null, 2));
return;
}
if (!amount) {
log(JSON.stringify({ error: "Enter an amount (with currency) e,g, KES 334"}, null, 2));
return;
}
log("Initiating Mobile B2C...");
$.ajax({
type: "POST",
url: `/mobile/b2c/${phone}?amount=${amount}`,
success: (resp) => {
try {
log(JSON.stringify(resp, null, 2));
} catch (ex) {
log(resp);
}
}
});
});
});
@@ -0,0 +1,60 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Africa's Talking</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/paper/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/tomorrow.min.css" />
<link href="public/css/style.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="container">
<div class="header clearfix">
<nav>
<ul class="nav nav-pills pull-right">
<li role="presentation" class="active"><a href="#">Home</a></li>
<li role="presentation"><a href="http://docs.africastalking.com" target="_blank">Docs</a></li>
</ul>
</nav>
<h3 class="text-muted">Africa's Talking</h3>
</div>
<br/>
<br/>
<div class="col-md-6">
<pre><span id="response"></span></pre>
</div>
<div class="col-md-6">
<p class="lead">Try clicking some buttons</p>
<div class="col-sm-6 col-offset-sm-3">
<input id="phone" type="text" class="form-control" placeholder="Your phone number">
</div>
<p><a class="btn btn-success btn-sm" role="button" id="signUp">Send SMS</a></p>
<div class="col-sm-6 col-offset-sm-3">
<input id="amount" type="text" class="form-control" placeholder="Amount e.g. USD 35">
</div>
<p><a class="btn btn-success btn-sm" role="button" id="airtime">Airtime</a></p>
<div class="col-sm-6 col-offset-sm-3">
<input id="mobileCheckoutAmount" type="text" class="form-control" placeholder="Amount e.g. KES 4456">
</div>
<p><a class="btn btn-success btn-sm" role="button" id="mobileCheckout">Mobile Checkout</a></p>
<div class="col-sm-6 col-offset-sm-3">
<input id="mobileB2CAmount" type="text" class="form-control" placeholder="Amount e.g. KES 4456">
</div>
<p><a class="btn btn-success btn-sm" role="button" id="mobileB2C">Mobile B2C</a></p>
</div>
</div> <!-- /container -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/json.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/xml.min.js"></script>
<script src="public/js/main.js"></script>
</body>
</html>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="./vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.4/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="AfricasTalking Test Suite">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
@@ -0,0 +1,134 @@
<?php
namespace AfricasTalking\SDK;
use GuzzleHttp\Client;
class AfricasTalking
{
const BASE_DOMAIN = "africastalking.com";
const BASE_SANDBOX_DOMAIN = "sandbox." . self::BASE_DOMAIN;
protected $username;
protected $apiKey;
protected $client;
protected $voiceClient;
protected $tokenClient;
protected $contentClient;
protected $mobileDataClient;
public $baseUrl;
protected $voiceUrl;
protected $contentUrl;
protected $mobileDataUrl;
public function __construct($username, $apiKey)
{
if($username === 'sandbox') {
$this->baseDomain = self::BASE_SANDBOX_DOMAIN;
} else {
$this->baseDomain = self::BASE_DOMAIN;
}
$this->baseUrl = "https://api." . $this->baseDomain . "/version1/";
$this->voiceUrl = "https://voice." . $this->baseDomain . "/";
$this->mobileDataUrl = "https://bundles." . $this->baseDomain . "/";
$this->contentUrl = ($username === "sandbox") ? ($this->baseUrl) : ("https://content." . $this->baseDomain . "/version1/");
$this->checkoutTokenUrl = "https://api." . $this->baseDomain . "/";
if ($username === 'sandbox') {
$this->contentUrl = $this->baseUrl;
}
$this->username = $username;
$this->apiKey = $apiKey;
$this->client = new Client([
'base_uri' => $this->baseUrl,
'headers' => [
'apikey' => $this->apiKey,
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json'
]
]);
$this->contentClient = new Client([
'base_uri' => $this->contentUrl,
'headers' => [
'apikey' => $this->apiKey,
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json'
]
]);
$this->voiceClient = new Client([
'base_uri' => $this->voiceUrl,
'headers' => [
'apikey' => $this->apiKey,
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json'
]
]);
$this->mobileDataClient = new Client([
'base_uri' => $this->mobileDataUrl,
'headers' => [
'apikey' => $this->apiKey,
'Content-Type' => 'application/json',
'Accept' => 'application/json'
]
]);
$this->tokenClient = new Client([
'base_uri' => $this->checkoutTokenUrl,
'headers' => [
'apikey' => $this->apiKey,
'Content-Type' => 'application/json',
'Accept' => 'application/json'
]
]);
}
public function sms()
{
$content = new Content($this->contentClient, $this->username, $this->apiKey);
$sms = new SMS($this->client, $this->username, $this->apiKey, $content);
return $sms;
}
public function content()
{
$content = new Content($this->contentClient, $this->username, $this->apiKey);
return $content;
}
public function airtime()
{
$airtime = new Airtime($this->client, $this->username, $this->apiKey);
return $airtime;
}
public function voice()
{
$voice = new Voice($this->voiceClient, $this->username, $this->apiKey);
return $voice;
}
public function application()
{
$application = new Application($this->client, $this->username, $this->apiKey);
return $application;
}
public function mobileData()
{
$mobileData = new MobileData($this->mobileDataClient, $this->username, $this->apiKey);
return $mobileData;
}
public function token()
{
$token = new Token($this->tokenClient, $this->username, $this->apiKey);
return $token;
}
}
@@ -0,0 +1,62 @@
<?php
namespace AfricasTalking\SDK;
class Airtime extends Service
{
public function send($parameters, $options = [])
{
if(empty($parameters['recipients'])) {
return $this->error("recipients must be specified");
}
if(!is_array($parameters['recipients'])) {
return $this->error("recipients must be an array");
}
foreach ($parameters['recipients'] as $key=>$recipient){
if(!is_array($recipient)) {
return $this->error("every recipient must be an array");
}
if(!array_key_exists('phoneNumber', $recipient) || !array_key_exists('currencyCode', $recipient) || !array_key_exists('amount', $recipient)) {
return $this->error("phoneNumber, currencyCode and amount must be specified for each recipient");
}
$currencyCode = $recipient['currencyCode'];
if (strlen($currencyCode) != 3) {
return $this->error('currencyCode must be in 3-digit ISO format');
}
$recipient['amount'] = $recipient['currencyCode'].' '.$recipient['amount'];
unset($recipient['currencyCode']);
$parameters['recipients'][$key] = $recipient;
}
$data = [
'username' => $this->username,
'recipients' => json_encode($parameters['recipients'])
];
if (isset($options['maxNumRetry']) && is_numeric($options['maxNumRetry']) && $options['maxNumRetry'] > 0) {
$data['maxNumRetry'] = $options['maxNumRetry'];
}
$requestOptions = [
'form_params' => $data,
];
if(isset($options['idempotencyKey'])) {
$requestOptions['headers'] = [
'Idempotency-Key' => $options['idempotencyKey'],
];
}
$response = $this->client->post('airtime/send', $requestOptions);
return $this->success($response);
}
}
@@ -0,0 +1,17 @@
<?php
namespace AfricasTalking\SDK;
class Application extends Service
{
public function doFetchApplication()
{
$response = $this->client->get('user', ['query' => ['username'=> $this->username]]);
return $this->success($response);
}
public function fetchApplicationData()
{
return $this->doFetchApplication();
}
}
@@ -0,0 +1,122 @@
<?php
namespace AfricasTalking\SDK;
class Content extends Service
{
public function send ($options)
{
if (empty($options['to']) || empty($options['message'])) {
return $this->error('recipient and message must be defined');
}
if (!is_array($options['to'])) {
$options['to'] = [$options['to']];
}
$data = [
'username' => $this->username,
'to' => implode(",", $options['to']),
'message' => $options['message']
];
if (array_key_exists('enqueue', $options) && $options['enqueue']) {
$data['enqueue'] = 1;
}
if (empty($options['from'])) {
return [
'status' => 'error',
'data' => 'from is required for premium SMS'
];
} else {
$data['from'] = $options['from'];
}
if (!empty($options['keyword'])) {
$data['keyword'] = $options['keyword'];
}
if (!empty($options['linkId'])) {
$data['linkId'] = $options['linkId'];
}
if (!empty($options['retryDurationInHours'])) {
$data['retryDurationInHours'] = $options['retryDurationInHours'];
}
// turn off bulk sms mode
$data['bulkSMSMode'] = 0;
$response = $this->client->post('messaging', ['form_params' => $data ]);
return $this->success($response);
}
public function createSubscription ($options)
{
if (empty($options['phoneNumber']) ||
empty($options['shortCode']) ||
empty($options['keyword'])) {
return $this->error("phoneNumber, shortCode and keyword must be specified");
}
$data = [
'username' => $this->username,
'phoneNumber' => $options['phoneNumber'],
'shortCode' => $options['shortCode'],
'keyword' => $options['keyword']
];
$response = $this->client->post('subscription/create', ['form_params' => $data ] );
return $this->success($response);
}
public function deleteSubscription ($options)
{
if (empty($options['phoneNumber']) ||
empty($options['shortCode']) ||
empty($options['keyword'])) {
return $this->error("phoneNumber, shortCode and keyword must be specified");
}
$data = [
'username' => $this->username,
'phoneNumber' => $options['phoneNumber'],
'shortCode' => $options['shortCode'],
'keyword' => $options['keyword']
];
$response = $this->client->post('subscription/delete', ['form_params' => $data ] );
return $this->success($response);
}
public function fetchSubscriptions($options)
{
if(empty($options['shortCode']) || empty($options['keyword'])) {
return $this->error("shortCode and keyword must be specified");
}
if (empty($options['lastReceivedId'])) {
$options['lastReceivedId'] = 0;
}
if (!is_numeric($options['lastReceivedId'])) {
return $this->error('lastReceivedId must be an integer');
}
$data = [
'username' => $this->username,
'lastReceivedId' => $options['lastReceivedId'],
'shortCode' => $options['shortCode'],
'keyword' => $options['keyword']
];
$response = $this->client->get('subscription', ['query' => $data ] );
return $this->success($response);
}
}
@@ -0,0 +1,108 @@
<?php
namespace AfricasTalking\SDK;
class MobileData extends Service
{
public function __call($method, $args)
{
// First check if method exists
if (method_exists($this, 'do' . $method)) {
$func = 'do' . $method;
if (!isset($args[0])) {
$args = [ 0 => ''];
}
return $this->$func($args[0]);
} else {
return $this->error($method .' is an invalid Mobile Data SDK Method');
}
}
protected function doSend($parameters, $options = [])
{
// Check if productName is set
if (!isset($parameters['productName'])) {
return $this->error('productName must be defined');
}
$productName = $parameters['productName'];
// Check if recipients array is provided
if (!isset($parameters['recipients'])) {
return $this->error('recipients must be an array containing phoneNumber, unit, quatity, validity and metadata');
} else if (isset($parameters['recipients']) && is_array($parameters['recipients'])) {
$recipients = $parameters['recipients'];
foreach ($recipients as $r) {
if (!isset($r['phoneNumber']) ||
!isset($r['quantity']) ||
!isset($r['unit']) ||
!isset($r['validity']) ||
!isset($r['metadata'])) {
return $this->error('recipients must be an array containing phoneNumber, quantity, unit, validity and metadata');
}
if (isset($r['validity'])) {
if (!in_array($r['validity'], ['Day', 'Month', 'Week'])) {
return $this->error('validity must be one of Day, Week, Month');
}
}
if (isset($r['unit'])) {
if (!in_array($r['unit'], ['MB', 'GB'])) {
return $this->error('unit must be one of MB, GB');
}
}
}
}
// Make request data array
$requestData = [
'username' => $this->username,
'productName' => $productName,
'recipients' => $recipients,
];
if (isset($parameters['isPromoBundles'])) {
$requestData['isPromoBundle'] = $parameters['isPromoBundles'];
}
$requestOptions = [
'json' => $requestData,
];
if(isset($options['idempotencyKey'])) {
$requestOptions['headers'] = [
'Idempotency-Key' => $options['idempotencyKey'],
];
}
$response = $this->client->post('mobile/data/request', $requestOptions);
return $this->success($response);
}
protected function doFindTransaction($options)
{
if (!isset($options['transactionId'])) {
return $this->error('transactionId must be defined');
}
$requestData = [
'username' => $this->username,
'transactionId' => $options['transactionId']
];
$response = $this->client->get('query/transaction/find', ['query' => $requestData]);
return $this->success($response);
}
protected function doFetchWalletBalance()
{
$requestData = [
'username' => $this->username
];
$response = $this->client->get('query/wallet/balance', ['query' => $requestData]);
return $this->success($response);
}
}
@@ -0,0 +1,82 @@
<?php
namespace AfricasTalking\SDK;
class SMS extends Service
{
protected $content;
public function __construct($client, $username, $apiKey, $content)
{
parent::__construct($client, $username, $apiKey);
$this->content = $content;
}
public function send($options)
{
if (empty($options['to']) || empty($options['message'])) {
return $this->error('recipient and message must be defined');
}
if (!is_array($options['to'])) {
$options['to'] = [$options['to']];
}
$data = [
'username' => $this->username,
'to' => implode(",", $options['to']),
'message' => $options['message']
];
if (array_key_exists('enqueue', $options) && $options['enqueue']) {
$data['enqueue'] = 1;
}
if (!empty($options['from'])) {
$data['from'] = $options['from'];
}
$response = $this->client->post('messaging', ['form_params' => $data ]);
return $this->success($response);
}
public function fetchMessages($options = [])
{
if (empty($options['lastReceivedId'])) {
$options['lastReceivedId'] = 0;
}
if (!is_numeric($options['lastReceivedId'])) {
return $this->error('lastReceivedId must be an integer');
}
$data = [
'username' => $this->username,
'lastReceivedId' => $options['lastReceivedId']
];
$response = $this->client->get('messaging', ['query' => $data ] );
return $this->success($response);
}
public function sendPremium($options)
{
return $this->content->send($options);
}
public function createSubscription ($options)
{
return $this->content->createSubscription($options);
}
public function deleteSubscription ($options)
{
return $this->content->deleteSubscription($options);
}
public function fetchSubscriptions($options)
{
return $this->content->fetchSubscriptions($options);
}
}
@@ -0,0 +1,33 @@
<?php
namespace AfricasTalking\SDK;
abstract class Service
{
protected $client;
protected $username;
protected $apiKey;
public function __construct($client, $username, $apiKey)
{
$this->client = $client;
$this->username = $username;
$this->apiKey = $apiKey;
}
protected static function error($data) {
return [
'status' => 'error',
'data' => $data
];
}
protected static function success($data) {
return [
'status' => 'success',
'data' => json_decode($data->getBody()->getContents())
];
}
}
@@ -0,0 +1,13 @@
<?php
namespace AfricasTalking\SDK;
class Token extends Service
{
public function generateAuthToken()
{
$requestData = json_encode(['username' => $this->username]);
$response = $this->client->post('auth-token/generate', ['body' => $requestData ] );
return $this->success($response);
}
}
@@ -0,0 +1,508 @@
<?php
namespace AfricasTalking\SDK;
class Voice extends Service
{
private $xmlString;
public function __call($method, $args)
{
// First check if method exists
if (method_exists($this, 'build' . $method)) {
if (!isset($args[0])) {
$args = [ 0 => ''];
}
return $this->stringBuilder('build'. $method, $args[0]);
} else if (method_exists($this, 'do' . $method)) {
if (!isset($args[0])) {
$args = [ 0 => ''];
}
return $this->apiCall('do' . $method, $args[0]);
} else {
return $this->error($method .' is an invalid Voice SDK Method');
}
}
public function messageBuilder()
{
return $this;
}
private function stringBuilder($method, $args)
{
$result = $this->$method($args);
if (empty($this->xmlString)) {
$this->xmlString = '<?xml version="1.0" encoding="UTF-8"?><Response>'. $result;
} else {
$this->xmlString .= $result;
}
return $this;
}
private function apiCall($method, $args)
{
return $this->$method($args);
}
/**
* Builds XML string from chained voice actions
*
* @return string
*/
public function build()
{
if (empty($this->xmlString)) {
return null;
}
return $this->xmlString . '</Response>';
}
protected function doCall($options)
{
if (!isset($options['to']) || !isset($options['from'])) {
return $this->error('The parameters to and from must be defined');
}
// Validate callTo
$checkCallTo = strpos($options['to'], '+');
if ($checkCallTo === false || $checkCallTo !== 0) {
return $this->error('callTo must be in the format \'+2XXYYYYYYYYY\'');
}
// Validate callFrom
$checkCallFrom = strpos($options['from'], '+');
if ($checkCallFrom === false || $checkCallFrom !== 0) {
return $this->error('callFrom must be in the format \'+2XXYYYYYYYYY\'');
}
$requestData = [
'username' => $this->username,
'to' => $options['to'],
'from' => $options['from']
];
if (isset($options['clientRequestId']) && !empty($options['clientRequestId'])) {
$requestData['clientRequestId'] = (string) $options['clientRequestId'];
}
$response = $this->client->post('call', ['form_params' => $requestData ] );
return $this->success($response);
}
protected function doUploadMediaFile($options)
{
// Check and validate phoneNumber
if (!isset($options['phoneNumber'])) {
return $this->error('Phone number is required and must be in the format \'+2XXYYYYYYYYY\'');
}
$phoneNumber = $options['phoneNumber'];
$checkPhoneNumber = strpos($phoneNumber, '+');
if ($checkPhoneNumber === false || $checkPhoneNumber != 0) {
return $this->error('Phone number must be in the format \'+2XXYYYYYYYYY\'');
}
if (!isset($options['url'])) {
return $this->error('url must be defined');
}
$url = $options['url'];
// Check if valid URL passed
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
return $this->error('URL not valid');
}
$requestData = [
'username' => $this->username,
'phoneNumber' => $phoneNumber,
'url' => $url
];
$response = $this->client->post('mediaUpload', ['form_params' => $requestData]);
return $this->success($response);
}
protected function dofetchQueuedCalls($options)
{
// Check and validate phoneNumber
if (!isset($options['phoneNumber'])) {
return $this->error('Phone number is required and must be in the format \'+2XXYYYYYYYYY\'');
}
$phoneNumber = $options['phoneNumber'];
$checkPhoneNumber = strpos($phoneNumber, '+');
if ($checkPhoneNumber === false || $checkPhoneNumber != 0) {
return $this->error('Phone number must be in the format \'+2XXYYYYYYYYY\'');
}
$requestData = [
'username' => $this->username,
'phoneNumbers' => $phoneNumber
];
if(isset($options['name'])) {
$requestData['name'] = $options['name'];
}
$response = $this->client->post('queueStatus', ['form_params' => $requestData]);
return $this->success($response);
}
protected function buildSay($options)
{
if (is_string($options)) {
return "<Say>$options</Say>";
}
// Check for text
if (!isset($options['text'])) {
return $this->error('Please set text to be read out');
}
$text = $options['text'];
// Check if read out voice has been set
if (isset($options['voice'])) {
$voice = $options['voice'];
}
// Check if playBeep option has been set
if (isset($options['playBeep'])) {
$playBeep = $options['playBeep'];
}
if (isset($options['voice']) && isset($options['playBeep'])) {
$sayString = '<Say voice="' . $voice . '" playBeep="'. $playBeep .'">'. $text .'</Say>';
} else if (isset($options['voice'])) {
$sayString = '<Say voice="' . $voice . '">'. $text .'</Say>';
} else if (isset($options['playBeep'])) {
$sayString = '<Say playBeep="'. $playBeep .'">'. $text .'</Say>';
} else {
$sayString = "<Say>$text</Say>";
}
return $sayString;
}
protected function buildPlay($url)
{
if (!$this->isValidURL($url))
return $this->error('Play URL is not valid');
$playString = '<Play url="'. $url . '"/>';
return $playString;
}
protected function buildGetDigits($options) {
// Check for text
if (!isset($options['text'])) {
return $this->error('Please set text to be read out');
}
$text = $options['text'];
// Check if URL is set
if (isset($options['url'])) {
$url = $options['url'];
}
// Get number of digits
if(isset($options['numDigits'])) {
$numDigits = $options['numDigits'];
if (!is_numeric($numDigits)) {
return $this->error('Please set a number value for the timeout');
}
}
// Get timeout
if(isset($options['timeout'])) {
$timeout = $options['timeout'];
if (!is_numeric($timeout)) {
return $this->error('Please set a number value for the timeout');
}
}
// Get finishOnKey
$finishOnKey = $options['finishOnKey'];
// Get callbackURL
if (isset($options['callBackUrl'])) {
$callBackUrl = $options['callBackUrl'];
if (!$this->isValidURL($callBackUrl)) {
return $this->error('Please set a valid callback URL');
}
}
// -- NOW TO BUILD STRING
// Build opening tag
$getDigitsString = '<GetDigits ';
if (!empty($finishOnKey)) {
$getDigitsString .= ' finishOnKey="'. $finishOnKey .'"';
}
if (!empty($timeout)) {
$getDigitsString .= ' timeout="'. $timeout .'"';
}
if (!empty($numDigits)) {
$getDigitsString .= ' numDigits="'. $numDigits .'"';
}
if (!empty($callBackUrl)) {
$getDigitsString .= ' callBackUrl="'. $callBackUrl .'"';
}
$getDigitsString .= '>';
// ... add child element
if (!empty($text)) {
$getDigitsString .= $this->buildSay($text);
}
if (!empty($url)) {
$getDigitsString .= $this->buildPlay($url);
}
$getDigitsString .= '</GetDigits>';
return $getDigitsString;
}
protected function buildDial($options)
{
// Validate phoneNumber
if (!isset($options['phoneNumbers'])) {
return $this->error('Please specifiy at least one number to dial');
}
$phoneNumbers = implode(",", $options["phoneNumbers"]);
// Check if ringback tone is set
if (isset($options['ringbackTone'])) {
if (!$this->isValidURL($options['ringbackTone'])) {
return $this->error('ringbackTone not a valid URL');
}
$ringbackTone = $options['ringbackTone'];
}
// Check if record is set
if (!isset($options['record']) || !is_bool($options['record'])) {
$record = false;
} else {
$record = $options['record'];
}
// change record to true or false string
$record ? $record = "true" : $record ="false";
// Check if sequential
if (!isset($options['sequential']) || !is_bool($options['sequential'])) {
$sequential = false;
} else {
$sequential = $options['sequential'];
}
// change sequential to true or false string
$sequential ? $sequential = "true" : $sequential ="false";
// Check if callerId is set
if (!isset($options['callerId']) || !($options['callerId'])) {
$callerId = false;
} else {
$callerId = $options['callerId'];
}
// change callerId to number provided or false string
$callerId ? $callerId = $callerId : $callerId ="false";
// Check if maxDuration is set
if(isset($options['maxDuration'])) {
$maxDuration = $options['maxDuration'];
if (!is_integer($maxDuration) || $maxDuration < 0) {
return $this->error('Max duration must be an integer value');
}
}
$dialString = '<Dial phoneNumbers="'. $phoneNumbers . '"';
if (!empty($record)) {
$dialString .= ' record="'. $record .'"';
}
if (!empty($sequential)) {
$dialString .= ' sequential="'. $sequential .'"';
}
if (!empty($callerId)) {
$dialString .= ' callerId="'. $callerId .'"';
}
if (!empty($ringbackTone)) {
$dialString .= ' ringbackTone="'. $ringbackTone .'"';
}
if (!empty($maxDuration)) {
$dialString .= ' maxDuration="'. $maxDuration .'"';
}
$dialString .= ' />';
return $dialString;
}
protected function buildRecord($options)
{
/** Terminal Recording **/
if (empty($options)) {
return '<Record />';
}
/** Partial Recording **/
// Get finishOnKey
$finishOnKey = $options['finishOnKey'];
// Get Max Length
if (isset($options['maxLength'])) {
$maxLength = $options['maxLength'];
if (!is_numeric($maxLength)) {
return $this->error('Please set a number value for the timeout');
}
}
// Get timeout
if (isset($options['timeout'])) {
$timeout = $options['timeout'];
if (!is_numeric($timeout)) {
return $this->error('Please set a number value for the timeout');
}
}
// Check if trimSilence is set
if (!isset($options['trimSilence']) || !is_bool($options['trimSilence'])) {
$trimSilence = false;
} else {
$trimSilence = $options['trimSilence'];
}
// change trimSilence to true or false string
$trimSilence ? $trimSilence = "true" : $trimSilence ="false";
// Check if playBeep option has been set
if (!isset($options['playBeep']) || !is_bool($options['playBeep'])) {
$playBeep = false;
} else {
$playBeep = $options['playBeep'];
}
// change playBeep to true or false string
$playBeep ? $playBeep = "true" : $playBeep ="false";
// Get callbackURL
if (isset($options['callBackUrl'])) {
$callBackUrl = $options['callBackUrl'];
if (!$this->isValidURL($callBackUrl)) {
return $this->error('Please set a valid callback URL');
}
}
// Build opening tag
$recordString = '<Record';
if (!empty($finishOnKey)) {
$recordString .= ' finishOnKey="'. $finishOnKey .'"';
}
if (!empty($maxLength)) {
$recordString .= ' maxLength="'. $maxLength .'"';
}
if (!empty($timeout)) {
$recordString .= ' timeout="'. $timeout .'"';
}
if (!empty($trimSilence)) {
$recordString .= ' trimSilence="'. $trimSilence .'"';
}
if (!empty($playBeep)) {
$playBeep .= ' playBeep="'. $playBeep .'"';
}
if (!empty($callBackUrl)) {
$getDigitsString .= ' callBackUrl="'. $callBackUrl .'"';
}
$recordString .= ' />';
return $recordString;
}
protected function buildEnqueue($options)
{
// Check if holdMusic option has been set
if (isset($options['holdMusic'])) {
$holdMusic = $options['holdMusic'];
if (!$this->isValidURL($holdMusic)) {
return $this->error('Please set a valid URL value for holdMusic');
}
}
// Build opening tag
$enqueueString = '<Enqueue';
if (!empty($holdMusic)) {
$enqueueString .= ' holdMusic="'. $holdMusic .'"';
}
if (isset($options['name'])) {
$name = $options['name'];
$enqueueString .= ' name="'. $name .'"';
}
$enqueueString .= ' />';
return $enqueueString;
}
protected function buildDequeue($options)
{
// Check if holdMusic option has been set
if (!isset($options['phoneNumber'])) {
return $this->error('Please enter a valid phone number');
}
$phoneNumber = $options['phoneNumber'];
// Build opening tag
$dequeueString = '<Dequeue phoneNumber="'. $phoneNumber . '"';
if (isset($options['name'])) {
$name = $options['name'];
$dequeueString .= ' name="'. $name .'"';
}
$dequeueString .= ' />';
return $dequeueString;
}
protected function buildConference()
{
return '<Conference />';
}
protected function buildRedirect($url)
{
return '<Redirect>'. $url.'</Redirect>';
}
protected function buildReject($url)
{
return '<Reject />';
}
private function isValidURL($url)
{
// If no url passed then it can't be valid
if (empty($url)) {
return false;
}
return !filter_var($url, FILTER_VALIDATE_URL) === false;
}
}
@@ -0,0 +1,46 @@
<?php
namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
class AfricasTalkingTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
{
$this->username = Fixtures::$username;
$this->apiKey = Fixtures::$apiKey;
$this->client = new AfricasTalking($this->username, $this->apiKey);
}
public function testSMSClass()
{
$this->assertInstanceOf(\AfricasTalking\SDK\SMS::class, $this->client->sms());
}
public function testContentClass()
{
$this->assertInstanceOf(\AfricasTalking\SDK\Content::class, $this->client->content());
}
public function testAirtimeClass()
{
$this->assertInstanceOf(\AfricasTalking\SDK\Airtime::class, $this->client->airtime());
}
public function testVoiceClass()
{
$this->assertInstanceOf(\AfricasTalking\SDK\Voice::class, $this->client->voice());
}
public function testApplicationClass()
{
$this->assertInstanceOf(\AfricasTalking\SDK\Application::class, $this->client->application());
}
public function testMobileDataClass()
{
$this->assertInstanceOf(\AfricasTalking\SDK\MobileData::class, $this->client->mobileData());
}
}
@@ -0,0 +1,63 @@
<?php
namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
class AirtimeTest extends \PHPUnit\Framework\TestCase
{
public function setup(): void
{
$this->username = Fixtures::$username;
$this->apiKey = Fixtures::$apiKey;
$at = new AfricasTalking($this->username, $this->apiKey);
$this->client = $at->airtime();
}
public function testSendAirtimeToOne()
{
$response = $this->client->send([
'recipients' => [[
'phoneNumber' => Fixtures::$phoneNumber,
'currencyCode' => Fixtures::$currencyCode,
'amount' => Fixtures::$amount
]]
]);
$this->assertObjectHasProperty('responses', $response['data']);
}
public function testSendAirtimeIdempotency()
{
$response = $this->client->send([
'recipients' => [[
'phoneNumber' => Fixtures::$phoneNumber,
'currencyCode' => Fixtures::$currencyCode,
'amount' => Fixtures::$amount
]]
], [
'idempotencyKey' => 'req-' . mt_rand(10, 100),
]);
$this->assertObjectHasProperty('responses', $response['data']);
}
public function testSendAirtimeToMany()
{
$response = $this->client->send([
'recipients' => [[
'phoneNumber' => Fixtures::$phoneNumber,
'currencyCode' => Fixtures::$currencyCode,
'amount' => Fixtures::$amount
], [
'phoneNumber' => '+2347038151149',
'currencyCode' => 'NGN',
'amount' => '10000'
]]
]);
$this->assertObjectHasProperty('responses', $response['data']);
}
}
@@ -0,0 +1,24 @@
<?php
namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
class ApplicationTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
{
$this->username = Fixtures::$username;
$this->apiKey = Fixtures::$apiKey;
$at = new AfricasTalking($this->username, $this->apiKey);
$this->client = $at->application();
}
public function testFetchAplication()
{
$response = $this->client->fetchApplicationData();
$this->assertObjectHasProperty('UserData', $response['data']);
}
}
@@ -0,0 +1,66 @@
<?php
namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
class ContentTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
{
$this->username = Fixtures::$username;
$this->apiKey = Fixtures::$apiKey;
$at = new AfricasTalking($this->username, $this->apiKey);
$this->client = $at->content();
$this->tokenClient = $at->token();
}
public function send()
{
$response = $this->client->send([
'to' => Fixtures::$multiplePhoneNumbersSMS,
'linkId' => 'messageLinkId',
'keyword' => Fixtures::$keyword,
'from' => Fixtures::$shortCode,
'message' => 'Testing Premium...'
]);
$this->assertObjectHasProperty('SMSMessageData', $response['data']);
}
public function testCreateSubscription()
{
$response = $this->client->createSubscription([
'phoneNumber' => Fixtures::$phoneNumber,
'shortCode' => Fixtures::$shortCode,
'keyword' => Fixtures::$keyword,
]);
$this->assertArrayHasKey('status',$response);
$this->assertEquals('success',$response['status']);
}
public function testDeleteSubscription()
{
$response = $this->client->deleteSubscription([
'phoneNumber' => Fixtures::$phoneNumber,
'shortCode' => Fixtures::$shortCode,
'keyword' => Fixtures::$keyword
]);
$this->assertArrayHasKey('status',$response);
$this->assertEquals('success',$response['status']);
}
public function testFetchSubscriptions()
{
$response = $this->client->fetchSubscriptions([
'shortCode' => Fixtures::$shortCode,
'keyword' => Fixtures::$keyword
]);
$this->assertObjectHasProperty('responses', $response['data']);
}
}
@@ -0,0 +1,39 @@
<?php
namespace AfricasTalking\SDK\Tests;
class Fixtures
{
public static $username = 'sandbox';
public static $apiKey = '';
public static $dateOfBirth = '';
public static $accountName = 'Test Bank Account';
public static $accountNumber = '0123456789';
public static $phoneNumber = '+254724486439';
public static $multiplePhoneNumbersSMS = ['+254724486439', '+254724567567', '+254724567569'];
public static $voicePhoneNumber = '+254711082489';
public static $voicePhoneNumber2 = '+254724486439';
public static $narration = 'Test Payment';
public static $shortCode = '';
public static $keyword = 'Test';
public static $alphanumeric = 'Test';
public static $targetProductCode = 1411;
public static $mediaUrl = 'http://thesoundeffect.com/music/mp3/AintTooProudToBeg.mp3';
public static $amount = '60';
public static $currencyCode = 'KES';
public static $currencyCode2 = 'NGN';
public static $productName = 'TestProduct';
public static $provider = 'ATHENA';
public static $transferType = '';
public static $startDate = '2018-01-01';
public static $endDate = '2018-12-30';
public static $transactionId = 'ATPid_SampleTxnId1';
public static $otp = '1234';
public static $MobileDataRecipients = array([
'phoneNumber' => '25471xxxxxxx',
'quantity' => 15,
'unit' => 'MB',
'validity' => 'Day',
'metadata' => ['notes' => 'Data for January 2018']
]);
}
@@ -0,0 +1,43 @@
<?php
namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use AfricasTalking\SDK\MobileData;
use GuzzleHttp\Exception\GuzzleException;
class MobileDataTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
{
$this->username = Fixtures::$username;
$this->apiKey = Fixtures::$apiKey;
$at = new AfricasTalking($this->username, $this->apiKey);
$this->client = $at->mobileData();
}
public function testSend()
{
$response = $this->client->send([
'productName' => Fixtures::$productName,
'recipients' => Fixtures::$MobileDataRecipients,
]);
$this->assertArrayHasKey('status', $response);
}
public function testFindTransaction()
{
$response = $this->client->findTransaction([
'transactionId' => Fixtures::$transactionId
]);
$this->assertEquals('Failure', $response['data']->status);
}
public function testFetchWalletBalance()
{
$response = $this->client->fetchWalletBalance();
$this->assertEquals('Success', $response['data']->status);
}
}
@@ -0,0 +1,135 @@
<?php
namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
class SMSTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
{
$this->username = Fixtures::$username;
$this->apiKey = Fixtures::$apiKey;
$at = new AfricasTalking($this->username, $this->apiKey);
$this->client = $at->sms();
$this->tokenClient = $at->token();
}
public function testSMSWithEmptyMessage()
{
$response = $this->client->send([
'to' => Fixtures::$multiplePhoneNumbersSMS,
]);
$this->assertArrayHasKey('status',$response);
$this->assertEquals('error',$response['status']);
}
public function testSMSWithEmptyRecipient()
{
$response = $this->client->send([
'message' => 'Testing...'
]);
$this->assertArrayHasKey('status',$response);
$this->assertEquals('error',$response['status']);
}
public function testSingleSMSSending()
{
$response = $this->client->send([
'to' => Fixtures::$phoneNumber,
'message' => 'Testing SMS...'
]);
$this->assertObjectHasProperty('SMSMessageData', $response['data']);
}
public function testMultipleSMSSending()
{
$response = $this->client->send([
'to' => Fixtures::$multiplePhoneNumbersSMS,
'message' => 'Testing multiple sending...'
]);
$this->assertObjectHasProperty('SMSMessageData', $response['data']);
}
public function testSMSSendingWithShortcode()
{
$response = $this->client->send([
'to' => Fixtures::$multiplePhoneNumbersSMS,
'message' => 'Testing with short code...',
'from' => Fixtures::$shortCode
]);
$this->assertObjectHasProperty('SMSMessageData', $response['data']);
}
public function testSMSSendingWithAlphanumeric()
{
$response = $this->client->send([
'to' => Fixtures::$multiplePhoneNumbersSMS,
'message' => 'Testing with AlphaNumeric...',
'from' => Fixtures::$alphanumeric
]);
$this->assertObjectHasProperty('SMSMessageData', $response['data']);
}
public function testPremiumSMSSending()
{
$response = $this->client->sendPremium([
'to' => Fixtures::$multiplePhoneNumbersSMS,
'linkId' => 'messageLinkId',
'keyword' => Fixtures::$keyword,
'from' => Fixtures::$shortCode,
'message' => 'Testing Premium...'
]);
$this->assertObjectHasProperty('SMSMessageData', $response['data']);
}
public function testFetchMessages()
{
$response = $this->client->fetchMessages(['lastReceivedId' => '8796']);
$this->assertObjectHasProperty('SMSMessageData', $response['data']);
}
public function testCreateSubscription()
{
$response = $this->client->createSubscription([
'phoneNumber' => Fixtures::$phoneNumber,
'shortCode' => Fixtures::$shortCode,
'keyword' => Fixtures::$keyword,
]);
$this->assertArrayHasKey('status',$response);
$this->assertEquals('success',$response['status']);
}
public function testDeleteSubscription()
{
$response = $this->client->deleteSubscription([
'phoneNumber' => Fixtures::$phoneNumber,
'shortCode' => Fixtures::$shortCode,
'keyword' => Fixtures::$keyword
]);
$this->assertArrayHasKey('status',$response);
$this->assertEquals('success',$response['status']);
}
public function testFetchSubscriptions()
{
$response = $this->client->fetchSubscriptions([
'shortCode' => Fixtures::$shortCode,
'keyword' => Fixtures::$keyword
]);
$this->assertObjectHasProperty('responses', $response['data']);
}
}
@@ -0,0 +1,24 @@
<?php
namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
class TokenTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
{
$this->username = Fixtures::$username;
$this->apiKey = Fixtures::$apiKey;
$at = new AfricasTalking($this->username, $this->apiKey);
$this->client = $at->token();
}
public function testGenerateAuthToken()
{
$response = $this->client->generateAuthToken();
$this->assertEquals(3600, $response['data']->lifetimeInSeconds);
}
}
@@ -0,0 +1,90 @@
<?php
namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
class VoiceTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
{
$this->username = Fixtures::$username;
$this->apiKey = Fixtures::$apiKey;
$at = new AfricasTalking($this->username, $this->apiKey);
$this->client = $at->voice();
}
public function testCall()
{
$response = $this->client->call([
'from' => Fixtures::$voicePhoneNumber,
'to' => Fixtures::$voicePhoneNumber2
]);
$this->assertObjectHasProperty('entries', $response['data']);
}
public function testCallsMustHaveRequiredAttributes()
{
$response = $this->client->call([
'from' => Fixtures::$voicePhoneNumber
]);
$this->assertArrayHasKey('status',$response);
$this->assertEquals('error',$response['status']);
}
public function testFetchQueuedCalls()
{
$response = $this->client->fetchQueuedCalls([
'phoneNumber' => Fixtures::$voicePhoneNumber,
'name' => 'someQueueName'
]);
$this->assertArrayHasKey('status', $response);
}
public function testFetchQueuedCallsMustHaveRequiredAttributes()
{
$response = $this->client->fetchQueuedCalls();
$this->assertArrayHasKey('status',$response);
$this->assertEquals('error',$response['status']);
}
public function testUploadMediaFile()
{
$response = $this->client->uploadMediaFile([
'phoneNumber' => Fixtures::$voicePhoneNumber,
'url' => Fixtures::$mediaUrl
]);
$this->assertArrayHasKey('status', $response);
}
public function testuploadMediaFileMustHaveRequiredAttributes()
{
$response = $this->client->uploadMediaFile([
'url' => 'test@google'
]);
$this->assertArrayHasKey('status',$response);
$this->assertEquals('error',$response['status']);
}
public function testuploadMediaFileCannotBeEmpty()
{
$response = $this->client->uploadMediaFile();
$this->assertArrayHasKey('status',$response);
$this->assertEquals('error',$response['status']);
}
// public function testMessageBuilder()
// {
// // TODO
// }
}