1 <?php
2
3 namespace MaxMind;
4
5 use MaxMind\Exception\AuthenticationException;
6 use MaxMind\Exception\HttpException;
7 use MaxMind\Exception\InsufficientFundsException;
8 use MaxMind\Exception\InvalidInputException;
9 use MaxMind\Exception\InvalidRequestException;
10 use MaxMind\Exception\WebServiceException;
11 use MaxMind\MinFraud\Validation;
12 use MaxMind\WebService\Client;
13 use Respect\Validation\Exceptions\ValidationException;
14
15 /**
16 * This class provides a client API for accessing MaxMind minFraud Score
17 * and Insights.
18 *
19 * ## Usage ##
20 *
21 * The constructor takes your MaxMind user ID and license key. The object
22 * returned is immutable. To build up a request, call the `->with*()` methods.
23 * Each of these returns a new object (a clone of the original) with the
24 * additional data. These can be chained together:
25 *
26 * ```
27 * $client = new MinFraud(6, 'LICENSE_KEY');
28 *
29 * $score = $client->withDevice(['ip_address' => '1.1.1.1',
30 * 'accept_language' => 'en-US'])
31 * ->withEmail(['domain' => 'maxmind.com'])
32 * ->score();
33 * ```
34 *
35 * If the request fails, an exception is thrown.
36 */
37 class MinFraud
38 {
39 const VERSION = 'v1.3.0';
40
41 private $client;
42 private static $host = 'minfraud.maxmind.com';
43
44 private static $basePath = '/minfraud/v2.0/';
45 private $content;
46 private $locales;
47 private $validateInput = true;
48
49 /**
50 * @param int $userId Your MaxMind user ID
51 * @param string $licenseKey Your MaxMind license key
52 * @param array $options An array of options. Possible keys:
53 *
54 * * `host` - The host to use when connecting to the web service.
55 * * `userAgent` - The prefix for the User-Agent header to use in the
56 * request.
57 * * `caBundle` - The bundle of CA root certificates to use in the request.
58 * * `connectTimeout` - The connect timeout to use for the request.
59 * * `timeout` - The timeout to use for the request.
60 * * `proxy` - The HTTP proxy to use. May include a schema, port,
61 * username, and password, e.g., `http://username:password@127.0.0.1:10`.
62 * * `locales` - An array of locale codes to use for the location name
63 * properties.
64 * * `validateInput` - Default is `true`. Determines whether values passed
65 * to the `with*()` methods are validated. It is recommended that you
66 * leave validation on while developing and only (optionally) disable it
67 * before deployment.
68 */
69 public function __construct(
70 $userId,
71 $licenseKey,
72 $options = []
73 ) {
74 if (isset($options['locales'])) {
75 $this->locales = $options['locales'];
76 } else {
77 $this->locales = ['en'];
78 }
79
80 if (isset($options['validateInput'])) {
81 $this->validateInput = $options['validateInput'];
82 }
83
84 if (!isset($options['host'])) {
85 $options['host'] = self::$host;
86 }
87 $options['userAgent'] = $this->userAgent();
88 $this->client = new Client($userId, $licenseKey, $options);
89 }
90
91 /**
92 * This returns a `MinFraud` object with the array to be sent to the web
93 * service set to `$values`. Existing values will be replaced.
94 * @link https://dev.maxmind.com/minfraud/ minFraud API docs
95 *
96 * @param $values
97 * @return MinFraud
98 */
99 public function with($values)
100 {
101 $values = $this->cleanAndValidate('Transaction', $values);
102
103 $new = clone $this;
104 $new->content = $values;
105
106 return $new;
107 }
108
109 /**
110 * This returns a `MinFraud` object with the `device` array set to
111 * `$values`. Existing `device` data will be replaced.
112 * @link https://dev.maxmind.com/minfraud/#Device_device
113 * minFraud device API docs
114 *
115 * @param $values
116 * @return MinFraud
117 */
118 public function withDevice($values)
119 {
120 return $this->validateAndAdd('Device', 'device', $values);
121 }
122
123 /**
124 * This returns a `MinFraud` object with the `events` array set to
125 * `$values`. Existing `event` data will be replaced.
126 * @link https://dev.maxmind.com/minfraud/#Event_event
127 * minFraud event API docs
128 *
129 * @param $values
130 * @return MinFraud
131 */
132 public function withEvent($values)
133 {
134 return $this->validateAndAdd('Event', 'event', $values);
135 }
136
137 /**
138 * This returns a `MinFraud` object with the `account` array set to
139 * `$values`. Existing `account` data will be replaced.
140 * @link https://dev.maxmind.com/minfraud/#Account_account
141 * minFraud account API docs
142 *
143 * @param $values
144 * @return MinFraud
145 */
146 public function withAccount($values)
147 {
148 return $this->validateAndAdd('Account', 'account', $values);
149 }
150
151 /**
152 * This returns a `MinFraud` object with the `email` array set to
153 * `$values`. Existing `email` data will be replaced.
154 * @link https://dev.maxmind.com/minfraud/#Email_email
155 * minFraud email API docs
156 *
157 * @param $values
158 * @return MinFraud
159 */
160 public function withEmail($values)
161 {
162 return $this->validateAndAdd('Email', 'email', $values);
163 }
164
165 /**
166 * This returns a `MinFraud` object with the `billing` array set to
167 * `$values`. Existing `billing` data will be replaced.
168 * @link https://dev.maxmind.com/minfraud/#Billing_billing
169 * minFraud billing API docs
170 *
171 * @param $values
172 * @return MinFraud
173 */
174 public function withBilling($values)
175 {
176 return $this->validateAndAdd('Billing', 'billing', $values);
177 }
178
179 /**
180 * This returns a `MinFraud` object with the `shipping` array set to
181 * `$values`. Existing `shipping` data will be replaced.
182 * @link https://dev.maxmind.com/minfraud/#Shipping_shipping
183 * minFraud shipping API docs
184 *
185 * @param $values
186 * @return MinFraud
187 */
188 public function withShipping($values)
189 {
190 return $this->validateAndAdd('Shipping', 'shipping', $values);
191 }
192
193 /**
194 * This returns a `MinFraud` object with the `payment` array set to
195 * `$values`. Existing `payment` data will be replaced.
196 * @link https://dev.maxmind.com/minfraud/#Payment_payment
197 * minFraud payment API docs
198 *
199 * @param $values
200 * @return MinFraud
201 */
202 public function withPayment($values)
203 {
204 return $this->validateAndAdd('Payment', 'payment', $values);
205 }
206
207 /**
208 * This returns a `MinFraud` object with the `credit_card` array set to
209 * `$values`. Existing `credit_card` data will be replaced.
210 * @link https://dev.maxmind.com/minfraud/#Credit_Card_credit_card
211 * minFraud credit_card API docs
212 *
213 * @param $values
214 * @return MinFraud
215 */
216 public function withCreditCard($values)
217 {
218 return $this->validateAndAdd('CreditCard', 'credit_card', $values);
219 }
220
221 /**
222 * This returns a `MinFraud` object with the `order` array set to
223 * `$values`. Existing `order` data will be replaced.
224 * @link https://dev.maxmind.com/minfraud/#Order_order
225 * minFraud order API docs
226 *
227 * @param $values
228 * @return MinFraud
229 */
230 public function withOrder($values)
231 {
232 return $this->validateAndAdd('Order', 'order', $values);
233 }
234
235 /**
236 * This returns a `MinFraud` object with `$values` added to the shopping
237 * cart array.
238 * @link https://dev.maxmind.com/minfraud/#Shopping_Cart_Item
239 * minFraud shopping cart item API docs
240 *
241 * @param $values
242 * @return MinFraud
243 */
244 public function withShoppingCartItem($values)
245 {
246 $values = $this->cleanAndValidate('ShoppingCartItem', $values);
247
248 $new = clone $this;
249 if (!isset($new->content['shopping_cart'])) {
250 $new->content['shopping_cart'] = [];
251 }
252 array_push($new->content['shopping_cart'], $values);
253
254 return $new;
255 }
256
257 /**
258 * This method performs a minFraud Score lookup using the request data in
259 * the current object and returns a model object for minFraud Score.
260 *
261 * @return MinFraud\Model\Score minFraud Score model object.
262 * @throws InvalidInputException when the request has missing or invalid
263 * data.
264 * @throws AuthenticationException when there is an issue authenticating
265 * the request.
266 * @throws InsufficientFundsException when your account is out of funds.
267 * @throws InvalidRequestException when the request is invalid for some
268 * other reason, e.g., invalid JSON in the POST.
269 * @throws HttpException when an unexpected HTTP error occurs.
270 * @throws WebServiceException when some other error occurs. This also
271 * serves as the base class for the above exceptions.
272 */
273 public function score()
274 {
275 return $this->post('Score');
276 }
277
278 /**
279 * This method performs a minFraud Insights lookup using the request data
280 * in the current object and returns a model object for minFraud Insights.
281 *
282 * @return MinFraud\Model\Insights minFraud Insights model object.
283 * @throws InvalidInputException when the request has missing or invalid
284 * data.
285 * @throws AuthenticationException when there is an issue authenticating
286 * the request.
287 * @throws InsufficientFundsException when your account is out of funds.
288 * @throws InvalidRequestException when the request is invalid for some
289 * other reason, e.g., invalid JSON in the POST.
290 * @throws HttpException when an unexpected HTTP error occurs.
291 * @throws WebServiceException when some other error occurs. This also
292 * serves as the base class for the above exceptions.
293 */
294 public function insights()
295 {
296 return $this->post('Insights');
297 }
298
299 /**
300 * This method performs a minFraud Factors lookup using the request data
301 * in the current object and returns a model object for minFraud Factors.
302 *
303 * @return MinFraud\Model\Factors minFraud Factors model object.
304 * @throws InvalidInputException when the request has missing or invalid
305 * data.
306 * @throws AuthenticationException when there is an issue authenticating
307 * the request.
308 * @throws InsufficientFundsException when your account is out of funds.
309 * @throws InvalidRequestException when the request is invalid for some
310 * other reason, e.g., invalid JSON in the POST.
311 * @throws HttpException when an unexpected HTTP error occurs.
312 * @throws WebServiceException when some other error occurs. This also
313 * serves as the base class for the above exceptions.
314 */
315 public function factors()
316 {
317 return $this->post('Factors');
318 }
319
320 /**
321 * @param $service $service The name of the service to use.
322 * @return mixed The model class for the service.
323 * @throws InvalidInputException when the request has missing or invalid
324 * data.
325 * @throws AuthenticationException when there is an issue authenticating the
326 * request.
327 * @throws InsufficientFundsException when your account is out of funds.
328 * @throws InvalidRequestException when the request is invalid for some
329 * other reason, e.g., invalid JSON in the POST.
330 * @throws HttpException when an unexpected HTTP error occurs.
331 * @throws WebServiceException when some other error occurs. This also
332 * serves as the base class for the above exceptions.
333 */
334 private function post($service)
335 {
336 if (!isset($this->content['device']['ip_address'])) {
337 throw new InvalidInputException(
338 'Key ip_address must be present in device'
339 );
340 }
341 $url = self::$basePath . strtolower($service);
342 $class = "MaxMind\\MinFraud\\Model\\" . $service;
343
344 return new $class(
345 $this->client->post($service, $url, $this->content),
346 $this->locales
347 );
348 }
349
350 /**
351 * @return string The prefix for the User-Agent header.
352 */
353 private function userAgent()
354 {
355 return 'minFraud-API/' . MinFraud::VERSION;
356 }
357
358 /**
359 * @param string $className The name of the class (but not the namespace)
360 * @param string $key The key in the transaction array to set
361 * @param array $values The values to validate
362 * @return MinFraud
363 * @throws InvalidInputException when $values does not validate
364 */
365 private function validateAndAdd($className, $key, $values)
366 {
367 $values = $this->cleanAndValidate($className, $values);
368 $new = clone $this;
369 $new->content[$key] = $values;
370
371 return $new;
372 }
373
374
375 /**
376 * @param string $className The name of the class (but not the namespace)
377 * @param array $values The values to validate
378 * @return array The cleaned values
379 * @throws InvalidInputException when $values does not validate
380 */
381 private function cleanAndValidate($className, $values)
382 {
383 $values = $this->clean($values);
384
385 if (!$this->validateInput) {
386 return $values;
387 }
388
389 $class = '\\MaxMind\\MinFraud\\Validation\\Rules\\' . $className;
390 $validator = new $class();
391 try {
392 $validator->check($values);
393 } catch (ValidationException $exception) {
394 throw new InvalidInputException(
395 $exception->getMessage(),
396 $exception->getCode()
397 );
398 }
399
400 return $values;
401 }
402
403 private function clean($array)
404 {
405 $cleaned = [];
406 foreach ($array as $key => $value) {
407 if (is_array($value)) {
408 $cleaned[$key] = $this->clean($array[$key]);
409 } elseif ($array[$key] !== null) {
410 $cleaned[$key] = $array[$key];
411 }
412 }
413
414 return $cleaned;
415 }
416 }
417