Skip to content

Commit 8e9f239

Browse files
Subaccounts API (#414)
* Hacked IterableAPICollection for subaccounts. I really need the class to handle this, it's violating DRY * TDD, a lot of failing tests here * Big chunk of TDD and implementation, almost done. * Docs, factory implementation * Update src/Subaccount/Filter/Subaccount.php Co-authored-by: Chris Tankersley <chris@ctankersley.com> * Update test/Subaccount/ClientTest.php Co-authored-by: Chris Tankersley <chris@ctankersley.com> * Refactor hydrators to use the stock one * Typo in client factory * Namespace change * more explicit class name * DTO object passed into update * Add secret to DTO * refactor out array payload * split out transfer requests --------- Co-authored-by: Chris Tankersley <chris@ctankersley.com>
1 parent 30eda93 commit 8e9f239

27 files changed

+1968
-33
lines changed

README.md

Lines changed: 170 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ $viberImage = new Vonage\Messages\Channel\Viber\ViberImage(
243243
$client->messages()->send($viberImage);
244244
```
245245

246-
## Verify Examples (v1)
246+
### Verify Examples (v1)
247247

248-
### Starting a Verification
248+
#### Starting a Verification
249249

250250
Vonage's [Verify API][doc_verify] makes it easy to prove that a user has provided their own phone number during signup,
251251
or implement second factor authentication during sign in.
@@ -260,7 +260,7 @@ echo "Started verification with an id of: " . $response->getRequestId();
260260

261261
Once the user inputs the pin code they received, call the `check()` method (see below) with the request ID and the PIN to confirm the PIN is correct.
262262

263-
### Controlling a Verification
263+
#### Controlling a Verification
264264

265265
To cancel an in-progress verification, or to trigger the next attempt to send the confirmation code, you can pass
266266
either an existing verification object to the client library, or simply use a request ID:
@@ -270,7 +270,7 @@ $client->verify()->trigger('00e6c3377e5348cdaf567e1417c707a5');
270270
$client->verify()->cancel('00e6c3377e5348cdaf567e1417c707a5');
271271
```
272272

273-
### Checking a Verification
273+
#### Checking a Verification
274274

275275
In the same way, checking a verification requires the PIN the user provided, and the request ID:
276276

@@ -284,7 +284,7 @@ try {
284284
}
285285
```
286286

287-
### Searching For a Verification
287+
#### Searching For a Verification
288288

289289
You can check the status of a verification, or access the results of past verifications using a request ID.
290290
The verification object will then provide a rich interface:
@@ -298,7 +298,7 @@ foreach($verification->getChecks() as $check){
298298
}
299299
```
300300

301-
### Payment Verification
301+
#### Payment Verification
302302

303303
Vonage's [Verify API][doc_verify] has SCA (Secure Customer Authentication) support, required by the PSD2 (Payment Services Directive) and used by applications that need to get confirmation from customers for payments. It includes the payee and the amount in the message.
304304

@@ -312,9 +312,9 @@ echo "Started verification with an id of: " . $response['request_id'];
312312

313313
Once the user inputs the pin code they received, call the `/check` endpoint with the request ID and the pin to confirm the pin is correct.
314314

315-
## Verify Examples (v2)
315+
### Verify Examples (v2)
316316

317-
### Starting a Verification
317+
#### Starting a Verification
318318

319319
Vonage's Verify v2 relies more on asynchronous workflows via. webhooks, and more customisable Verification
320320
workflows to the developer. To start a verification, you'll need the API client, which is under the namespace
@@ -363,7 +363,7 @@ The base request types are as follows:
363363
For adding workflows, you can see the available valid workflows as constants within the `VerificationWorkflow` object.
364364
For a better developer experience, you can't create an invalid workflow due to the validation that happens on the object.
365365

366-
### Check a submitted code
366+
#### Check a submitted code
367367

368368
To submit a code, you'll need to surround the method in a try/catch due to the nature of the API. If the code is correct,
369369
the method will return a `true` boolean. If it fails, it will throw the relevant Exception from the API that will need to
@@ -378,7 +378,7 @@ try {
378378
}
379379
```
380380

381-
### Webhooks
381+
#### Webhooks
382382

383383
As events happen during a verification workflow, events and updates will fired as webhooks. Incoming server requests that conform to
384384
PSR-7 standards can be hydrated into a webhook value object for nicer interactions. You can also hydrate
@@ -398,7 +398,7 @@ $verificationEvent = \Vonage\Verify2\Webhook\Factory::createFromArray($payload);
398398
var_dump($verificationEvent->getStatus());
399399
```
400400

401-
### Cancelling a request in-flight
401+
#### Cancelling a request in-flight
402402

403403
You can cancel a request should you need to, before the end user has taken any action.
404404

@@ -407,7 +407,7 @@ $requestId = 'c11236f4-00bf-4b89-84ba-88b25df97315';
407407
$client->verify2()->cancel($requestId);
408408
```
409409

410-
### Making a Call
410+
#### Making a Call
411411

412412
All `$client->voice()` methods require the client to be constructed with a `Vonage\Client\Credentials\Keypair`, or a
413413
`Vonage\Client\Credentials\Container` that includes the `Keypair` credentials:
@@ -907,28 +907,166 @@ try {
907907

908908
Check out the [documentation](https://developer.nexmo.com/number-insight/code-snippets/number-insight-advanced-async-callback) for what to expect in the incoming webhook containing the data you requested.
909909

910+
### Subaccount Examples
911+
912+
This API is used to create and configure subaccounts related to your primary account and transfer credit, balances and bought numbers between accounts.
913+
The subaccounts API is disabled by default. If you want to use subaccounts, [contact support](https://api.support.vonage.com) to have the API enabled on your account.
914+
915+
#### Get a list of Subaccounts
916+
917+
```php
918+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
919+
$apiKey = '34kokdf';
920+
$subaccounts = $client->subaccount()->getSubaccounts($apiKey);
921+
var_dump($subaccounts);
922+
```
923+
924+
#### Create a Subaccount
925+
926+
```php
927+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
928+
929+
$apiKey = 'acc6111f';
930+
931+
$payload = [
932+
'name' => 'sub name',
933+
'secret' => 's5r3fds',
934+
'use_primary_account_balance' => false
935+
];
936+
937+
$account = new Account();
938+
$account->fromArray($payload);
939+
940+
$response = $client->subaccount()->createSubaccount($apiKey, $account);
941+
var_dump($response);
942+
```
943+
944+
#### Get a Subaccount
945+
946+
```php
947+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
948+
949+
$apiKey = 'acc6111f';
950+
$subaccountKey = 'bbe6222f';
951+
952+
$response = $client->subaccount()->getSubaccount($apiKey, $subaccountKey);
953+
var_dump($response);
954+
```
955+
956+
#### Update a Subaccount
957+
958+
```php
959+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
960+
961+
$apiKey = 'acc6111f';
962+
$subaccountKey = 'bbe6222f';
963+
964+
$payload = [
965+
'suspended' => true,
966+
'use_primary_account_balance' => false,
967+
'name' => 'Subaccount department B'
968+
];
969+
970+
$account = new Account();
971+
$account->fromArray($payload);
972+
973+
$response = $client->subaccount()->updateSubaccount($apiKey, $subaccountKey, $account)
974+
var_dump($response);
975+
```
976+
977+
#### Get a list of Credit Transfers
978+
979+
```php
980+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
981+
982+
$apiKey = 'acc6111f';
983+
$filter = new Vonage\Subaccount\Filter\Subaccount(['subaccount' => '35wsf5'])
984+
$transfers = $client->subaccount()->getCreditTransfers($apiKey);
985+
var_dump($transfers);
986+
```
987+
988+
#### Transfer Credit between accounts
989+
990+
```php
991+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
992+
993+
$apiKey = 'acc6111f';
994+
995+
$transferRequest = (new TransferCreditRequest($apiKey))
996+
->setFrom('acc6111f')
997+
->setTo('s5r3fds')
998+
->setAmount('123.45')
999+
->setReference('this is a credit transfer');
1000+
1001+
$response = $this->subaccountClient->makeCreditTransfer($transferRequest);
1002+
```
1003+
1004+
#### Get a list of Balance Transfers
1005+
1006+
```php
1007+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
1008+
$apiKey = 'acc6111f';
1009+
1010+
$filter = new \Vonage\Subaccount\Filter\Subaccount(['end_date' => '2022-10-02']);
1011+
$transfers = $client->subaccount()->getBalanceTransfers($apiKey, $filter);
1012+
```
1013+
1014+
#### Transfer Balance between accounts
1015+
1016+
```php
1017+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
1018+
1019+
$apiKey = 'acc6111f';
1020+
1021+
$transferRequest = (new TransferBalanceRequest($apiKey))
1022+
->setFrom('acc6111f')
1023+
->setTo('s5r3fds')
1024+
->setAmount('123.45')
1025+
->setReference('this is a credit transfer');
1026+
1027+
$response = $client->subaccount()->makeBalanceTransfer($transferRequest);
1028+
var_dump($response);
1029+
```
1030+
1031+
#### Transfer a Phone Number between accounts
1032+
1033+
```php
1034+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic(API_KEY, API_SECRET));
1035+
$apiKey = 'acc6111f';
1036+
1037+
$numberTransferRequest = (new NumberTransferRequest($apiKey))
1038+
->setFrom('acc6111f')
1039+
->setTo('s5r3fds')
1040+
->setNumber('4477705478484')
1041+
->setCountry('GB');
1042+
1043+
$response = $client->subaccount()->makeNumberTransfer($numberTransferRequest);
1044+
var_dump($response);
1045+
```
1046+
9101047
## Supported APIs
9111048

912-
| API | API Release Status | Supported?
913-
|------------------------|:--------------------:|:-------------:|
914-
| Account API | General Availability ||
915-
| Alerts API | General Availability ||
916-
| Application API | General Availability ||
917-
| Audit API | Beta ||
918-
| Conversation API | Beta ||
919-
| Dispatch API | Beta ||
920-
| External Accounts API | Beta ||
921-
| Media API | Beta ||
922-
| Messages API | General Availability ||
923-
| Number Insight API | General Availability ||
924-
| Number Management API | General Availability ||
925-
| Pricing API | General Availability ||
926-
| Redact API | General Availability ||
927-
| Reports API | Beta ||
928-
| SMS API | General Availability ||
929-
| Verify API | General Availability ||
930-
| Verify API (Version 2) | Beta ||
931-
| Voice API | General Availability ||
1049+
| API | API Release Status | Supported?
1050+
|------------------------|:--------------------:|:----------:|
1051+
| Account API | General Availability ||
1052+
| Alerts API | General Availability ||
1053+
| Application API | General Availability ||
1054+
| Audit API | Beta ||
1055+
| Conversation API | Beta ||
1056+
| Dispatch API | Beta ||
1057+
| External Accounts API | Beta ||
1058+
| Media API | Beta ||
1059+
| Messages API | General Availability ||
1060+
| Number Insight API | General Availability ||
1061+
| Number Management API | General Availability ||
1062+
| Pricing API | General Availability ||
1063+
| Redact API | General Availability ||
1064+
| Reports API | Beta ||
1065+
| SMS API | General Availability ||
1066+
| Subaccounts API | General Availability ||
1067+
| Verify API | General Availability ||
1068+
| Verify API (Version 2) | Beta ||
1069+
| Voice API | General Availability ||
9321070

9331071
## Troubleshooting
9341072

phpunit.xml.dist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
<testsuite name="sms">
3333
<directory>test/SMS</directory>
3434
</testsuite>
35+
<testsuite name="subaccount">
36+
<directory>test/Subaccount</directory>
37+
</testsuite>
3538
</testsuites>
3639
<php>
3740
<ini name='error_reporting' value='E_ALL' />

src/Client.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
use Vonage\Redact\ClientFactory as RedactClientFactory;
5151
use Vonage\Secrets\ClientFactory as SecretsClientFactory;
5252
use Vonage\SMS\ClientFactory as SMSClientFactory;
53+
use Vonage\Subaccount\ClientFactory as SubaccountClientFactory;
5354
use Vonage\Messages\ClientFactory as MessagesClientFactory;
5455
use Vonage\Verify\ClientFactory as VerifyClientFactory;
5556
use Vonage\Verify2\ClientFactory as Verify2ClientFactory;
@@ -81,6 +82,7 @@
8182
* @method Redact\Client redact()
8283
* @method Secrets\Client secrets()
8384
* @method SMS\Client sms()
85+
* @method Subaccount\Client subaccount()
8486
* @method Verify\Client verify()
8587
* @method Verify2\Client verify2()
8688
* @method Voice\Client voice()
@@ -218,6 +220,7 @@ public function __construct(
218220
'redact' => RedactClientFactory::class,
219221
'secrets' => SecretsClientFactory::class,
220222
'sms' => SMSClientFactory::class,
223+
'subaccount' => SubaccountClientFactory::class,
221224
'verify' => VerifyClientFactory::class,
222225
'verify2' => Verify2ClientFactory::class,
223226
'voice' => VoiceClientFactory::class,
@@ -475,7 +478,7 @@ public function send(RequestInterface $request): ResponseInterface
475478
$response = $this->client->sendRequest($request);
476479

477480
if ($this->debug) {
478-
$id = uniqid();
481+
$id = uniqid('', true);
479482
$request->getBody()->rewind();
480483
$response->getBody()->rewind();
481484
$this->log(

src/Entity/IterableAPICollection.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ class IterableAPICollection implements ClientAwareInterface, Iterator, Countable
8989

9090
protected bool $isHAL = true;
9191

92+
/**
93+
* Used if there are HAL elements, but entities are all in one request
94+
* Specifically removes automatic pagination that adds request parameters
95+
*/
96+
protected bool $noQueryParameters = false;
97+
9298
/**
9399
* User set pgge sixe.
94100
*/
@@ -443,6 +449,8 @@ protected function fetchPage($absoluteUri): void
443449
{
444450
//use filter if no query provided
445451
if (false === strpos($absoluteUri, '?')) {
452+
$originalUri = $absoluteUri;
453+
446454
$query = [];
447455

448456
if (isset($this->size)) {
@@ -458,6 +466,11 @@ protected function fetchPage($absoluteUri): void
458466
}
459467

460468
$absoluteUri .= '?' . http_build_query($query);
469+
470+
// This is an override to completely remove request parameters for single requests
471+
if ($this->getNoQueryParameters()) {
472+
$absoluteUri = $originalUri;
473+
}
461474
}
462475

463476
$requestUri = $absoluteUri;
@@ -554,4 +567,16 @@ public function setNaiveCount(bool $naiveCount): self
554567

555568
return $this;
556569
}
570+
571+
public function setNoQueryParameters(bool $noQueryParameters): self
572+
{
573+
$this->noQueryParameters = $noQueryParameters;
574+
575+
return $this;
576+
}
577+
578+
public function getNoQueryParameters(): bool
579+
{
580+
return $this->noQueryParameters;
581+
}
557582
}

0 commit comments

Comments
 (0)