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