Completed
Push — whmcs-jetpack-partner-module ( 6558f9...3550d5 )
by
unknown
09:05
created

jetpack.php ➔ jetpack_CreateAccount()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 10
nop 1
dl 0
loc 30
rs 8.5066
c 0
b 0
f 0
1
<?php
2
3
use WHMCS\Database\Capsule;
4
5
6
if (!defined("WHMCS")) {
7
    die("This file cannot be accessed directly");
8
}
9
10
11
/**
12
 * A WHMCS module for use by Jetpack hosting partners to provision Jetpack plans.
13
 * The module provides functionality for partner hosts to be able to save their
14
 * client id and secret to request an access token for provisioning plans.
15
 *
16
 * Plans available for provisioning include free, personal, premium and professional
17
 *
18
 * A host has options to either provision(Create == WHMCS equivalent functional term)
19
 * or Cancel(Terminate == WHMCS equivalent functional term) from the WHMCS client area.
20
 *
21
 * Host setup for custom fields is currently required in order to use the module.
22
 *
23
 */
24
25
/**
26
 * Jetpack Meta Data for WHMCS module.
27
 * @return array
28
 */
29
function jetpack_MetaData()
30
{
31
    return array(
32
        'DisplayName' => 'Jetpack by Automattic',
33
        'Description' => 'Use this module to provision Jetpack plans with your Jetpack hosting partner account',
34
        'APIVersion' => '1.1',
35
        'RequiresServer' => false,
36
    );
37
}
38
39
40
/**
41
 * Basic configuration options required for a partner to get
42
 * a Jetpack plan provisioned. Currently a partner client id
43
 * and secret are the only host partner options needed to get
44
 * an access token to provision a Jetpack plan
45
 * @return array
46
 */
47
function jetpack_ConfigOptions()
48
{
49
    return array(
50
        'Jetpack Partner Client ID' => array(
51
            'Type' => 'text',
52
            'Size' => '256',
53
        ),
54
        'Jetpack Partner Client Secret' => array(
55
            'Type' => 'text',
56
            'Size' => '256',
57
        )
58
    );
59
}
60
61
62
/**
63
 * Equivalent to /provision. Create a Jetpack plan using
64
 * a Jetpack Hosting partner account.
65
 *
66
 *
67
 * @param array $params
68
 * @return string 'success'
69
 * @throws Exception
70
71
 */
72
function jetpack_CreateAccount(array $params)
73
{
74
    try {
75
        validate_required_fields($params);
76
77
        $access_token = get_access_token($params);
78
        $provisioning_url = "https://public-api.wordpress.com/rest/v1.3/jpphp/provision";
79
        $request_data = array (
80
            'plan' => strtolower($params['customfields']['Plan']),
81
            'siteurl' => $params['customfields']['Site URL'],
82
            'local_user' => $params['customfields']['Local User'],
83
            'force_register' => true,
84
        );
85
86
        $response = make_api_request($provisioning_url, $access_token, $request_data);
87
        if ($response->success && $response->success == true) {
88
            if ($response->next_url) {
89
                save_provisioning_details($response->next_url, $params);
90
            } else if(!$response->next_url && $response->auth_required)  {
91
                save_provisioning_details($response->next_url, $params, true);
92
            }
93
94
            return 'success';
95
        }
96
    } catch(Exception $e) {
97
        logModuleCall('jetpack', __FUNCTION__, $params, $e->getMessage(), $e->getMessage());
98
        return $e->getMessage();
99
    }
100
101
}
102
103
/**
104
 * Equivalent to partner/cancel. Cancel a Jetpack plan using
105
 * using a Jetpack Hosting partner account.
106
 *
107
 * @param array $params
108
 * @return string
109
 * @throws Exception
110
 */
111
function jetpack_TerminateAccount(array $params)
112
{
113
    try {
114
        validate_required_fields($params);
115
116
        $access_token = get_access_token($params);
117
        $clean_url = str_replace('/', '::', $params['customfields']['Site URL']);
118
        $url = 'https://public-api.wordpress.com/rest/v1.3/jpphp/' . $clean_url .'/partner-cancel';
119
        $response = make_api_request($url, $access_token);
120
        if ($response->success == true) {
121
            return 'success';
122
        }
123
    }
124
    catch (Exception $e) {
125
        logModuleCall('jetpack', __FUNCTION__, $params, $e->getMessage(), $e->getMessage());
126
        return $e->getMessage();
127
    }
128
129
}
130
131
/**
132
 * Get a Jetpack partner access token using the client_id
133
 * and client secret stored when the product was created in
134
 * the WHMCS product settings.
135
 *
136
 *
137
 * @param $params
138
 * @return mixed
139
 * @throws Exception
140
 */
141
function get_access_token($params)
142
{
143
144
    $oauthURL = "https://public-api.wordpress.com/oauth2/token";
145
146
    $credentials = array (
147
        'client_id' => $params['configoption1'],
148
        'client_secret' => $params['configoption2'],
149
        'grant_type' => 'client_credentials',
150
        'scope' => 'jetpack-partner'
151
    );
152
153
    $response = make_api_request($oauthURL, null, $credentials);
154
    if (isset($response->access_token)) {
155
        return $response->access_token;
156
    } else {
157
        throw new Exception('There was an issue authorizing your partner account for provisioning. Please contact
158
        us for assistance');
159
    }
160
}
161
162
163
/**
164
 * Make an API request for authenticating and provisioning or
165
 * cancelling a Jetpack plan
166
 *
167
 * @param $url
168
 * @param $data
169
 * @param string $auth
170
 * @return mixed
171
 * @throws Exception
172
 */
173
function make_api_request($url, $auth='', $data=[])
174
{
175
    if (isset($auth)) {
176
        $auth = "Authorization: Bearer " . $auth;
177
    }
178
179
    $curl = curl_init();
180
    curl_setopt_array($curl, array(
181
        CURLOPT_HTTPHEADER => array($auth),
182
        CURLOPT_URL => $url,
183
        CURLOPT_RETURNTRANSFER => true,
184
        CURLOPT_ENCODING => "",
185
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
186
        CURLOPT_POSTFIELDS => $data,
187
        CURLOPT_CUSTOMREQUEST => "POST"
188
    ));
189
190
    $response = curl_exec($curl);
191
    $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
192
193
    if ($http_code >= 400) {
194
        logModuleCall('jetpack', __FUNCTION__, $url, $data, $response);
195
        throw new Exception('Something went wrong while provisioning. Please temporarily enable Module logging 
196
        in the Utilities tab and try again for more information');
197
    } else if (curl_error($curl)) {
198
        throw new Exception('Unable to connect: ' . curl_errno($curl) . ' - ' . curl_error($curl));
199
    } elseif (empty($response)) {
200
        throw new Exception('Empty response');
201
    }
202
203
    curl_close($curl);
204
    return json_decode($response);
205
}
206
207
/**
208
 * Save the next_url for Jetpack activation/setup to the
209
 * order for the client
210
 *
211
 * @param $url
212
 * @param $orderId
213
 */
214
function save_provisioning_details($url, $params, $pending=false)
215
{
216
    $jetpack_next_url_field = Capsule::table('tblcustomfields')
217
        ->where(array('fieldname' => 'jetpack_provisioning_details', 'type' => 'product'))->first();
218
219
    $details = '';
220
    if ($url) {
221
        $details = 'URL to Activate Jetpack: ' . $url;
222
    } else if ($pending) {
223
        $details = 'The domain did not appear to resolve when provisioning was attempted however a Jetpack plan is waiting for ' .
224
            $params['customfields']['Site URL'] . '. Once DNS resolves please connect the site via the Jetpack Banner in the sites dashboard';
225
    }
226
    Capsule::table('tblcustomfieldsvalues')->where(array('fieldid' => $jetpack_next_url_field->id))->update(array(
227
         'relid' => $params['model']['orderId'], 'value' => $details));
228
}
229
230
/**
231
 * Validate that the module was correctly set up when the product was
232
 * created by the WHMCS user and that the required Fields/Options for
233
 * being able to provision a Jetpack plan are present. Fields validated are
234
 *  - Allowed Plans from Plan Custom Field
235
 *  - Required Custom Fields
236
 *  - Required Config Options
237
 *
238
 * @param array $params
239
 * @return bool
240
 * @throws Exception
241
 */
242
function validate_required_fields(array $params)
243
{
244
    $allowed_plans = array('free', 'personal', 'premium', 'professional');
245
    $required_custom_fields = array('Plan', 'Site URL', 'Local User');
246
247
    foreach ($required_custom_fields as $field)
248
        if (!isset($params['customfields'][$field])) {
249
            throw new Exception('The module does not appear to be setup correctly. The required custom field '
250
            . $field . ' was not setup when the product was created. Please see the module documentation for more information');
251
        }
252
253
    $jetpack_next_url_field = Capsule::table('tblcustomfields')
254
        ->where(array('fieldname' => 'jetpack_provisioning_details', 'type' => 'product'))->first();
255
256
    if (!$jetpack_next_url_field) {
257
        throw new Exception('The module does not appear to be setup correctly. The jetpack_provisioning_details field is missing');
258
    }
259
260
    if (!in_array(strtolower($params['customfields']['Plan']), $allowed_plans)) {
261
        throw new Exception('The module does not appear to be setup correctly. ' .
262
            $params['customfields']['Plan'] . ' is not an allowed plan');
263
    }
264
265
    if (!isset($params['configoption1']) || !isset($params['configoption2'])) {
266
        throw new Exception('Your credentials for provisioning are not complete. Please see the module documentation
267
        for more information');
268
    }
269
270
    return true;
271
}
272
273