Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ History
* Setuptools has been replaced with the uv build backend for building the
package.
* Added ``securepay`` to the ``/payment/processor`` validation.
* Added ``credit_application`` and ``fund_transfer`` to the ``/event/type``
validation.
* Added the input ``/event/party``. This is the party submitting the
transaction.
* Added the input ``/payment/method``. This is the payment method associated
with the transaction.

3.1.0 (2025-05-23)
++++++++++++++++++
Expand Down
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ Score, Insights and Factors Example
>>> 'user_agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36'
>>> },
>>> 'event': {
>>> 'party': 'customer',
>>> 'shop_id': 's2123',
>>> 'type': 'purchase',
>>> 'transaction_id': 'txn3134133',
Expand Down Expand Up @@ -209,6 +210,7 @@ Score, Insights and Factors Example
>>> 'payment': {
>>> 'decline_code': 'invalid number',
>>> 'was_authorized': False,
>>> 'method': 'card',
>>> 'processor': 'stripe'
>>> },
>>> 'shopping_cart': [{
Expand Down
21 changes: 21 additions & 0 deletions src/minfraud/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ def _hostname(hostname: str) -> str:

_shipping_address["delivery_speed"] = _delivery_speed

_payment_method = In(
[
"bank_debit",
"bank_redirect",
"bank_transfer",
"buy_now_pay_later",
"card",
"crypto",
"digital_wallet",
"gift_card",
"real_time_payment",
"rewards",
],
)

_payment_processor = In(
[
"adyen",
Expand Down Expand Up @@ -281,11 +296,15 @@ def _credit_card_token(s: str) -> str:
)


_event_party = In(["agent", "customer"])

_event_type = In(
[
"account_creation",
"account_login",
"credit_application",
"email_change",
"fund_transfer",
"password_reset",
"payout_change",
"purchase",
Expand Down Expand Up @@ -316,6 +335,7 @@ def _uri(s: str) -> str:
},
"billing": _address,
"payment": {
"method": _payment_method,
"processor": _payment_processor,
"was_authorized": bool,
"decline_code": str,
Expand Down Expand Up @@ -346,6 +366,7 @@ def _uri(s: str) -> str:
"domain": _hostname,
},
"event": {
"party": _event_party,
"shop_id": str,
"time": _rfc3339_datetime,
"type": _event_type,
Expand Down
2 changes: 2 additions & 0 deletions tests/data/full-transaction-request.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"event": {
"party": "customer",
"transaction_id": "txn3134133",
"shop_id": "s2123",
"time": "2014-04-12T23:20:50.052+00:00",
Expand Down Expand Up @@ -41,6 +42,7 @@
"delivery_speed": "same_day"
},
"payment": {
"method": "card",
"processor": "stripe",
"was_authorized": false,
"decline_code": "invalid number"
Expand Down
25 changes: 25 additions & 0 deletions tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@ def test_domain(self) -> None:


class TestEvent(ValidationBase, unittest.TestCase):
def test_party(self) -> None:
for good in ("agent", "customer"):
self.check_transaction({"event": {"party": good}})
for bad in ("bad", 1, ""):
self.check_invalid_transaction({"event": {"party": bad}})

def test_transaction(self) -> None:
self.check_transaction_str_type("event", "transaction_id")

Expand All @@ -324,7 +330,9 @@ def test_type(self) -> None:
for good in (
"account_creation",
"account_login",
"credit_application",
"email_change",
"fund_transfer",
"password_reset",
"payout_change",
"purchase",
Expand Down Expand Up @@ -370,6 +378,23 @@ def test_referrer_uri(self) -> None:


class TestPayment(ValidationBase, unittest.TestCase):
def test_method(self) -> None:
for good in (
"bank_debit",
"bank_redirect",
"bank_transfer",
"buy_now_pay_later",
"card",
"crypto",
"digital_wallet",
"gift_card",
"real_time_payment",
"rewards",
):
self.check_transaction({"payment": {"method": good}})
for bad in ("bad", 1, ""):
self.check_invalid_transaction({"payment": {"method": bad}})

def test_processor(self) -> None:
for good in ("adyen", "stripe"):
self.check_transaction({"payment": {"processor": good}})
Expand Down
Loading