Compare commits

...

3 Commits

Author SHA1 Message Date
Claude
d41b6aa535 Reuse existing Twilio credentials for mobile voice tokens
All checks were successful
Create Release / build (push) Successful in 3s
Mobile voice token endpoint now uses TWP_Twilio_API::generate_capability_token()
instead of separate API Key SID/Secret. Removes duplicate Twilio credential
fields from Mobile App settings page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 14:50:53 -08:00
Claude
4da794ed0c Remove Twilio Push Credential references and add real Firebase config
All checks were successful
Create Release / build (push) Successful in 4s
All FCM push notifications are handled by WordPress backend (TWP_FCM),
so Twilio Push Credentials are unnecessary. Also replaces placeholder
google-services.json with real Firebase project config (twp-softphone).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 14:43:21 -08:00
Claude
5adfa694c1 Migrate FCM from legacy v1 API to HTTP v2 with service account auth
All checks were successful
Create Release / build (push) Successful in 3s
Replace deprecated FCM server key authentication with Google service
account OAuth2 flow. The class now creates a signed JWT from the
service account credentials, exchanges it for a short-lived access
token (cached via WordPress transients), and sends messages to the
FCM v2 endpoint (projects/{id}/messages:send).

Settings page updated: FCM Server Key field replaced with Firebase
Project ID + Service Account JSON textarea with validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 14:04:33 -08:00
5 changed files with 262 additions and 180 deletions

View File

@@ -6,8 +6,26 @@
- **URL**: `https://phone.cloud-hosting.io/`
- **Deployment**: rsync to Docker (remote server only, not local)
- **SDK**: Twilio PHP SDK v8.7.0
- **PHP**: 8.0+ required
- **Optional**: AWS SDK (`aws/aws-sdk-php`) for SNS SMS provider
- **External SDK**: `wp-content/twilio-sdk/` (survives plugin updates)
## Commands
- **Install SDK (recommended)**: `./install-twilio-sdk-external.sh` (installs to `wp-content/twilio-sdk/`)
- **Install SDK (internal)**: `./install-twilio-sdk.sh` (installs to `vendor/`, lost on plugin update)
- **Test SDK**: `php test-sdk.php`
- **Composer install SDK**: `composer install-sdk`
- **Deploy**: rsync to Docker (remote server, see production path above)
- **CI/CD**: Gitea workflows in `.gitea/workflows/``release.yml`, `update-version.yml`
## Directory Structure
- `twilio-wp-plugin.php` — Main plugin file, constants, SDK loading
- `includes/` — All backend classes (28 class files)
- `admin/` — Admin UI class (`TWP_Admin`), mobile app settings page
- `assets/js/` — Browser phone JS, service worker
- `assets/images/`, `assets/sounds/` — Static assets
- `.gitea/workflows/` — CI/CD (release, version update)
## Phone Variable Names
**Use**: `incoming_number`, `agent_number`, `customer_number`, `workflow_number`, `queue_number`, `default_number`
**Don't use**: `from_number`, `to_number`, `phone_number`, `$agent_phone`
@@ -18,11 +36,20 @@
- **TWP_Admin**: Has `find_customer_call_leg()` - CRITICAL for call control
- **TWP_TTS_Helper**: ElevenLabs/Alice fallback, 30-day cache
- **TWP_User_Queue_Manager**: Auto-creates queues/extensions (100-9999)
- **TWP_Webhooks**: 26 endpoints at `twilio-webhook/v1`
- **TWP_Activator**: Creates 15 DB tables, run `ensure_tables_exist()` if missing
- **TWP_Webhooks**: 35 endpoints at `twilio-webhook/v1`
- **TWP_Activator**: Creates 16 DB tables, run `ensure_tables_exist()` if missing
- **TWP_Core**: Main plugin orchestrator, hooks all classes together
- **TWP_SMS_Manager**: SMS abstraction with provider interface
- **TWP_SMS_Provider_Twilio** / **TWP_SMS_Provider_SNS**: SMS providers (Twilio default, AWS SNS optional)
- **TWP_Mobile_API**: REST API for mobile app
- **TWP_Mobile_Auth** / **TWP_Mobile_SSE** / **TWP_FCM**: Mobile auth, server-sent events, push notifications
- **TWP_Call_Queue**: Queue operations and management
- **TWP_Callback_Manager**: Callback request handling
- **TWP_Workflow**: Workflow step execution engine
- **TWP_Auto_Updater**: Plugin auto-update from Gitea releases
## Database
15 tables with `twp_` prefix. Key notes:
16 tables with `twp_` prefix. Key notes:
- `twp_call_queues`: User queues (general/personal/hold)
- `twp_agent_status`: Has `auto_busy_at` for 1-min auto-revert
- `twp_queued_calls`: Uses `enqueued_at` not `joined_at`
@@ -41,32 +68,6 @@ $api->update_call($customer_call_sid, ['twiml' => $twiml_xml]);
- Queue: Pass `waitUrl` as option in `enqueue()`
- TwiML: Use SDK classes, not raw XML
## Recent Changes (v2.3.0)
- Browser phone moved to admin-only
- Call control uses `find_customer_call_leg()` to prevent disconnections
- Auto-creates user queues/extensions when needed
- Firefox support added
- 1-min agent status auto-revert
## SDK Installation
- **External SDK (Recommended)**: Use `install-twilio-sdk-external.sh` to install SDK to `wp-content/twilio-sdk/`
- Survives WordPress plugin updates
- SDK location defined by `TWP_EXTERNAL_SDK_DIR` constant
- Loading priority: External first, then internal `vendor/` fallback
- **Internal SDK (Alternative)**: Use `install-twilio-sdk.sh` to install to `vendor/`
- Will be deleted when WordPress updates the plugin
- Requires reinstallation after each plugin update
- **SDK Loading**: Plugin checks external location first via autoloader, falls back to internal
- **Post-Update Detection**: Hook on `upgrader_process_complete` checks SDK status and shows warning
## Browser Phone Configuration
- **Edge Location Setting**: Configurable via Settings → Twilio Edge Location
- Default: `roaming` (auto-select closest edge)
- Options: ashburn, umatilla, dublin, frankfurt, singapore, sydney, tokyo, sao-paulo
- Stored in: `twp_twilio_edge` option
- Used by: Browser phone JavaScript for WebRTC connection
- Critical: Wrong edge causes immediate call failures (e.g., US calls with Sydney edge)
## Development Notes
- **API**: E.164 format (+1XXXXXXXXXX)
- **Database**: Use `$wpdb`, prepared statements
@@ -79,30 +80,24 @@ $api->update_call($customer_call_sid, ['twiml' => $twiml_xml]);
- User-specific queues with extensions
- Browser phone at `admin.php?page=twilio-wp-browser-phone`
- ElevenLabs TTS with Alice fallback
- 68 AJAX actions, 26 REST endpoints
- 77 AJAX actions, 35 REST endpoints
- Browser phone moved to admin-only (v2.3.0)
- Firefox, Chrome, Safari, Edge support
- 1-min agent status auto-revert
## Recent Technical Changes (v2.8.9)
## SDK Loading
- **External SDK (Recommended)**: `wp-content/twilio-sdk/` — survives plugin updates
- **Internal SDK**: `vendor/` — deleted on plugin update, needs reinstall
- Loading priority: External first (`TWP_EXTERNAL_SDK_DIR`), then internal fallback
- Post-update hook (`upgrader_process_complete`) warns if SDK missing
### SDK Persistence Between Plugin Updates
- **Problem**: WordPress plugin updates delete entire plugin folder including `vendor/` SDK
- **Solution**: External SDK installation at `wp-content/twilio-sdk/` survives updates
- **Implementation**:
- New constant: `TWP_EXTERNAL_SDK_DIR` points to `wp-content/twilio-sdk/`
- Loading priority in `twp_check_sdk_installation()`: External first, internal fallback
- Classes updated: `TWP_Twilio_API`, `TWP_Webhooks` constructors check external location first
- New script: `install-twilio-sdk-external.sh` automates external installation
- Post-update hook: `twp_check_sdk_after_update()` detects missing SDK after updates
- Admin notices: `twp_sdk_missing_notice()` shows both installation options
- Warning system: `twp_show_sdk_update_warning()` via transient after plugin updates
## Browser Phone Configuration
- **Edge Location**: `twp_twilio_edge` option, default `roaming`
- Options: roaming, ashburn, umatilla, dublin, frankfurt, singapore, sydney, tokyo, sao-paulo
- Wrong edge causes immediate call failures (e.g., US calls with Sydney edge)
### US Calls Failing Fix (Browser Phone)
- **Problem**: Browser phone had hardcoded `edge: 'sydney'`, causing US calls to fail with immediate HANGUP
- **Solution**: Configurable edge location via WordPress settings
- **Implementation**:
- New setting: `twp_twilio_edge` with default value `roaming`
- Settings UI: Dropdown in admin settings with 8 edge options
- Browser phone JS: Uses `get_option('twp_twilio_edge', 'roaming')` instead of hardcoded value
- Edge options: roaming, ashburn, umatilla, dublin, frankfurt, singapore, sydney, tokyo, sao-paulo
## Changelog
See `README.md` for detailed version history. Current version: v2.8.9.
---
*Updated: Jan 2026*
*Updated: Mar 2026*

View File

@@ -36,25 +36,33 @@ if (isset($_POST['twp_test_notification']) && check_admin_referer('twp_mobile_se
// Save settings
if (isset($_POST['twp_save_mobile_settings']) && check_admin_referer('twp_mobile_settings')) {
update_option('twp_fcm_server_key', sanitize_text_field($_POST['twp_fcm_server_key']));
update_option('twp_fcm_project_id', sanitize_text_field($_POST['twp_fcm_project_id']));
// Service account JSON — validate it parses as JSON before saving
$sa_json_raw = isset($_POST['twp_fcm_service_account_json']) ? wp_unslash($_POST['twp_fcm_service_account_json']) : '';
if (!empty($sa_json_raw)) {
$sa_parsed = json_decode($sa_json_raw, true);
if ($sa_parsed && isset($sa_parsed['client_email'], $sa_parsed['private_key'])) {
update_option('twp_fcm_service_account_json', $sa_json_raw);
} else {
$sa_json_error = 'Invalid service account JSON — must contain client_email and private_key fields.';
}
} else {
update_option('twp_fcm_service_account_json', '');
}
update_option('twp_auto_update_enabled', isset($_POST['twp_auto_update_enabled']) ? '1' : '0');
update_option('twp_gitea_repo', sanitize_text_field($_POST['twp_gitea_repo']));
update_option('twp_gitea_token', sanitize_text_field($_POST['twp_gitea_token']));
update_option('twp_twilio_api_key_sid', sanitize_text_field($_POST['twp_twilio_api_key_sid']));
update_option('twp_twilio_api_key_secret', sanitize_text_field($_POST['twp_twilio_api_key_secret']));
update_option('twp_fcm_push_credential_sid', sanitize_text_field($_POST['twp_fcm_push_credential_sid']));
$settings_saved = true;
}
// Get current settings
$fcm_server_key = get_option('twp_fcm_server_key', '');
$fcm_project_id = get_option('twp_fcm_project_id', '');
$fcm_service_account_json = get_option('twp_fcm_service_account_json', '');
$fcm_sa_configured = !empty($fcm_service_account_json) && !empty($fcm_project_id);
$auto_update_enabled = get_option('twp_auto_update_enabled', '1') === '1';
$gitea_repo = get_option('twp_gitea_repo', 'wp-plugins/twilio-wp-plugin');
$gitea_token = get_option('twp_gitea_token', '');
$twilio_api_key_sid = get_option('twp_twilio_api_key_sid', '');
$twilio_api_key_secret = get_option('twp_twilio_api_key_secret', '');
$fcm_push_credential_sid = get_option('twp_fcm_push_credential_sid', '');
// Get update status
require_once TWP_PLUGIN_DIR . 'includes/class-twp-auto-updater.php';
@@ -90,6 +98,12 @@ $total_sessions = $wpdb->get_var("SELECT COUNT(*) FROM $sessions_table");
</div>
<?php endif; ?>
<?php if (isset($sa_json_error)): ?>
<div class="notice notice-error is-dismissible">
<p><strong><?php echo esc_html($sa_json_error); ?></strong></p>
</div>
<?php endif; ?>
<div class="twp-mobile-settings">
<!-- Mobile App Overview -->
<div class="card" style="max-width: 100%; margin-bottom: 20px;">
@@ -118,76 +132,48 @@ $total_sessions = $wpdb->get_var("SELECT COUNT(*) FROM $sessions_table");
<!-- FCM Configuration -->
<div class="card" style="max-width: 100%; margin-bottom: 20px;">
<h2>Firebase Cloud Messaging (FCM)</h2>
<p>Configure FCM to enable push notifications for the mobile app.</p>
<h2>Firebase Cloud Messaging (FCM) — HTTP v2 API</h2>
<p>Configure FCM using a service account for push notifications. The legacy server key API has been retired by Google.</p>
<table class="form-table">
<tr>
<th scope="row">
<label for="twp_fcm_server_key">FCM Server Key</label>
<label for="twp_fcm_project_id">Firebase Project ID</label>
</th>
<td>
<input type="text"
id="twp_fcm_server_key"
name="twp_fcm_server_key"
value="<?php echo esc_attr($fcm_server_key); ?>"
id="twp_fcm_project_id"
name="twp_fcm_project_id"
value="<?php echo esc_attr($fcm_project_id); ?>"
class="regular-text"
placeholder="AAAA...">
placeholder="my-project-12345">
<p class="description">
Get your server key from Firebase Console > Project Settings > Cloud Messaging > Server Key
Found in Firebase Console &gt; Project Settings &gt; General &gt; Project ID
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="twp_twilio_api_key_sid">Twilio API Key SID</label>
<label for="twp_fcm_service_account_json">Service Account JSON</label>
</th>
<td>
<input type="text"
id="twp_twilio_api_key_sid"
name="twp_twilio_api_key_sid"
value="<?php echo esc_attr($twilio_api_key_sid); ?>"
class="regular-text"
placeholder="SK...">
<textarea id="twp_fcm_service_account_json"
name="twp_fcm_service_account_json"
rows="6"
class="large-text code"
placeholder='Paste the entire contents of your service account JSON file...'><?php echo esc_textarea($fcm_service_account_json); ?></textarea>
<p class="description">
Create an API Key in Twilio Console &gt; Account &gt; API Keys. Required for mobile VoIP tokens.
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="twp_twilio_api_key_secret">Twilio API Key Secret</label>
</th>
<td>
<input type="password"
id="twp_twilio_api_key_secret"
name="twp_twilio_api_key_secret"
value="<?php echo esc_attr($twilio_api_key_secret); ?>"
class="regular-text">
<p class="description">
The secret associated with the API Key SID above. Shown only once when key is created.
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="twp_fcm_push_credential_sid">Push Credential SID</label>
</th>
<td>
<input type="text"
id="twp_fcm_push_credential_sid"
name="twp_fcm_push_credential_sid"
value="<?php echo esc_attr($fcm_push_credential_sid); ?>"
class="regular-text"
placeholder="CR...">
<p class="description">
Twilio Push Credential SID. Create in Twilio Console &gt; Messaging &gt; Push Credentials using your FCM server key. Required for incoming call push notifications.
Generate in Firebase Console &gt; Project Settings &gt; Service Accounts &gt; Generate New Private Key.
Paste the entire JSON file contents here. Must contain <code>client_email</code> and <code>private_key</code> fields.
</p>
<?php if ($fcm_sa_configured): ?>
<p style="color: #00a32a; margin-top: 5px;">&#10003; Service account configured</p>
<?php endif; ?>
</td>
</tr>
</table>
<?php if (!empty($fcm_server_key)): ?>
<?php if ($fcm_sa_configured): ?>
<p>
<button type="submit" name="twp_test_notification" class="button">
Send Test Notification

View File

@@ -1,27 +1,33 @@
<?php
/**
* Firebase Cloud Messaging (FCM) Integration
* Firebase Cloud Messaging (FCM) Integration — HTTP v2 API
*
* Handles push notifications to mobile devices via FCM
* Handles push notifications to mobile devices via FCM using
* service account credentials and OAuth2 access tokens.
*/
class TWP_FCM {
private $server_key;
private $fcm_url = 'https://fcm.googleapis.com/fcm/send';
private $project_id;
private $service_account;
private $fcm_url_template = 'https://fcm.googleapis.com/v1/projects/%s/messages:send';
/**
* Constructor
*/
public function __construct() {
$this->server_key = get_option('twp_fcm_server_key', '');
$this->project_id = get_option('twp_fcm_project_id', '');
$sa_json = get_option('twp_fcm_service_account_json', '');
if (!empty($sa_json)) {
$this->service_account = json_decode($sa_json, true);
}
}
/**
* Send push notification to user's devices
*/
public function send_notification($user_id, $title, $body, $data = array(), $data_only = false) {
if (empty($this->server_key)) {
error_log('TWP FCM: Server key not configured');
if (empty($this->project_id) || empty($this->service_account)) {
error_log('TWP FCM: Project ID or service account not configured');
return false;
}
@@ -57,47 +63,54 @@ class TWP_FCM {
}
/**
* Send notification to specific token
* Send notification to specific token via FCM HTTP v2 API
*/
private function send_to_token($token, $title, $body, $data = array(), $data_only = false) {
if ($data_only) {
$payload = array(
'to' => $token,
'data' => array_merge($data, array(
'title' => $title,
'body' => $body,
'timestamp' => time()
)),
'priority' => 'high'
);
} else {
$notification = array(
$access_token = $this->get_access_token();
if (!$access_token) {
return array('success' => false, 'error' => 'auth_failed');
}
// FCM v2 requires all data values to be strings
$string_data = array();
foreach ($data as $key => $value) {
$string_data[$key] = is_string($value) ? $value : (string)$value;
}
$string_data['title'] = $title;
$string_data['body'] = $body;
$string_data['timestamp'] = (string)time();
// Build the v2 message payload
$message = array(
'token' => $token,
'data' => $string_data,
'android' => array(
'priority' => 'high',
),
);
if (!$data_only) {
$message['notification'] = array(
'title' => $title,
'body' => $body,
'sound' => 'default',
'priority' => 'high',
'click_action' => 'FLUTTER_NOTIFICATION_CLICK'
);
$payload = array(
'to' => $token,
'notification' => $notification,
'data' => array_merge($data, array(
'title' => $title,
'body' => $body,
'timestamp' => time()
)),
'priority' => 'high'
$message['android']['notification'] = array(
'sound' => 'default',
'click_action' => 'FLUTTER_NOTIFICATION_CLICK',
);
}
$payload = array('message' => $message);
$url = sprintf($this->fcm_url_template, $this->project_id);
$headers = array(
'Authorization: key=' . $this->server_key,
'Authorization: Bearer ' . $access_token,
'Content-Type: application/json'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->fcm_url);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
@@ -111,10 +124,14 @@ class TWP_FCM {
if ($http_code !== 200) {
error_log("TWP FCM: Failed to send notification. HTTP $http_code: $response");
// Check if token is invalid
$response_data = json_decode($response, true);
if (isset($response_data['results'][0]['error']) &&
in_array($response_data['results'][0]['error'], array('InvalidRegistration', 'NotRegistered'))) {
$error_code = isset($response_data['error']['details'][0]['errorCode'])
? $response_data['error']['details'][0]['errorCode'] : '';
$error_status = isset($response_data['error']['status'])
? $response_data['error']['status'] : '';
if (in_array($error_code, array('UNREGISTERED', 'INVALID_ARGUMENT')) ||
$error_status === 'NOT_FOUND') {
return array('success' => false, 'error' => 'invalid_token');
}
@@ -124,6 +141,107 @@ class TWP_FCM {
return array('success' => true);
}
/**
* Get OAuth2 access token from service account credentials.
* Caches the token in a transient until near expiry.
*/
private function get_access_token() {
$cached = get_transient('twp_fcm_access_token');
if ($cached) {
return $cached;
}
if (empty($this->service_account)) {
error_log('TWP FCM: Service account not configured');
return false;
}
$jwt = $this->create_jwt();
if (!$jwt) {
return false;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->service_account['token_uri']);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion' => $jwt,
)));
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code !== 200) {
error_log("TWP FCM: Failed to get access token. HTTP $http_code: $response");
return false;
}
$token_data = json_decode($response, true);
$access_token = $token_data['access_token'];
$expires_in = isset($token_data['expires_in']) ? (int)$token_data['expires_in'] : 3600;
// Cache token for 5 minutes less than actual expiry
set_transient('twp_fcm_access_token', $access_token, max(60, $expires_in - 300));
return $access_token;
}
/**
* Create a signed JWT for the service account OAuth2 flow
*/
private function create_jwt() {
$sa = $this->service_account;
if (empty($sa['client_email']) || empty($sa['private_key']) || empty($sa['token_uri'])) {
error_log('TWP FCM: Service account JSON missing required fields');
return false;
}
$now = time();
$header = array('alg' => 'RS256', 'typ' => 'JWT');
$claims = array(
'iss' => $sa['client_email'],
'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
'aud' => $sa['token_uri'],
'iat' => $now,
'exp' => $now + 3600,
);
$segments = array(
$this->base64url_encode(json_encode($header)),
$this->base64url_encode(json_encode($claims)),
);
$signing_input = implode('.', $segments);
$private_key = openssl_pkey_get_private($sa['private_key']);
if (!$private_key) {
error_log('TWP FCM: Failed to parse service account private key');
return false;
}
$signature = '';
if (!openssl_sign($signing_input, $signature, $private_key, OPENSSL_ALGO_SHA256)) {
error_log('TWP FCM: Failed to sign JWT');
return false;
}
$segments[] = $this->base64url_encode($signature);
return implode('.', $segments);
}
/**
* Base64url encode (RFC 4648)
*/
private function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
/**
* Get all active FCM tokens for a user
*/
@@ -218,7 +336,7 @@ class TWP_FCM {
$data = array(
'type' => 'test',
'test' => true
'test' => 'true'
);
return $this->send_notification($user_id, $title, $body, $data);

View File

@@ -689,42 +689,25 @@ class TWP_Mobile_API {
public function get_voice_token($request) {
$user_id = $this->auth->get_current_user_id();
$user = get_userdata($user_id);
$identity = 'agent' . $user_id . preg_replace('/[^a-zA-Z0-9]/', '', $user->user_login);
$account_sid = get_option('twp_twilio_account_sid');
$auth_token = get_option('twp_twilio_auth_token');
$api_key_sid = get_option('twp_twilio_api_key_sid');
$api_key_secret = get_option('twp_twilio_api_key_secret');
$twiml_app_sid = get_option('twp_twiml_app_sid');
$push_credential_sid = get_option('twp_fcm_push_credential_sid');
if (empty($api_key_sid) || empty($api_key_secret)) {
return new WP_Error('missing_api_key', 'Twilio API Key SID and Secret must be configured', array('status' => 500));
$clean_name = preg_replace('/[^a-zA-Z0-9]/', '', $user->user_login);
if (empty($clean_name)) {
$clean_name = 'user';
}
$identity = 'agent' . $user_id . $clean_name;
try {
$token = new \Twilio\Jwt\AccessToken(
$account_sid,
$api_key_sid,
$api_key_secret,
3600,
$identity
);
require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-twp-twilio-api.php';
$twilio = new TWP_Twilio_API();
$result = $twilio->generate_capability_token($identity);
$voiceGrant = new \Twilio\Jwt\Grants\VoiceGrant();
$voiceGrant->setOutgoingApplicationSid($twiml_app_sid);
$voiceGrant->setIncomingAllow(true);
if (!empty($push_credential_sid)) {
$voiceGrant->setPushCredentialSid($push_credential_sid);
if (!$result['success']) {
return new WP_Error('token_error', $result['error'], array('status' => 500));
}
$token->addGrant($voiceGrant);
return new WP_REST_Response(array(
'token' => $token->toJWT(),
'identity' => $identity,
'expires_in' => 3600
'token' => $result['data']['token'],
'identity' => $result['data']['client_name'],
'expires_in' => $result['data']['expires_in']
), 200);
} catch (Exception $e) {

View File

@@ -1,13 +1,13 @@
{
"project_info": {
"project_number": "000000000000",
"project_id": "twp-softphone-placeholder",
"storage_bucket": "twp-softphone-placeholder.appspot.com"
"project_number": "187457540438",
"project_id": "twp-softphone",
"storage_bucket": "twp-softphone.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:000000000000:android:0000000000000000",
"mobilesdk_app_id": "1:187457540438:android:d6d777270c23f6660946f7",
"android_client_info": {
"package_name": "io.cloudhosting.twp.twp_softphone"
}
@@ -15,7 +15,7 @@
"oauth_client": [],
"api_key": [
{
"current_key": "PLACEHOLDER_API_KEY"
"current_key": "AIzaSyAdGJrWPy9b9arqHnlY5G_hsGiDcm6cchA"
}
],
"services": {