|
7 | 7 |
|
8 | 8 | """ |
9 | 9 |
|
10 | | - |
11 | 10 | import ipaddress |
12 | 11 | import re |
13 | 12 | import uuid |
|
17 | 16 |
|
18 | 17 | from email_validator import validate_email # type: ignore |
19 | 18 | from voluptuous import All, Any, In, Match, Range, Required, Schema |
20 | | -from voluptuous.error import UrlInvalid |
| 19 | +from voluptuous.error import LengthInvalid, UrlInvalid |
21 | 20 |
|
22 | 21 | # Pylint doesn't like the private function type naming for the callable |
23 | 22 | # objects below. Given the consistent use of them, the current names seem |
@@ -250,9 +249,9 @@ def _hostname(hostname: str) -> str: |
250 | 249 |
|
251 | 250 | _single_char = Match("^[A-Za-z0-9]$") |
252 | 251 |
|
253 | | -_iin = Match("^[0-9]{6}$") |
| 252 | +_iin = Match("^(?:[0-9]{6}|[0-9]{8})$") |
254 | 253 |
|
255 | | -_credit_card_last_4 = Match("^[0-9]{4}$") |
| 254 | +_credit_card_last_digits = Match("^(?:[0-9]{2}|[0-9]{4})$") |
256 | 255 |
|
257 | 256 |
|
258 | 257 | def _credit_card_token(s: str) -> str: |
@@ -292,67 +291,94 @@ def _uri(s: str) -> str: |
292 | 291 | return s |
293 | 292 |
|
294 | 293 |
|
| 294 | +def _validate_last_digits(req): |
| 295 | + cc = req.get("credit_card") |
| 296 | + if cc is None: |
| 297 | + return |
| 298 | + |
| 299 | + iin = cc.get("issuer_id_number") |
| 300 | + if iin is None: |
| 301 | + return |
| 302 | + |
| 303 | + if iin and len(iin) == 8: |
| 304 | + last_digits = cc.get("last_digits") |
| 305 | + last_4_digits = cc.get("last_4_digits") |
| 306 | + if last_digits and len(last_digits) != 2: |
| 307 | + raise LengthInvalid( |
| 308 | + "last_digits must be two digits when the issuer_id_number is eight digits." |
| 309 | + ) |
| 310 | + if last_4_digits and len(last_4_digits) != 2: |
| 311 | + raise LengthInvalid( |
| 312 | + "last_4_digits must be two digits when the issuer_id_number is eight digits." |
| 313 | + ) |
| 314 | + return |
| 315 | + |
| 316 | + |
295 | 317 | validate_transaction = Schema( |
296 | | - { |
297 | | - "account": { |
298 | | - "user_id": str, |
299 | | - "username_md5": _md5, |
300 | | - }, |
301 | | - "billing": _address, |
302 | | - "payment": { |
303 | | - "processor": _payment_processor, |
304 | | - "was_authorized": bool, |
305 | | - "decline_code": str, |
306 | | - }, |
307 | | - "credit_card": { |
308 | | - "avs_result": _single_char, |
309 | | - "bank_name": str, |
310 | | - "bank_phone_country_code": _telephone_country_code, |
311 | | - "bank_phone_number": str, |
312 | | - "cvv_result": _single_char, |
313 | | - "issuer_id_number": _iin, |
314 | | - "last_4_digits": _credit_card_last_4, |
315 | | - "token": _credit_card_token, |
316 | | - "was_3d_secure_successful": bool, |
317 | | - }, |
318 | | - "custom_inputs": {_custom_input_key: _custom_input_value}, |
319 | | - "device": { |
320 | | - "accept_language": str, |
321 | | - "ip_address": _ip_address, |
322 | | - "session_age": All(_any_number, Range(min=0)), |
323 | | - "session_id": str, |
324 | | - "user_agent": str, |
325 | | - }, |
326 | | - "email": { |
327 | | - "address": _email_or_md5, |
328 | | - "domain": _hostname, |
329 | | - }, |
330 | | - "event": { |
331 | | - "shop_id": str, |
332 | | - "time": _rfc3339_datetime, |
333 | | - "type": _event_type, |
334 | | - "transaction_id": str, |
335 | | - }, |
336 | | - "order": { |
337 | | - "affiliate_id": str, |
338 | | - "amount": _price, |
339 | | - "currency": _currency_code, |
340 | | - "discount_code": str, |
341 | | - "has_gift_message": bool, |
342 | | - "is_gift": bool, |
343 | | - "referrer_uri": _uri, |
344 | | - "subaffiliate_id": str, |
345 | | - }, |
346 | | - "shipping": _shipping_address, |
347 | | - "shopping_cart": [ |
348 | | - { |
349 | | - "category": str, |
350 | | - "item_id": str, |
351 | | - "price": _price, |
352 | | - "quantity": All(int, Range(min=1)), |
| 318 | + All( |
| 319 | + { |
| 320 | + "account": { |
| 321 | + "user_id": str, |
| 322 | + "username_md5": _md5, |
353 | 323 | }, |
354 | | - ], |
355 | | - }, |
| 324 | + "billing": _address, |
| 325 | + "payment": { |
| 326 | + "processor": _payment_processor, |
| 327 | + "was_authorized": bool, |
| 328 | + "decline_code": str, |
| 329 | + }, |
| 330 | + "credit_card": { |
| 331 | + "avs_result": _single_char, |
| 332 | + "bank_name": str, |
| 333 | + "bank_phone_country_code": _telephone_country_code, |
| 334 | + "bank_phone_number": str, |
| 335 | + "cvv_result": _single_char, |
| 336 | + "issuer_id_number": _iin, |
| 337 | + "last_digits": _credit_card_last_digits, |
| 338 | + "last_4_digits": _credit_card_last_digits, |
| 339 | + "token": _credit_card_token, |
| 340 | + "was_3d_secure_successful": bool, |
| 341 | + }, |
| 342 | + "custom_inputs": {_custom_input_key: _custom_input_value}, |
| 343 | + "device": { |
| 344 | + "accept_language": str, |
| 345 | + "ip_address": _ip_address, |
| 346 | + "session_age": All(_any_number, Range(min=0)), |
| 347 | + "session_id": str, |
| 348 | + "user_agent": str, |
| 349 | + }, |
| 350 | + "email": { |
| 351 | + "address": _email_or_md5, |
| 352 | + "domain": _hostname, |
| 353 | + }, |
| 354 | + "event": { |
| 355 | + "shop_id": str, |
| 356 | + "time": _rfc3339_datetime, |
| 357 | + "type": _event_type, |
| 358 | + "transaction_id": str, |
| 359 | + }, |
| 360 | + "order": { |
| 361 | + "affiliate_id": str, |
| 362 | + "amount": _price, |
| 363 | + "currency": _currency_code, |
| 364 | + "discount_code": str, |
| 365 | + "has_gift_message": bool, |
| 366 | + "is_gift": bool, |
| 367 | + "referrer_uri": _uri, |
| 368 | + "subaffiliate_id": str, |
| 369 | + }, |
| 370 | + "shipping": _shipping_address, |
| 371 | + "shopping_cart": [ |
| 372 | + { |
| 373 | + "category": str, |
| 374 | + "item_id": str, |
| 375 | + "price": _price, |
| 376 | + "quantity": All(int, Range(min=1)), |
| 377 | + }, |
| 378 | + ], |
| 379 | + }, |
| 380 | + _validate_last_digits, |
| 381 | + ) |
356 | 382 | ) |
357 | 383 |
|
358 | 384 |
|
|
0 commit comments