diff --git a/venmo_api/apis/payment_api.py b/venmo_api/apis/payment_api.py index bf95493..ea078a9 100644 --- a/venmo_api/apis/payment_api.py +++ b/venmo_api/apis/payment_api.py @@ -3,6 +3,8 @@ User, PaymentMethod, PaymentRole, PaymentPrivacy, deserialize, wrap_callback, get_user_id from typing import List, Union +from venmo_api.models.eligibility_token import EligibilityToken + class PaymentApi(object): @@ -155,6 +157,38 @@ def request_money(self, amount: float, target_user=target_user, callback=callback) + def __get_eligibility_token(self, amount: float, note: str, target_id: int = None, funding_source_id: str = None, + action: str = "pay", + country_code: str = "1", target_type: str = "user_id", callback=None): + """ + Generate eligibility token which is needed in payment requests + :param amount: amount of money to be requested + :param note: message/note of the transaction + :param target_id: the user id of the person you are sending money to + :param funding_source_id: Your payment_method id for this payment + :param action: action that eligibility token is used for + :param country_code: country code, not sure what this is for + :param target_type: set by default to user_id, but there are probably other target types + """ + resource_path = '/protection/eligibility' + body = { + "funding_source_id": self.get_default_payment_method().id if not funding_source_id else funding_source_id, + "action": action, + "country_code": country_code, + "target_type": target_type, + "note": note, + "target_id": get_user_id(user=None, user_id=target_id), + "amount": amount, + } + + response = self.__api_client.call_api(resource_path=resource_path, + body=body, + method='POST') + if callback: + return + + return deserialize(response=response, data_type=EligibilityToken) + def __update_payment(self, action, payment_id): if not payment_id: @@ -198,6 +232,7 @@ def __send_or_request_money(self, amount: float, funding_source_id: str = None, privacy_setting: str = PaymentPrivacy.PRIVATE.value, target_user_id: int = None, target_user: User = None, + eligibility_token: str = None, callback=None) -> Union[bool, None]: """ Generic method for sending and requesting money @@ -208,6 +243,7 @@ def __send_or_request_money(self, amount: float, :param privacy_setting: :param target_user_id: :param target_user: + :param eligibility_token: :param callback: :return: """ @@ -227,6 +263,10 @@ def __send_or_request_money(self, amount: float, if is_send_money: if not funding_source_id: funding_source_id = self.get_default_payment_method().id + if not eligibility_token: + eligibility_token = self.__get_eligibility_token(amount, note, int(target_user_id)).eligibility_token + + body.update({"eligibility_token": eligibility_token}) body.update({"funding_source_id": funding_source_id}) resource_path = '/payments' diff --git a/venmo_api/models/eligibility_token.py b/venmo_api/models/eligibility_token.py new file mode 100644 index 0000000..112ef9e --- /dev/null +++ b/venmo_api/models/eligibility_token.py @@ -0,0 +1,36 @@ +from venmo_api import BaseModel, JSONSchema +from venmo_api.models.fee import Fee + + +class EligibilityToken(BaseModel): + def __init__(self, eligibility_token, eligible, fees, fee_disclaimer, json=None): + super().__init__() + + self.eligibility_token = eligibility_token + self.eligible = eligible + self.fees = fees + self.fee_disclaimer = fee_disclaimer + self._json = json + + @classmethod + def from_json(cls, json): + """ + Initialize a new eligibility token object from JSON. + :param json: JSON data to parse. + :return: EligibilityToken object. + """ + if not json: + return None + + parser = JSONSchema.eligibility_token(json) + + fees = parser.get_fees() + fee_objects = [Fee.from_json(fee) for fee in fees] if fees else [] + + return cls( + eligibility_token=parser.get_eligibility_token(), + eligible=parser.get_eligible(), + fees=fee_objects, + fee_disclaimer=parser.get_fee_disclaimer(), + json=json + ) diff --git a/venmo_api/models/fee.py b/venmo_api/models/fee.py new file mode 100644 index 0000000..59ef580 --- /dev/null +++ b/venmo_api/models/fee.py @@ -0,0 +1,37 @@ +from venmo_api import BaseModel, JSONSchema + + +class Fee(BaseModel): + def __init__(self, product_uri, applied_to, base_fee_amount, fee_percentage, calculated_fee_amount_in_cents, + fee_token, json=None): + super().__init__() + + self.product_uri = product_uri + self.applied_to = applied_to + self.base_fee_amount = base_fee_amount + self.fee_percentage = fee_percentage + self.calculated_fee_amount_in_cents = calculated_fee_amount_in_cents + self.fee_token = fee_token + self._json = json + + @classmethod + def from_json(cls, json): + """ + Initialize a new Fee object from JSON using the FeeParser. + :param json: JSON data to parse. + :return: Fee object. + """ + if not json: + return None + + parser = JSONSchema.fee(json) + + return cls( + product_uri=parser.get_product_uri(), + applied_to=parser.get_applied_to(), + base_fee_amount=parser.get_base_fee_amount(), + fee_percentage=parser.get_fee_percentage(), + calculated_fee_amount_in_cents=parser.get_calculated_fee_amount_in_cents(), + fee_token=parser.get_fee_token(), + json=json + ) diff --git a/venmo_api/models/json_schema.py b/venmo_api/models/json_schema.py index 6d3dab8..6f74d27 100644 --- a/venmo_api/models/json_schema.py +++ b/venmo_api/models/json_schema.py @@ -24,6 +24,14 @@ def comment(json): def mention(json): return MentionParser(json) + @staticmethod + def eligibility_token(json): + return EligibilityTokenParser(json) + + @staticmethod + def fee(json): + return FeeParser(json) + class TransactionParser: @@ -324,3 +332,57 @@ def get_user(self): "username": "username", "user": "user" } + +class EligibilityTokenParser: + def __init__(self, json): + self.json = json + + def get_eligibility_token(self): + return self.json.get(eligibility_token_json_format['eligibility_token']) + + def get_eligible(self): + return self.json.get(eligibility_token_json_format['eligible']) + + def get_fees(self): + return self.json.get(eligibility_token_json_format['fees']) + + def get_fee_disclaimer(self): + return self.json.get(eligibility_token_json_format['fee_disclaimer']) + +eligibility_token_json_format = { + 'eligibility_token': 'eligibility_token', + 'eligible': 'eligible', + 'fees': 'fees', + 'fee_disclaimer': 'fee_disclaimer' +} + +class FeeParser: + def __init__(self, json): + self.json = json + + def get_product_uri(self): + return self.json.get(fee_json_format['product_uri']) + + def get_applied_to(self): + return self.json.get(fee_json_format['applied_to']) + + def get_base_fee_amount(self): + return self.json.get(fee_json_format['base_fee_amount']) + + def get_fee_percentage(self): + return self.json.get(fee_json_format['fee_percentage']) + + def get_calculated_fee_amount_in_cents(self): + return self.json.get(fee_json_format['calculated_fee_amount_in_cents']) + + def get_fee_token(self): + return self.json.get(fee_json_format['fee_token']) + +fee_json_format = { + 'product_uri': 'product_uri', + 'applied_to': 'applied_to', + 'base_fee_amount': 'base_fee_amount', + 'fee_percentage': 'fee_percentage', + 'calculated_fee_amount_in_cents': 'calculated_fee_amount_in_cents', + 'fee_token': 'fee_token' +} \ No newline at end of file diff --git a/venmo_api/utils/api_client.py b/venmo_api/utils/api_client.py index b06aff7..719c786 100644 --- a/venmo_api/utils/api_client.py +++ b/venmo_api/utils/api_client.py @@ -21,7 +21,7 @@ def __init__(self, access_token=None): self.access_token = access_token self.configuration = {"host": "https://api.venmo.com/v1"} - self.default_headers = {"User-Agent": "Venmo/7.44.0 (iPhone; iOS 13.0; Scale/2.0)"} + self.default_headers = {"User-Agent": "Venmo/10.50.0 (iPhone; iOS 18.0; Scale/3.0)"} if self.access_token: self.default_headers.update({"Authorization": self.access_token})