19 | Join our active, engaged community:
20 |
Website
21 |
|
22 |
Spectrum
23 |
24 |
25 |
26 | [](https://travis-ci.com/sparkplug/momoapi-php)
27 | [](https://packagist.org/packages/sparkplug/momoapi-php)
28 | [](https://packagist.org/packages/sparkplug/momoapi-php)
29 | [](https://packagist.org/packages/sparkplug/momoapi-php)
30 | [](https://coveralls.io/github/sparkplug/momoapi-php?branch=master)
31 | [](https://spectrum.chat/momo-api-developers/)
32 |
33 |
34 |
35 | # Installation
36 |
37 | You are required to have PHP 5.4.0 and later.
38 |
39 | ## Composer
40 |
41 | You can install the bindings via [Composer](http://getcomposer.org/). Run the following command:
42 |
43 | ```bash
44 | composer require sparkplug/momoapi-php
45 | ```
46 |
47 | To use the bindings, use Composer's [autoload](https://getcomposer.org/doc/01-basic-usage.md#autoloading):
48 |
49 | ```php
50 | require_once('vendor/autoload.php');
51 | ```
52 |
53 | ## Manual Installation
54 |
55 | If you do not wish to use Composer, you can download the [latest release](https://github.com/sparkplug/momoapi-php/releases). Then, to use the bindings, include the `init.php` file.
56 |
57 | ```php
58 | require_once('/path/to/momoapi-php/init.php');
59 | ```
60 |
61 | ## Dependencies
62 |
63 | The bindings require the following extensions in order to work properly:
64 |
65 | - [`curl`](https://secure.php.net/manual/en/book.curl.php), although you can use your own non-cURL client if you prefer
66 | - [`json`](https://secure.php.net/manual/en/book.json.php)
67 | - [`mbstring`](https://secure.php.net/manual/en/book.mbstring.php) (Multibyte String)
68 |
69 | If you use Composer, these dependencies should be handled automatically. If you install manually, you'll want to make sure that these extensions are available.
70 |
71 | # Sandbox Environment
72 |
73 | ## Creating a sandbox environment API user
74 |
75 | Next, we need to get the `User ID` and `User Secret` and to do this we shall need to use the Primary Key for the Product to which we are subscribed, as well as specify a host. The library ships with a commandline application that helps to create sandbox credentials. It assumes you have created an account on `https://momodeveloper.mtn.com` and have your `Ocp-Apim-Subscription-Key`.
76 |
77 | ```bash
78 | ## within the project, on the command line. In this example, our domain is akabbo.ug
79 | $ php vendor/sparkplug/momoapi-php/lib/Provision.php
80 | $ providerCallBackHost: https://akabbo.ug
81 | $ Ocp-Apim-Subscription-Key: f83xx8d8xx6749f19a26e2265aeadbcdeg
82 | ```
83 |
84 | The `providerCallBackHost` is your callback host and `Ocp-Apim-Subscription-Key` is your API key for the specific product to which you are subscribed. The `API Key` is unique to the product and you will need an `API Key` for each product you use. You should get a response similar to the following:
85 |
86 | ```bash
87 | Here is your User Id and API secret : {'apiKey': 'b0431db58a9b41faa8f5860230xxxxxx', 'UserId': '053c6dea-dd68-xxxx-xxxx-c830dac9f401'}
88 | ```
89 |
90 | These are the credentials we shall use for the sandbox environment. In production, these credentials are provided for you on the MTN OVA management dashboard after KYC requirements are met.
91 |
92 |
93 |
94 | ## Configuration
95 |
96 | Before we can fully utilize the library, we need to specify global configurations. The global configuration using the requestOpts builder. By default, these are picked from environment variables,
97 | but can be overidden using the MomoApi builder
98 |
99 | * `BASE_URL`: An optional base url to the MTN Momo API. By default the staging base url will be used
100 | * `ENVIRONMENT`: Optional enviroment, either "sandbox" or "production". Default is 'sandbox'
101 | * `CURRENCY`: currency by default its EUR
102 | * `CALLBACK_HOST`: The domain where you webhooks urls are hosted. This is mandatory.
103 | * `COLLECTION_PRIMARY_KEY`: The collections API primary key,
104 | * `COLLECTION_USER_ID`: The collection User Id
105 | * `COLLECTION_API_SECRET`: The Collection API secret
106 | * `REMITTANCE_USER_ID`: The Remittance User ID
107 | * `REMITTANCE_API_SECRET`: The Remittance API Secret
108 | * `REMITTANCE_PRIMARY_KEY`: The Remittance Subscription Key
109 | * `DISBURSEMENT_USER_ID`: The Disbursement User ID
110 | * `DISBURSEMENT_API_SECRET`: The Disbursement API Secret
111 | * `DISBURSEMENT_PRIMARY_KEY`: The Disbursement Primary Key
112 |
113 | Once you have specified the global variables, you can now provide the product-specific variables. Each MoMo API product requires its own authentication details i.e its own `Subscription Key`, `User ID` and `User Secret`, also sometimes refered to as the `API Secret`. As such, we have to configure subscription keys for each product you will be using.
114 |
115 | You will only need to configure the variables for the product(s) you will be using.
116 |
117 | you can also use the MomoApi to globally set the different variables.
118 |
119 |
120 |
121 | ```php
122 | MomoApi::setBaseUrl('base');
123 |
124 | MomoApi::setTargetEnvironment("targetenv");
125 |
126 | MomoApi::setCurrency("UGX");
127 |
128 | MomoApi::setCollectionApiSecret("collection_api_secret");
129 |
130 | MomoApi::setCollectionPrimaryKey("collection_primary_key");
131 |
132 | MomoApi::setCollectionUserId("collection_user_id");
133 |
134 | MomoApi::setRemittanceApiSecret("remittance_api_secret");
135 |
136 | MomoApi::setRemittancePrimaryKey("remittance_primary_key");
137 |
138 | MomoApi::setRemittanceUserId("remittance_user_id" );
139 |
140 | MomoApi::setDisbursementApiSecret("disbursement_api_secret");
141 |
142 | MomoApi::setDisbursementPrimaryKey("disbursement_primary_key");
143 |
144 | MomoApi::setDisbursementUserId("disbursement_user_id");
145 | ```
146 |
147 |
148 | ## Collections
149 |
150 | The collections client can be created with the following paramaters. Note that the `COLLECTION_USER_ID` and `COLLECTION_API_SECRET` for production are provided on the MTN OVA dashboard;
151 |
152 | * `COLLECTION_PRIMARY_KEY`: Primary Key for the `Collection` product on the developer portal.
153 | * `COLLECTION_USER_ID`: For sandbox, use the one generated with the `mtnmomo` command.
154 | * `COLLECTION_API_SECRET`: For sandbox, use the one generated with the `mtnmomo` command.
155 |
156 | You can create a collection client with the following:
157 |
158 | ```php
159 | $client = Collection();
160 | ```
161 |
162 | ### Methods
163 |
164 | 1. `requestToPay`: This operation is used to request a payment from a consumer (Payer). The payer will be asked to authorize the payment. The transaction is executed once the payer has authorized the payment. The transaction will be in status PENDING until it is authorized or declined by the payer or it is timed out by the system. Status of the transaction can be validated by using `getTransactionStatus`.
165 |
166 | 2. `getTransaction`: Retrieve transaction information using the `transactionId` returned by `requestToPay`. You can invoke it at intervals until the transaction fails or succeeds. If the transaction has failed, it will throw an appropriate error.
167 |
168 | 3. `getBalance`: Get the balance of the account.
169 |
170 | 4. `isPayerActive`: check if an account holder is registered and active in the system.
171 |
172 | ### Sample Code
173 |
174 | ```php
175 |
176 | $coll = new Collection($currency = "c..", $baseUrl = "url..", $targetEnvironment = "u...", $collectionApiSecret = "u...", $collectionPrimaryKey = "u...", $collectionUserId = "u..."]);
177 |
178 | $params = ['mobile' => "256782181656", 'payee_note' => "34", 'payer_message' => "12", 'external_id' => "ref", 'currency' => "EUR", 'amount' => "500"];
179 |
180 | $t = $coll->requestToPay($params);
181 |
182 | $transaction = $coll->getTransaction($t);
183 |
184 | ```
185 |
186 | ## Disbursement
187 |
188 | The Disbursements client can be created with the following paramaters. Note that the `DISBURSEMENT_USER_ID` and `DISBURSEMENT_API_SECRET` for production are provided on the MTN OVA dashboard;
189 |
190 | * `DISBURSEMENT_PRIMARY_KEY`: Primary Key for the `Disbursement` product on the developer portal.
191 | * `DISBURSEMENT_USER_ID`: For sandbox, use the one generated with the `mtnmomo` command.
192 | * `DISBURSEMENT_API_SECRET`: For sandbox, use the one generated with the `mtnmomo` command.
193 |
194 | You can create a disbursements client with the following
195 |
196 | ```php
197 |
198 | $disbursement = new Disbursement();
199 |
200 | $params = ['mobile' => "256782181656", 'payee_note' => "34", 'payer_message' => "12", 'external_id' => "ref", 'currency' => "EUR", 'amount' => "500"];
201 |
202 | $t = $disbursement->requestToPay($params);
203 |
204 |
205 | $transaction = $disbursement->getTransaction($t);
206 |
207 | ```
208 |
209 | ### Methods
210 |
211 | 1. `transfer`: Used to transfer an amount from the owner’s account to a payee account. Status of the transaction can be validated by using the `getTransactionStatus` method.
212 |
213 | 2. `getTransactionStatus`: Retrieve transaction information using the `transactionId` returned by `transfer`. You can invoke it at intervals until the transaction fails or succeeds.
214 |
215 | 2. `getBalance`: Get your account balance.
216 |
217 | 3. `isPayerActive`: This method is used to check if an account holder is registered and active in the system.
218 |
219 | #### Sample Code
220 |
221 | ```php
222 |
223 |
224 | ```
225 |
226 |
227 | ## Custom Request Timeouts
228 |
229 | *NOTE:* We do not recommend decreasing the timeout for non-read-only calls , since even if you locally timeout, the request can still complete.
230 |
231 | To modify request timeouts (connect or total, in seconds) you'll need to tell the API client to use a CurlClient other than its default. You'll set the timeouts in that CurlClient.
232 |
233 | ```php
234 | // set up your tweaked Curl client
235 | $curl = new \MomoApi\HttpClient\CurlClient();
236 | $curl->setTimeout(10); // default is \MomoApi\HttpClient\CurlClient::DEFAULT_TIMEOUT
237 | $curl->setConnectTimeout(5); // default is \MomoApi\HttpClient\CurlClient::DEFAULT_CONNECT_TIMEOUT
238 |
239 | echo $curl->getTimeout(); // 10
240 | echo $curl->getConnectTimeout(); // 5
241 |
242 | // tell MomoApi to use the tweaked client
243 | \MomoApi\ApiRequest::setHttpClient($curl);
244 |
245 | // use the Momo API client as you normally would
246 | ```
247 |
248 | ## Custom cURL Options (e.g. proxies)
249 |
250 | Need to set a proxy for your requests? Pass in the requisite `CURLOPT_*` array to the CurlClient constructor, using the same syntax as `curl_stopt_array()`. This will set the default cURL options for each HTTP request made by the SDK, though many more common options (e.g. timeouts; see above on how to set those) will be overridden by the client even if set here.
251 |
252 | ```php
253 | // set up your tweaked Curl client
254 | $curl = new \MomoApi\HttpClient\CurlClient([CURLOPT_PROXY => 'proxy.local:80']);
255 | // tell MomoApi to use the tweaked client
256 | \MomoApi\ApiRequest::setHttpClient($curl);
257 | ```
258 |
259 | Alternately, a callable can be passed to the CurlClient constructor that returns the above array based on request inputs. See `testDefaultOptions()` in `tests/CurlClientTest.php` for an example of this behavior. Note that the callable is called at the beginning of every API request, before the request is sent.
260 |
261 | ### Configuring a Logger
262 |
263 | The library does minimal logging, but it can be configured
264 | with a [`PSR-3` compatible logger][psr3] so that messages
265 | end up there instead of `error_log`:
266 |
267 | ```php
268 | \MomoApi\MomoApi::setLogger($logger);
269 | ```
270 |
271 |
272 | ### Configuring Automatic Retries
273 |
274 | The library can be configured to automatically retry requests that fail due to
275 | an intermittent network problem:
276 |
277 | ```php
278 | \MomoApi\MomoApi::setMaxNetworkRetries(2);
279 | ```
280 |
281 |
282 | ## Development
283 |
284 | Get [Composer][composer]. For example, on Mac OS:
285 |
286 | ```bash
287 | brew install composer
288 | ```
289 |
290 | Install dependencies:
291 |
292 | ```bash
293 | composer install
294 | ```
295 |
296 |
297 |
298 | Install dependencies as mentioned above (which will resolve [PHPUnit](http://packagist.org/packages/phpunit/phpunit)), then you can run the test suite:
299 |
300 | ```bash
301 | ./vendor/bin/phpunit
302 | ```
303 |
304 | Or to run an individual test file:
305 |
306 | ```bash
307 | ./vendor/bin/phpunit tests/UtilTest.php
308 | ```
309 |
310 |
311 | [composer]: https://getcomposer.org/
312 | [curl]: http://curl.haxx.se/docs/caextract.html
313 | [psr3]: http://www.php-fig.org/psr/psr-3/
314 |
--------------------------------------------------------------------------------
/lib/HttpClient/CurlClient.php:
--------------------------------------------------------------------------------
1 | defaultOptions = $defaultOptions;
58 | $this->randomGenerator = $randomGenerator ?: new Util\RandomGenerator();
59 | $this->initUserAgentInfo();
60 |
61 | // TODO: curl_reset requires PHP >= 5.5.0. Once we drop support for PHP 5.4, we can simply
62 | // initialize this to true.
63 | $this->enablePersistentConnections = function_exists('curl_reset');
64 |
65 | $this->enableHttp2 = $this->canSafelyUseHttp2();
66 | }
67 |
68 | public function initUserAgentInfo()
69 | {
70 | $curlVersion = curl_version();
71 | $this->userAgentInfo = [
72 | 'httplib' => 'curl ' . $curlVersion['version'],
73 | 'ssllib' => $curlVersion['ssl_version'],
74 | ];
75 | }
76 |
77 | /**
78 | * Indicates whether it is safe to use HTTP/2 or not.
79 | *
80 | * @return boolean
81 | */
82 | private function canSafelyUseHttp2()
83 | {
84 | // Versions of curl older than 7.60.0 don't respect GOAWAY frames
85 | // (cf. https://github.com/curl/curl/issues/2416), which MomoApi use.
86 | $curlVersion = curl_version()['version'];
87 | return (version_compare($curlVersion, '7.60.0') >= 0);
88 | }
89 |
90 | public static function instance()
91 | {
92 | if (!self::$instance) {
93 | self::$instance = new self();
94 | }
95 | return self::$instance;
96 | }
97 |
98 | public function __destruct()
99 | {
100 | $this->closeCurlHandle();
101 | }
102 |
103 | /**
104 | * Closes the curl handle if initialized. Do nothing if already closed.
105 | */
106 | private function closeCurlHandle()
107 | {
108 | if (!is_null($this->curlHandle)) {
109 | curl_close($this->curlHandle);
110 | $this->curlHandle = null;
111 | }
112 | }
113 |
114 | // USER DEFINED TIMEOUTS
115 |
116 | public function getDefaultOptions()
117 | {
118 | return $this->defaultOptions;
119 | }
120 |
121 | public function getUserAgentInfo()
122 | {
123 | return $this->userAgentInfo;
124 | }
125 |
126 | public function getTimeout()
127 | {
128 | return $this->timeout;
129 | }
130 |
131 | public function setTimeout($seconds)
132 | {
133 | $this->timeout = (int)max($seconds, 0);
134 | return $this;
135 | }
136 |
137 | public function getConnectTimeout()
138 | {
139 | return $this->connectTimeout;
140 | }
141 |
142 | public function setConnectTimeout($seconds)
143 | {
144 | $this->connectTimeout = (int)max($seconds, 0);
145 | return $this;
146 | }
147 |
148 | public function request($method, $absUrl, $headers, $params)
149 | {
150 | $method = strtolower($method);
151 |
152 | $opts = [];
153 | if (is_callable($this->defaultOptions)) { // call defaultOptions callback, set options to return value
154 | $opts = call_user_func_array($this->defaultOptions, func_get_args());
155 | if (!is_array($opts)) {
156 | throw new Error\MomoApiError("Non-array value returned by defaultOptions CurlClient callback");
157 | }
158 | } elseif (is_array($this->defaultOptions)) { // set default curlopts from array
159 | $opts = $this->defaultOptions;
160 | }
161 |
162 | $params = Util\Util::objectsToIds($params);
163 |
164 | if ($method == 'get') {
165 | $opts[CURLOPT_HTTPGET] = 1;
166 | if (count($params) > 0) {
167 | $encoded = Util\Util::encodeParameters($params);
168 | $absUrl = "$absUrl?$encoded";
169 | }
170 | } elseif ($method == 'post') {
171 | $opts[CURLOPT_POST] = true;
172 | $opts[CURLOPT_POSTFIELDS] = json_encode($params);
173 | } elseif ($method == 'delete') {
174 | $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
175 | if (count($params) > 0) {
176 | $encoded = json_encode($params);
177 | $absUrl = "$absUrl?$encoded";
178 | }
179 | } else {
180 | throw new Error\MomoApiError("Unrecognized method $method");
181 | }
182 |
183 |
184 | // Create a callback to capture HTTP headers for the response
185 | $rheaders = new Util\CaseInsensitiveArray();
186 | $headerCallback = function ($curl, $header_line) use (&$rheaders) {
187 | // Ignore the HTTP request line (HTTP/1.1 200 OK)
188 | if (strpos($header_line, ":") === false) {
189 | return strlen($header_line);
190 | }
191 | list($key, $value) = explode(":", trim($header_line), 2);
192 | $rheaders[trim($key)] = trim($value);
193 | return strlen($header_line);
194 | };
195 |
196 | // By default for large request body sizes (> 1024 bytes), cURL will
197 | // send a request without a body and with a `Expect: 100-continue`
198 | // header, which gives the server a chance to respond with an error
199 | // status code in cases where one can be determined right away (say
200 | // on an authentication problem for example), and saves the "large"
201 | // request body from being ever sent.
202 | //
203 | // Unfortunately, the bindings don't currently correctly handle the
204 | // success case (in which the server sends back a 100 CONTINUE), so
205 | // we'll error under that condition. To compensate for that problem
206 | // for the time being, override cURL's behavior by simply always
207 | // sending an empty `Expect:` header.
208 | array_push($headers, 'Expect: ');
209 |
210 | $absUrl = Util\Util::utf8($absUrl);
211 | $opts[CURLOPT_URL] = $absUrl;
212 | $opts[CURLOPT_RETURNTRANSFER] = true;
213 | $opts[CURLOPT_CONNECTTIMEOUT] = $this->connectTimeout;
214 | $opts[CURLOPT_TIMEOUT] = $this->timeout;
215 | $opts[CURLOPT_HEADERFUNCTION] = $headerCallback;
216 | $opts[CURLOPT_HTTPHEADER] = $headers;
217 |
218 | //$opts[CURLOPT_VERBOSE] = 1;
219 |
220 | $opts[CURLOPT_SSL_VERIFYHOST] = false;
221 | $opts[CURLOPT_SSL_VERIFYPEER] = false;
222 |
223 |
224 | if (!isset($opts[CURLOPT_HTTP_VERSION]) && $this->getEnableHttp2()) {
225 | // For HTTPS requests, enable HTTP/2, if supported
226 | $opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2TLS;
227 | }
228 |
229 | list($rbody, $rcode) = $this->executeRequestWithRetries($opts, $absUrl);
230 |
231 | return [$rbody, $rcode, $rheaders];
232 | }
233 |
234 | /**
235 | * @return boolean
236 | */
237 | public function getEnableHttp2()
238 | {
239 | return $this->enableHttp2;
240 | }
241 |
242 | // END OF USER DEFINED TIMEOUTS
243 |
244 | /**
245 | * @param boolean $enable
246 | */
247 | public function setEnableHttp2($enable)
248 | {
249 | $this->enableHttp2 = $enable;
250 | }
251 |
252 | /**
253 | * @param array $opts cURL options
254 | */
255 | private function executeRequestWithRetries($opts, $absUrl)
256 | {
257 | $numRetries = 0;
258 |
259 | while (true) {
260 | $rcode = 0;
261 | $errno = 0;
262 |
263 | $this->resetCurlHandle();
264 | curl_setopt_array($this->curlHandle, $opts);
265 | $rbody = curl_exec($this->curlHandle);
266 |
267 | if ($rbody === false) {
268 | $errno = curl_errno($this->curlHandle);
269 | $message = curl_error($this->curlHandle);
270 | } else {
271 | $rcode = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE);
272 | }
273 | if (!$this->getEnablePersistentConnections()) {
274 | $this->closeCurlHandle();
275 | }
276 |
277 | if ($this->shouldRetry($errno, $rcode, $numRetries)) {
278 | $numRetries += 1;
279 | $sleepSeconds = $this->sleepTime($numRetries);
280 | usleep(intval($sleepSeconds * 1000000));
281 | } else {
282 | break;
283 | }
284 | }
285 |
286 | if ($rbody === false) {
287 | $this->handleCurlError($absUrl, $errno, $message, $numRetries);
288 | }
289 |
290 | return [$rbody, $rcode];
291 | }
292 |
293 | /**
294 | * Resets the curl handle. If the handle is not already initialized, or if persistent
295 | * connections are disabled, the handle is reinitialized instead.
296 | */
297 | private function resetCurlHandle()
298 | {
299 | if (!is_null($this->curlHandle) && $this->getEnablePersistentConnections()) {
300 | curl_reset($this->curlHandle);
301 | } else {
302 | $this->initCurlHandle();
303 | }
304 | }
305 |
306 | /**
307 | * @return boolean
308 | */
309 | public function getEnablePersistentConnections()
310 | {
311 | return $this->enablePersistentConnections;
312 | }
313 |
314 | /**
315 | * @param boolean $enable
316 | */
317 | public function setEnablePersistentConnections($enable)
318 | {
319 | $this->enablePersistentConnections = $enable;
320 | }
321 |
322 | /**
323 | * Initializes the curl handle. If already initialized, the handle is closed first.
324 | */
325 | private function initCurlHandle()
326 | {
327 | $this->closeCurlHandle();
328 | $this->curlHandle = curl_init();
329 | }
330 |
331 | /**
332 | * Checks if an error is a problem that we should retry on. This includes both
333 | * socket errors that may represent an intermittent problem and some special
334 | * HTTP statuses.
335 | *
336 | * @param int $errno
337 | * @param int $rcode
338 | * @param int $numRetries
339 | * @return bool
340 | */
341 | private function shouldRetry($errno, $rcode, $numRetries)
342 | {
343 | if ($numRetries >= MomoApi::getMaxNetworkRetries()) {
344 | return false;
345 | }
346 |
347 | // Retry on timeout-related problems (either on open or read).
348 | if ($errno === CURLE_OPERATION_TIMEOUTED) {
349 | return true;
350 | }
351 |
352 | // Destination refused the connection, the connection was reset, or a
353 | // variety of other connection failures. This could occur from a single
354 | // saturated server, so retry in case it's intermittent.
355 | if ($errno === CURLE_COULDNT_CONNECT) {
356 | return true;
357 | }
358 |
359 | // 409 conflict
360 | if ($rcode === 409) {
361 | return true;
362 | }
363 |
364 | return false;
365 | }
366 |
367 | private function sleepTime($numRetries)
368 | {
369 | // Apply exponential backoff with $initialNetworkRetryDelay on the
370 | // number of $numRetries so far as inputs. Do not allow the number to exceed
371 | // $maxNetworkRetryDelay.
372 | $sleepSeconds = min(
373 | MomoApi::getInitialNetworkRetryDelay() * 1.0 * pow(2, $numRetries - 1),
374 | MomoApi::getMaxNetworkRetryDelay()
375 | );
376 |
377 | // Apply some jitter by randomizing the value in the range of
378 | // ($sleepSeconds / 2) to ($sleepSeconds).
379 | $sleepSeconds *= 0.5 * (1 + $this->randomGenerator->randFloat());
380 |
381 | // But never sleep less than the base sleep seconds.
382 | $sleepSeconds = max(MomoApi::getInitialNetworkRetryDelay(), $sleepSeconds);
383 |
384 | return $sleepSeconds;
385 | }
386 |
387 | /**
388 | * @param string $url
389 | * @param int $errno
390 | * @param string $message
391 | * @param int $numRetries
392 | * @throws Error\ApiConnection
393 | */
394 | private function handleCurlError($url, $errno, $message, $numRetries)
395 | {
396 | switch ($errno) {
397 | case CURLE_COULDNT_CONNECT:
398 | case CURLE_COULDNT_RESOLVE_HOST:
399 | case CURLE_OPERATION_TIMEOUTED:
400 | $msg = "Could not connect to MomoApi ($url). Please check your "
401 | . "internet connection and try again. If this problem persists, "
402 | . "you should check MomoApi's service status at "
403 | . "https://twitter.com/stripestatus, or";
404 | break;
405 | case CURLE_SSL_CACERT:
406 | case CURLE_SSL_PEER_CERTIFICATE:
407 | $msg = "Could not verify MomoApi's SSL certificate. Please make sure "
408 | . "that your network is not intercepting certificates. "
409 | . "(Try going to $url in your browser.) "
410 | . "If this problem persists,";
411 | break;
412 | default:
413 | $msg = "Unexpected error communicating with MomoApi. "
414 | . "If this problem persists,";
415 | }
416 | $msg .= " let us know at mossplix@gmail.com
417 |
418 | .";
419 |
420 | $msg .= "\n\n(Network error [errno $errno]: $message)";
421 |
422 | if ($numRetries > 0) {
423 | $msg .= "\n\nRequest was retried $numRetries times.";
424 | }
425 |
426 | throw new Error\ApiConnection($msg);
427 | }
428 |
429 | /**
430 | * Checks if a list of headers contains a specific header name.
431 | *
432 | * @param string[] $headers
433 | * @param string $name
434 | * @return boolean
435 | */
436 | private function hasHeader($headers, $name)
437 | {
438 | foreach ($headers as $header) {
439 | if (strncasecmp($header, "{$name}: ", strlen($name) + 2) === 0) {
440 | return true;
441 | }
442 | }
443 |
444 | return false;
445 | }
446 | }
447 |
--------------------------------------------------------------------------------