diff --git a/bfxapi/labeler.py b/bfxapi/labeler.py index 02b6699..213752c 100644 --- a/bfxapi/labeler.py +++ b/bfxapi/labeler.py @@ -14,12 +14,15 @@ def compose(*decorators): def partial(cls): def __init__(self, **kwargs): - for key, value in kwargs.items(): - self.__setattr__(key, value) - for annotation in self.__annotations__.keys(): if annotation not in kwargs: self.__setattr__(annotation, None) + else: self.__setattr__(annotation, kwargs[annotation]) + + kwargs.pop(annotation, None) + + if len(kwargs) != 0: + raise TypeError(f"{cls.__name__}.__init__() got an unexpected keyword argument '{list(kwargs.keys())[0]}'") cls.__init__ = __init__ diff --git a/bfxapi/rest/endpoints/rest_merchant_endpoints.py b/bfxapi/rest/endpoints/rest_merchant_endpoints.py index 3c6f5c7..dd760c5 100644 --- a/bfxapi/rest/endpoints/rest_merchant_endpoints.py +++ b/bfxapi/rest/endpoints/rest_merchant_endpoints.py @@ -26,10 +26,10 @@ class RestMerchantEndpoints(Middleware): return InvoiceSubmission.parse(data) def get_invoices(self, id: Optional[str] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> List[InvoiceSubmission]: - return [ InvoiceSubmission.parse(sub_data) for sub_data in self._POST("auth/r/ext/pay/invoices", body={ + return [ InvoiceSubmission.parse(sub_data) for sub_data in to_snake_case_keys(self._POST("auth/r/ext/pay/invoices", body={ "id": id, "start": start, "end": end, "limit": limit - }) ] + })) ] def get_invoice_count_stats(self, status: Literal["CREATED", "PENDING", "COMPLETED", "EXPIRED"], format: str) -> List[InvoiceStats]: return [ InvoiceStats(**sub_data) for sub_data in self._POST("auth/r/ext/pay/invoice/stats/count", body={ "status": status, "format": format }) ] @@ -38,13 +38,13 @@ class RestMerchantEndpoints(Middleware): return [ InvoiceStats(**sub_data) for sub_data in self._POST("auth/r/ext/pay/invoice/stats/earning", body={ "currency": currency, "format": format }) ] def complete_invoice(self, id: str, pay_currency: str, deposit_id: Optional[int] = None, ledger_id: Optional[int] = None) -> InvoiceSubmission: - return InvoiceSubmission.parse(self._POST("auth/w/ext/pay/invoice/complete", body={ + return InvoiceSubmission.parse(to_snake_case_keys(self._POST("auth/w/ext/pay/invoice/complete", body={ "id": id, "payCcy": pay_currency, "depositId": deposit_id, "ledgerId": ledger_id - })) + }))) def expire_invoice(self, id: str) -> InvoiceSubmission: - return InvoiceSubmission.parse(self._POST("auth/w/ext/pay/invoice/expire", body={ "id": id })) + return InvoiceSubmission.parse(to_snake_case_keys(self._POST("auth/w/ext/pay/invoice/expire", body={ "id": id }))) def get_currency_conversion_list(self) -> List[CurrencyConversion]: return [ diff --git a/bfxapi/rest/types.py b/bfxapi/rest/types.py index 3bd12e8..b0c7270 100644 --- a/bfxapi/rest/types.py +++ b/bfxapi/rest/types.py @@ -569,63 +569,54 @@ class DerivativePositionCollateralLimits(_Type): class InvoiceSubmission(_Type): id: str t: int - merchant_name: str type: Literal["ECOMMERCE", "POS"] duration: int amount: float currency: str order_id: str pay_currencies: List[str] + webhook: Optional[str] + redirect_url: Optional[str] status: Literal["CREATED", "PENDING", "COMPLETED", "EXPIRED"] - customer_info: Optional["CustomerInfo"] - payment: Optional["Payment"] - invoices: List["Invoice"] + customer_info: Optional["_CustomerInfo"] + invoices: List["_Invoice"] + payment: Optional["_Payment"] + additional_payments: Optional[List["_Payment"]] + + merchant_name: str @classmethod def parse(cls, data: Dict[str, Any]) -> "InvoiceSubmission": if "customer_info" in data and data["customer_info"] != None: - data["customer_info"] = CustomerInfo(**data["customer_info"]) + data["customer_info"] = _CustomerInfo(**data["customer_info"]) + + for index, invoice in enumerate(data["invoices"]): + data["invoices"][index] = _Invoice(**invoice) if "payment" in data and data["payment"] != None: - data["payment"] = Payment(**data["payment"]) + data["payment"] = _Payment(**data["payment"]) - if "invoices" in data and data["invoices"] != None: - for index, invoice in enumerate(data["invoices"]): - data["invoices"][index] = Invoice(**invoice) + if "additional_payments" in data and data["additional_payments"] != None: + for index, additional_payment in enumerate(data["additional_payments"]): + data["additional_payments"][index] = _Payment(**additional_payment) return InvoiceSubmission(**data) @compose(dataclass, partial) -class CustomerInfo(_Type): +class _CustomerInfo: nationality: str resid_country: str + resid_state: Optional[str] resid_city: str resid_zip_code: str resid_street: str - resid_building_no: str + resid_building_no: Optional[str] full_name: str email: str tos_accepted: bool @compose(dataclass, partial) -class Payment(_Type): - transaction_id: str - amount: str - currency: str - method: str - status: str - confirmations: int - created: str - updated: str - deposit_id: int - ledger_id: int - force_completed: bool - amount_diff: str - additional_payments: JSON - additional_payment: JSON - -@compose(dataclass, partial) -class Invoice(_Type): +class _Invoice: amount: float currency: str pay_currency: str @@ -633,6 +624,21 @@ class Invoice(_Type): address: str ext: JSON +@compose(dataclass, partial) +class _Payment: + txid: str + amount: float + currency: str + method: str + status: Literal["CREATED", "COMPLETED", "PROCESSING"] + confirmations: int + created_at: str + updated_at: str + deposit_id: Optional[int] + ledger_id: Optional[int] + force_completed: Optional[bool] + amount_diff: Optional[str] + @dataclass class InvoiceStats(_Type): time: str diff --git a/bfxapi/utils/camel_and_snake_case_adapters.py b/bfxapi/utils/camel_and_snake_case_adapters.py index 85ccdf4..7255940 100644 --- a/bfxapi/utils/camel_and_snake_case_adapters.py +++ b/bfxapi/utils/camel_and_snake_case_adapters.py @@ -15,8 +15,8 @@ def _scheme(data: T, adapter: Callable[[str], str]) -> T: return cast(T, { adapter(key): _scheme(value, adapter) for key, value in data.items() }) else: return data -def to_snake_case_keys(dictionary: Dict[str, Any]) -> Dict[str, Any]: +def to_snake_case_keys(dictionary: T) -> T: return _scheme(dictionary, _to_snake_case) -def to_camel_case_keys(dictionary: Dict[str, Any]) -> Dict[str, Any]: +def to_camel_case_keys(dictionary: T) -> T: return _scheme(dictionary, _to_camel_case) \ No newline at end of file