Skip to content
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
- [V2 Endpoints](#v2-endpoints)
- [Nested actions or resources](#nested-actions-or-resources)
- [Entity onboardings](#entity-onboardings)
- [Links](#links)
- [Payouts](#payouts)
- [Payment methods](#payment-methods)
- [Transfer batches](#transfer-batches)
- [Webhook Signature Validation](#webhook-signature-validation)
- [Idempotency Keys](#idempotency-keys)
Expand Down Expand Up @@ -256,13 +259,78 @@ client.v2.entities.onboardings.upload_legal_representative_document(
)
```

Slots that accept more than one file, such as `incorporation_amendments`, take additional
files with `append_document`. Each file is listed in the onboarding's `documents`, and its
`id` lets you delete it:

```python
onboarding = client.v2.entities.onboardings.append_document(
onboarding_id, "incorporation_amendments", "amendment.pdf", entity_id=entity_id
)
onboarding = client.v2.entities.onboardings.delete_document(
onboarding_id,
"incorporation_amendments",
onboarding.documents[0].id,
entity_id=entity_id,
)
```

Once every required field and document is complete, `submittable` turns `True` and the
onboarding can be sent for review:

```python
onboarding = client.v2.entities.onboardings.submit(onboarding_id, entity_id=entity_id)
```

#### Links

After the account holder connects their account through the widget, exchange the
`exchange_token` for the link. The resulting link includes its `link_token`, used to fetch
the link's resources:

```python
link = client.links.exchange("exchange_token_123")
accounts = link.accounts.list()
tax_statements = link.tax_statements.list()
```

#### Payouts

The resources a payout disburses or discounts are nested under the payout. Use
`resource_type` (`payment_intent`, `charge`, `refund` or `refund_adjustment`) to choose
which ones to list:

```python
payouts = client.payouts.list(status="succeeded")
charges = client.payouts.resources.list(
payout_id="po_0ujsswThIGTUYm2K8FjOOfXtY1K", resource_type="charge"
)
```

#### Payment methods

Mexican direct debit mandates are created with a `multipart/form-data` request, so the
consent documents can be paths or binary file-like objects:

```python
payment_method = client.v2.payment_methods.create(
type="mx_direct_debit",
customer="cus_0ujsswThIGTUYm2K8FjOOfXtY1K",
mx_direct_debit={
"account_number": "000000000000000000",
"account_type": "clabe",
"interval": "month",
"max_amount": 100000,
"consent_documents": {
"contract": "contract.pdf",
"selfie": "selfie.jpg",
"id_front": "id_front.jpg",
"id_back": "id_back.jpg",
},
},
)
```

#### Transfer batches

Transfer batches let you send several transfers in a single request. Creating one
Expand Down
9 changes: 6 additions & 3 deletions fintoc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ def _build_jws_header(self, method, raw_body=None):

return {}

def _build_request(self, method, url, params, headers, json=None, files=None):
def _build_request(
self, method, url, params, headers, json=None, files=None, data=None
):
if files is not None:
# Multipart uploads: let httpx set the multipart Content-Type and
# boundary automatically. Multipart bodies are not utf-8-safe, so
# JWS signing is skipped for these requests.
return httpx.Request(
method, url, headers=headers, params=params, files=files
method, url, headers=headers, params=params, files=files, data=data
)

request = httpx.Request(method, url, headers=headers, params=params, json=json)
Expand All @@ -85,6 +87,7 @@ def request(
params=None,
json=None,
files=None,
data=None,
idempotency_key=None,
):
"""
Expand All @@ -107,7 +110,7 @@ def request(
)

_request = self._build_request(
method, url, all_params, headers, json, files=files
method, url, all_params, headers, json, files=files, data=data
)
response = self._client.send(_request)
response.raise_for_status()
Expand Down
13 changes: 12 additions & 1 deletion fintoc/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,33 @@
ChargesManager,
CheckoutSessionsManager,
DisputesManager,
InstitutionsManager,
InvoicesManager,
LinkIntentsManager,
LinksManager,
PaymentIntentsManager,
PaymentLinksManager,
PayoutsManager,
RefreshIntentsManager,
RefundsManager,
SubscriptionIntentsManager,
SubscriptionsManager,
TaxReturnsManager,
TaxStatementsManager,
WebhookEndpointsManager,
)
from fintoc.managers.v2 import AccountNumbersManager
from fintoc.managers.v2 import AccountsManager as AccountsManagerV2
from fintoc.managers.v2 import AccountVerificationsManager
from fintoc.managers.v2 import CheckoutSessionsManager as CheckoutSessionsManagerV2
from fintoc.managers.v2 import CustomersManager, EntitiesManager
from fintoc.managers.v2 import CustomersManager, EntitiesManager, EventsManager
from fintoc.managers.v2 import InvoicesManager as InvoicesManagerV2
from fintoc.managers.v2 import PaymentIntentsManager as PaymentIntentsManagerV2
from fintoc.managers.v2 import (
PaymentMethodsManager,
ProductsManager,
RecipientsManager,
SetupIntentsManager,
SimulateManager,
)
from fintoc.managers.v2 import SubscriptionsManager as SubscriptionsManagerV2
Expand Down Expand Up @@ -75,6 +80,10 @@ def __init__(self, api_key, api_version=None, jws_private_key=None):
self.tax_returns = TaxReturnsManager("/v1/tax_returns", self._client)
self.invoices = InvoicesManager("/v1/invoices", self._client)
self.disputes = DisputesManager("/v1/disputes", self._client)
self.institutions = InstitutionsManager("/v1/institutions", self._client)
self.link_intents = LinkIntentsManager("/v1/link_intents", self._client)
self.payouts = PayoutsManager("/v1/payouts", self._client)
self.tax_statements = TaxStatementsManager("/v1/tax_statements", self._client)

self.v2 = _FintocV2(self._client)

Expand All @@ -90,6 +99,7 @@ def __init__(self, client):
)
self.customers = CustomersManager("/v2/customers", client)
self.entities = EntitiesManager("/v2/entities", client)
self.events = EventsManager("/v2/events", client)
self.recipients = RecipientsManager("/v2/recipients", client)
self.simulate = SimulateManager("/v2/simulate", client)
self.checkout_sessions = CheckoutSessionsManagerV2(
Expand All @@ -99,4 +109,5 @@ def __init__(self, client):
self.payment_intents = PaymentIntentsManagerV2("/v2/payment_intents", client)
self.payment_methods = PaymentMethodsManager("/v2/payment_methods", client)
self.products = ProductsManager("/v2/products", client)
self.setup_intents = SetupIntentsManager("/v2/setup_intents", client)
self.subscriptions = SubscriptionsManagerV2("/v2/subscriptions", client)
5 changes: 5 additions & 0 deletions fintoc/managers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
from .checkout_sessions_manager import CheckoutSessionsManager
from .dispute_documents_manager import DisputeDocumentsManager
from .disputes_manager import DisputesManager
from .institutions_manager import InstitutionsManager
from .invoices_manager import InvoicesManager
from .link_intents_manager import LinkIntentsManager
from .links_manager import LinksManager
from .movements_manager import MovementsManager
from .payment_intents_manager import PaymentIntentsManager
from .payment_links_manager import PaymentLinksManager
from .payout_resources_manager import PayoutResourcesManager
from .payouts_manager import PayoutsManager
from .refresh_intents_manager import RefreshIntentsManager
from .refunds_manager import RefundsManager
from .subscription_intents_manager import SubscriptionIntentsManager
from .subscriptions_manager import SubscriptionsManager
from .tax_returns_manager import TaxReturnsManager
from .tax_statements_manager import TaxStatementsManager
from .webhook_endpoints_manager import WebhookEndpointsManager
7 changes: 6 additions & 1 deletion fintoc/managers/charges_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ class ChargesManager(ManagerMixin):
"""Represents a charges manager."""

resource = "charge"
methods = ["list", "get", "create"]
methods = ["list", "get", "create", "update", "cancel"]

def _cancel(self, identifier, **kwargs):
"""Cancel a charge."""
path = f"{self._build_path(**kwargs)}/{identifier}/cancel"
return self._create(path_=path, **kwargs)
11 changes: 11 additions & 0 deletions fintoc/managers/institutions_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Module to hold the institutions manager."""

from fintoc.mixins import ManagerMixin


class InstitutionsManager(ManagerMixin):

"""Represents an institutions manager."""

resource = "institution"
methods = ["list"]
2 changes: 1 addition & 1 deletion fintoc/managers/invoices_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ class InvoicesManager(ManagerMixin):
"""Represents an invoices manager."""

resource = "invoice"
methods = ["list"]
methods = ["list", "get"]
11 changes: 11 additions & 0 deletions fintoc/managers/link_intents_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Module to hold the link intents manager."""

from fintoc.mixins import ManagerMixin


class LinkIntentsManager(ManagerMixin):

"""Represents a link intents manager."""

resource = "link_intent"
methods = ["create"]
10 changes: 9 additions & 1 deletion fintoc/managers/links_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ class LinksManager(ManagerMixin):
"""Represents a links manager."""

resource = "link"
methods = ["list", "get", "update", "delete"]
methods = ["list", "get", "update", "delete", "exchange"]

def _exchange(self, exchange_token, **kwargs):
"""Exchange the widget's :exchange_token: for its link."""
path = f"{self._build_path(**kwargs)}/exchange"
link = self._request_resource(
"get", path, exchange_token=exchange_token, **kwargs
)
return self.post_get_handler(link, link.link_token)

def post_get_handler(self, object_, identifier, **kwargs):
# pylint: disable=protected-access
Expand Down
35 changes: 35 additions & 0 deletions fintoc/managers/payout_resources_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Module to hold the payout resources manager."""

from fintoc.mixins import ManagerMixin
from fintoc.resource_handlers import resource_list
from fintoc.utils import can_raise_fintoc_error, get_resource_class


class PayoutResourcesManager(ManagerMixin):

"""Represents a payout resources manager."""

resource = "generic_fintoc_resource"
methods = ["list"]

resource_types = {
"payment_intent": "payment_intent",
"charge": "charge",
"refund": "refund",
"refund_adjustment": "refund",
}

@can_raise_fintoc_error
def _list(self, **kwargs):
"""List the resources of a payout of the given ``resource_type``."""
resource = self.resource_types.get(
kwargs.get("resource_type"), self.__class__.resource
)
return resource_list(
client=self._client,
path=self._build_path(**kwargs),
klass=get_resource_class(resource),
handlers={},
methods=[],
params=kwargs,
)
30 changes: 30 additions & 0 deletions fintoc/managers/payouts_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Module to hold the payouts manager."""

from fintoc.managers.payout_resources_manager import PayoutResourcesManager
from fintoc.mixins import ManagerMixin


class PayoutsManager(ManagerMixin):

"""Represents a payouts manager."""

resource = "payout"
methods = ["list", "get"]

def __init__(self, path, client):
super().__init__(path, client)
self.__resources_manager = None

@property
def resources(self):
"""Proxies the payout resources manager."""
if self.__resources_manager is None:
self.__resources_manager = PayoutResourcesManager(
"/v1/payouts/{payout_id}/resources",
self._client,
)
return self.__resources_manager

@resources.setter
def resources(self, new_value): # pylint: disable=no-self-use
Comment thread
FranciscaOtero marked this conversation as resolved.
Resolved
raise NameError("Attribute name corresponds to a manager")
7 changes: 6 additions & 1 deletion fintoc/managers/refunds_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ class RefundsManager(ManagerMixin):
"""Represents a refunds manager."""

resource = "refund"
methods = ["list", "get", "create", "cancel"]
methods = ["list", "get", "create", "cancel", "voucher_url"]

def _cancel(self, identifier, **kwargs):
"""Expire a refund."""
path = f"{self._build_path(**kwargs)}/{identifier}/cancel"
return self._create(path_=path, **kwargs)

def _voucher_url(self, identifier, **kwargs):
"""Get a presigned URL to download the PDF voucher of a refund."""
path = f"{self._build_path(**kwargs)}/{identifier}/voucher_url"
return self._request_resource("get", path, resource="voucher_url", **kwargs)
11 changes: 11 additions & 0 deletions fintoc/managers/tax_statements_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Module to hold the tax_statements manager."""

from fintoc.mixins import ManagerMixin


class TaxStatementsManager(ManagerMixin):

"""Represents a tax_statements manager."""

resource = "tax_statement"
methods = ["list", "get"]
2 changes: 2 additions & 0 deletions fintoc/managers/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .checkout_sessions_manager import CheckoutSessionsManager
from .customers_manager import CustomersManager
from .entities_manager import EntitiesManager
from .events_manager import EventsManager
from .invoices_manager import InvoicesManager
from .lines_manager import LinesManager
from .movements_manager import MovementsManager
Expand All @@ -15,6 +16,7 @@
from .payment_methods_manager import PaymentMethodsManager
from .products_manager import ProductsManager
from .recipients_manager import RecipientsManager
from .setup_intents_manager import SetupIntentsManager
from .simulate_manager import SimulateManager
from .subscription_items_manager import SubscriptionItemsManager
from .subscriptions_manager import SubscriptionsManager
Expand Down
9 changes: 8 additions & 1 deletion fintoc/managers/v2/entities_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,19 @@ class EntitiesManager(ManagerMixin):
"""Represents an entities manager."""

resource = "entity"
methods = ["list", "get", "create"]
methods = ["list", "get", "create", "settlement_account"]

def __init__(self, path, client):
super().__init__(path, client)
self.__onboardings_manager = None

def _settlement_account(self, identifier, **kwargs):
"""Get the bank account where Fintoc settles funds for an entity."""
path = f"{self._build_path(**kwargs)}/{identifier}/settlement_account"
return self._request_resource(
"get", path, resource="settlement_account", **kwargs
)

@property
def onboardings(self):
"""Proxies the onboardings manager."""
Expand Down
Loading
Loading