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
12 changes: 12 additions & 0 deletions e2e_tests/authorization_request_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def test_card_transaction_authorization_request_dto():
"createdAt": "2021-06-22T13:39:17.018Z",
"amount": 2500,
"status": "Pending",
"outcome": "WaitTimeout",
"partialApprovalAllowed": False,
"merchant": {
"name": "Apple Inc.",
Expand Down Expand Up @@ -84,6 +85,7 @@ def test_card_transaction_authorization_request_dto():
assert authorization_request.id == data["id"]
assert authorization_request.type == data["type"]
assert authorization_request.attributes.get("cardNetwork") == data["attributes"]["cardNetwork"]
assert authorization_request.attributes.get("outcome") == data["attributes"]["outcome"]


def test_atm_authorization_request_dto():
Expand All @@ -94,6 +96,7 @@ def test_atm_authorization_request_dto():
"createdAt": "2021-06-22T13:39:17.018Z",
"amount": 2500,
"status": "Pending",
"outcome": "PostTimeout",
"partialApprovalAllowed": False,
"direction": "Debit",
"atmName": "HOME FED SAV BK",
Expand Down Expand Up @@ -129,6 +132,7 @@ def test_atm_authorization_request_dto():
assert authorization_request.id == data["id"]
assert authorization_request.type == data["type"]
assert authorization_request.attributes.get("cardNetwork") == data["attributes"]["cardNetwork"]
assert authorization_request.attributes.get("outcome") == data["attributes"]["outcome"]


def test_purchase_authorization_request_dto():
Expand All @@ -139,6 +143,7 @@ def test_purchase_authorization_request_dto():
"createdAt": "2021-06-22T13:39:17.018Z",
"amount": 2500,
"status": "Pending",
"outcome": "Approved",
"partialApprovalAllowed": False,
"merchant": {
"name": "Apple Inc.",
Expand Down Expand Up @@ -179,6 +184,13 @@ def test_purchase_authorization_request_dto():
}
}

authorization_request = DtoDecoder.decode(data)
assert type(authorization_request) is PurchaseAuthorizationRequestDTO
assert authorization_request.id == data["id"]
assert authorization_request.type == data["type"]
assert authorization_request.attributes.get("cardNetwork") == data["attributes"]["cardNetwork"]
assert authorization_request.attributes.get("outcome") == data["attributes"]["outcome"]


#
# def test_decline_request():
Expand Down
1 change: 1 addition & 0 deletions e2e_tests/authorization_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def test_authorization_api_response():
"cardNetwork": "Visa",
"paymentMethod": "Swipe",
"digitalWallet": "Google",
"cardDecisionSource": "Org",
"cashWithdrawalAmount": 150
},
"relationships": {
Expand Down
12 changes: 9 additions & 3 deletions e2e_tests/transaction_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ def test_card_transaction():
"cardVerificationData": {
"verificationMethod": "CVV2"
},
"cardNetwork": "Visa"
"cardNetwork": "Visa",
"cardDecisionSource": "IssuerStandIn"
},
"relationships": {
"account": {
Expand Down Expand Up @@ -233,6 +234,7 @@ def test_card_transaction():
assert transaction.attributes["recurring"] is False
assert transaction.attributes["paymentMethod"] == "Contactless"
assert transaction.attributes["cardNetwork"] == "Visa"
assert transaction.attributes["cardDecisionSource"] == "IssuerStandIn"
assert transaction.attributes["digitalWallet"] == "Apple"
assert transaction.attributes["cardVerificationData"]["verificationMethod"] == "CVV2"

Expand All @@ -252,7 +254,8 @@ def test_atm_transaction():
"atmLocation": "Masontown, PA 15461",
"surcharge": 10,
"interchange": 15.2,
"cardNetwork": "Allpoint"
"cardNetwork": "Allpoint",
"cardDecisionSource": "TimeoutApprove"
},
"relationships": {
"account": {
Expand Down Expand Up @@ -287,6 +290,7 @@ def test_atm_transaction():
assert transaction.attributes["surcharge"] == 10
assert transaction.attributes["interchange"] == 15.2
assert transaction.attributes["cardNetwork"] == "Allpoint"
assert transaction.attributes["cardDecisionSource"] == "TimeoutApprove"

def test_purchase_transaction():
purchase_transaction_api_response = {
Expand Down Expand Up @@ -318,7 +322,8 @@ def test_purchase_transaction():
"cardVerificationData": {
"verificationMethod": "CVV2"
},
"cardNetwork": "Visa"
"cardNetwork": "Visa",
"cardDecisionSource": "Unit"
},
"relationships": {
"account": {
Expand Down Expand Up @@ -361,6 +366,7 @@ def test_purchase_transaction():
assert transaction.attributes["recurring"] is False
assert transaction.attributes["cardPresent"] is True
assert transaction.attributes["cardNetwork"] == "Visa"
assert transaction.attributes["cardDecisionSource"] == "Unit"
assert transaction.attributes["digitalWallet"] == "Apple"
assert transaction.attributes["paymentMethod"] == "Contactless"

Expand Down
10 changes: 7 additions & 3 deletions unit/models/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from unit.utils import date_utils

AuthorizationStatus = Literal["Authorized", "Completed", "Canceled", "Declined"]
CardDecisionSource = Literal["Org", "Unit", "Network", "TimeoutApprove", "TimeoutDecline", "DefaultApprove",
"InternalError", "IssuerStandIn"]


class AuthorizationDTO(object):
Expand All @@ -14,7 +16,8 @@ def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digit
card_network: Optional[str], tags: Optional[Dict[str, str]],
relationships: Optional[Dict[str, Relationship]], merchant_id: Optional[str],
decline_reason: Optional[str], cash_withdrawal_amount: Optional[int], summary: Optional[str],
currency_conversion: Optional[CurrencyConversion], rich_merchant_data: Optional[RichMerchantData]
currency_conversion: Optional[CurrencyConversion], rich_merchant_data: Optional[RichMerchantData],
card_decision_source: Optional[CardDecisionSource] = None
):
self.id = id
self.type = "authorization"
Expand All @@ -26,7 +29,7 @@ def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digit
"cardVerificationData": card_verification_data, "cardNetwork": card_network, "tags": tags,
"declineReason": decline_reason, "cashWithdrawalAmount": cash_withdrawal_amount,
"summary": summary, "currencyConversion": currency_conversion,
"richMerchantData": rich_merchant_data}
"richMerchantData": rich_merchant_data, "cardDecisionSource": card_decision_source}
self.relationships = relationships

@staticmethod
Expand All @@ -41,7 +44,8 @@ def from_json_api(_id, _type, attributes, relationships):
attributes.get("declineReason"), attributes.get("cashWithdrawalAmount"),
attributes.get("summary"),
CurrencyConversion.from_json_api(attributes.get("currencyConversion")),
RichMerchantData.from_json_api(attributes.get("richMerchantData"))
RichMerchantData.from_json_api(attributes.get("richMerchantData")),
attributes.get("cardDecisionSource")
)


Expand Down
2 changes: 2 additions & 0 deletions unit/models/authorization_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from unit.utils import date_utils

PurchaseAuthorizationRequestStatus = Literal["Pending", "Approved", "Declined"]
AuthorizationRequestOutcome = Literal["Approved", "Declined", "PostTimeout", "PostError", "WaitTimeout"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused type alias AuthorizationRequestOutcome never referenced

Low Severity

AuthorizationRequestOutcome is defined as a Literal type alias but is never used in any type annotation. The outcome field in BaseAuthorizationRequest is stored via attributes.get("outcome") without referencing this type. Compare this with CardDecisionSource in authorization.py, which is actually used as a type hint for card_decision_source in AuthorizationDTO.__init__.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 692f9fa. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is also true for the PurchaseAuthorizationRequestStatus 🤷 this is an SDK that can be imported by others, so those literals can be used by SDK users, just following w/e established pattern Unit defined. The AuthorizationRequest object is not handled the same way the authorization/transaction objects are.

DeclineReason = Literal["AccountClosed", "CardExceedsAmountLimit", "DoNotHonor", "InsufficientFunds", "InvalidMerchant",
"ReferToCardIssuer", "RestrictedCard", "Timeout", "TransactionNotPermittedToCardholder"]

Expand All @@ -19,6 +20,7 @@ def __init__(self, id: str, type: str, attributes: Dict[str, object],
self.type = type
self.attributes = {"createdAt": date_utils.to_datetime(attributes["createdAt"]), "amount": attributes["amount"],
"status": attributes["status"],
"outcome": attributes.get("outcome"),
"partialApprovalAllowed": attributes.get("partialApprovalAllowed"),
"approvedAmount": attributes.get("approvedAmount"),
"declineReason": attributes.get("declineReason"),
Expand Down
19 changes: 13 additions & 6 deletions unit/models/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b
card_network: Optional[str], tags: Optional[Dict[str, str]],
relationships: Optional[Dict[str, Relationship]], gross_interchange: Optional[str],
cash_withdrawal_amount: Optional[int], currency_conversion: Optional[CurrencyConversion],
rich_merchant_data: Optional[RichMerchantData]):
rich_merchant_data: Optional[RichMerchantData], card_decision_source: Optional[str] = None):
BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships)
self.type = 'purchaseTransaction'
self.attributes["cardLast4Digits"] = card_last_4_digits
Expand All @@ -153,6 +153,7 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b
self.attributes["cashWithdrawalAmount"] = cash_withdrawal_amount
self.attributes["currencyConversion"] = currency_conversion
self.attributes["richMerchantData"] = rich_merchant_data
self.attributes["cardDecisionSource"] = card_decision_source

@staticmethod
def from_json_api(_id, _type, attributes, relationships):
Expand All @@ -165,15 +166,16 @@ def from_json_api(_id, _type, attributes, relationships):
attributes.get("cardVerificationData"), attributes.get("cardNetwork"), attributes.get("tags"),
relationships, attributes.get("grossInterchange"), attributes.get("cashWithdrawalAmount"),
CurrencyConversion.from_json_api(attributes.get("currencyConversion")),
RichMerchantData.from_json_api(attributes.get("richMerchantData")))
RichMerchantData.from_json_api(attributes.get("richMerchantData")), attributes.get("cardDecisionSource"))


class AtmTransactionDTO(BaseTransactionDTO):
def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int,
summary: str, card_last_4_digits: str, atm_name: str, atm_location: Optional[str], surcharge: int,
interchange: Optional[int], card_network: Optional[str],
tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]],
gross_interchange: Optional[str], currency_conversion: Optional[CurrencyConversion]):
gross_interchange: Optional[str], currency_conversion: Optional[CurrencyConversion],
card_decision_source: Optional[str] = None):
BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships)
self.type = 'atmTransaction'
self.attributes["cardLast4Digits"] = card_last_4_digits
Expand All @@ -184,6 +186,7 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b
self.attributes["cardNetwork"] = card_network
self.attributes["grossInterchange"] = gross_interchange
self.attributes["currencyConversion"] = currency_conversion
self.attributes["cardDecisionSource"] = card_decision_source

@staticmethod
def from_json_api(_id, _type, attributes, relationships):
Expand All @@ -192,7 +195,8 @@ def from_json_api(_id, _type, attributes, relationships):
attributes["cardLast4Digits"], attributes["atmName"], attributes.get("atmLocation"),
attributes["surcharge"], attributes.get("interchange"), attributes.get("cardNetwork"),
attributes.get("tags"), relationships, attributes.get("grossInterchange"),
CurrencyConversion.from_json_api(attributes.get("currencyConversion")))
CurrencyConversion.from_json_api(attributes.get("currencyConversion")),
attributes.get("cardDecisionSource"))


class FeeTransactionDTO(BaseTransactionDTO):
Expand All @@ -214,7 +218,8 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b
interchange: Optional[int], payment_method: Optional[str], digital_wallet: Optional[str],
card_verification_data: Optional[Dict], card_network: Optional[str], tags: Optional[Dict[str, str]],
relationships: Optional[Dict[str, Relationship]], gross_interchange: Optional[str],
currency_conversion: Optional[CurrencyConversion], rich_merchant_data: Optional[RichMerchantData]):
currency_conversion: Optional[CurrencyConversion], rich_merchant_data: Optional[RichMerchantData],
card_decision_source: Optional[str] = None):
BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships)
self.type = 'cardTransaction'
self.attributes["cardLast4Digits"] = card_last_4_digits
Expand All @@ -228,6 +233,7 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b
self.attributes["grossInterchange"] = gross_interchange
self.attributes["currencyConversion"] = currency_conversion
self.attributes["richMerchantData"] = rich_merchant_data
self.attributes["cardDecisionSource"] = card_decision_source

@staticmethod
def from_json_api(_id, _type, attributes, relationships):
Expand All @@ -239,7 +245,8 @@ def from_json_api(_id, _type, attributes, relationships):
attributes.get("cardVerificationData"), attributes.get("cardNetwork"),
attributes.get("tags"), relationships, attributes.get("grossInterchange"),
CurrencyConversion.from_json_api(attributes.get("currencyConversion")),
RichMerchantData.from_json_api(attributes.get("richMerchantData"))
RichMerchantData.from_json_api(attributes.get("richMerchantData")),
attributes.get("cardDecisionSource")
)


Expand Down
Loading