wise-banking-api-client
=======================
[![](https://badge.fury.io/py/wise-banking-api-client.svg)](https://pypi.org/project/wise-banking-api-client/)
[![](https://img.shields.io/pypi/pyversions/wise-banking-api-client)](https://pypi.org/project/wise-banking-api-client)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/foss-fund/wise-banking-api-client/test-and-publish.yml?logo=github)](https://github.com/foss-fund/wise-banking-api-client/actions)
![](https://img.shields.io/badge/code%20style-black-000000.svg)
An unofficial Python client library for the [Wise API](https://docs.wise.com/api-docs/api-reference).
The classes, functions and interfaces that this library provides are very much in-development and prone to change.
This is a fork of [pywisetransfer].
[pywisetransfer]: https://github.com/jayaddison/pywisetransfer
## Installation
```bash
pip install wise-banking-api-client
```
## Command Line
You can use some of the functions on the command line.
By installing `wise_banking_api_client[cli]`, you also install the `wise` command.
```bash
pip install wise_banking_api_client[cli]
```
### Help and Information
Get help:
```bash
wise --help
```
### Generate a Key Pair
We can generate a key pair.
Note that this needs the `openssl` command installed.
```sh
$ wise new-key
writing RSA key
private key: wise.com.private.pem
public key: wise.com.public.pem
You can now upload the public key to https://wise.com or https://sandbox.transferwise.tech.
```
### Check API Key
You can check if your API key and private key work.
```sh
$ WISE_API_KEY="your api key" python -m wise_banking_api_client check
Permissions on sandbox: read+write+sca
Permissions on live: none
```
## Python Package
After installation, you should be able to import the package.
```python
>>> from wise_banking_api_client import Client
```
Wise provides [two APIs](https://docs.wise.com/api-docs/api-reference#environments): `live` and `sandbox`.
You can use either of these environments with this library.
### API Key
In order to use the API, you need to obtain an API key.
This key looks like this: `12345678-1234-1234-1234-123456789abcde`
To use the sandbox API, log into [sandbox.transferwise.tech].
Then, go to Settings 🠚 Developer Tools 🠚 API-Tokens.
Create and copy a new API key.
To use your own account, log into [wise.com](https://wise.com).
Then click on your account at the top right 🠚 Integrations and Tools 🠚 Developer Tools 🠚 API Tokens and add a new token.
[sandbox.transferwise.tech]: https://sandbox.transferwise.tech
### API Requests
The API requests are made using the [requests](https://requests.readthedocs.io/en/latest/) library.
First of all, you create a `Client` object:
- Create a `Client` object with your API key for the `live` environment:
```python
>>> client = Client(api_key="your-api-key-here", environment="live")
```
- Create a `Client` object with your API key for the `sandbox` environment:
```python
>>> client = Client(api_key="your-api-key-here")
```
- Create a `Client` object which interacts with the recorded API which is used for the tests:
```python
>>> from wise_banking_api_client.test import TestClient
>>> client = TestClient()
```
After this, all calls to the real Wise API are blocked by the `responses` library.
To stop using the recorded API:
```python
client.stop()
```
## Examples
This section provides a few examples of how to use this package.
### Profiles
This library follows the **[Wise API Reference]**.
So, if you find e.g. profile here, you can look up all the values of it in the [Wise API Reference].
If you create a sandbox account, you should have two profiles: `business` and `personal`.
```python
>>> for profile in client.profiles.list():
... print(f"id: {profile.id}")
... print(f"type: {profile.type}")
... print(f"name: {profile.details.name}")
...
id: 28577318
type: personal
name: Teresa Adams
id: 28577319
type: business
name: Law and Daughters 6423
```
### Receive Money
One profile can have several accounts in different currencies.
This shows which currencies are accepted and how to receive money.
```python
>>> for profile in client.profiles.list():
... accounts = client.account_details.list(profile_id=profile.id)
... print(f"type: {profile.type}")
... for account in accounts:
... print(
... f" currency: {account.currency.code}"
... f" receive with: {', '.join(feature.title for feature in account.bankFeatures if feature.supported)}")
...
type: personal
currency: EUR receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe
currency: GBP receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe
currency: USD receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe
currency: AUD receive with: Receive locally, Set up Direct Debits
currency: NZD receive with: Receive locally
currency: CAD receive with: Receive locally, Set up Direct Debits
currency: HUF receive with: Receive locally
currency: MYR receive with:
currency: RON receive with: Receive locally
currency: SGD receive with: Receive locally
currency: TRY receive with: Receive locally
type: business
currency: EUR receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe
currency: GBP receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe
currency: USD receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe
currency: AUD receive with: Receive locally, Set up Direct Debits
currency: NZD receive with: Receive locally
currency: CAD receive with: Receive locally, Set up Direct Debits
currency: HUF receive with: Receive locally
currency: MYR receive with:
currency: RON receive with: Receive locally
currency: SGD receive with: Receive locally
currency: TRY receive with: Receive locally
```
[Wise API Reference]: https://docs.wise.com/api-docs/api-reference
### Balance
This code retrieves the balance for each currency in each account.
```python
>>> profiles = client.profiles.list()
>>> for profile in profiles:
... print(f"type: {profile.type} {', '.join(f'{balance.totalWorth.value}{balance.totalWorth.currency}' for balance in client.balances.list(profile=profile))}")
...
type: personal 1000000.0GBP, 1000000.0EUR, 1000000.0USD, 1000000.0AUD
type: business 1000000.0GBP, 1000000.0EUR, 1000000.0USD, 1000000.0AUD
```
### Currencies
Wise supports many [currencies](https://docs.wise.com/api-docs/api-reference/currencies).
```python
>>> currencies = client.currencies.list()
>>> AED = currencies[0]
>>> AED.code
'AED'
>>> AED.name
'United Arab Emirates dirham'
>>> AED.symbol
'د.إ'
```
Above are the up-to-date currencies.
You can also use those in the package.
```python
>>> from wise_banking_api_client import Currency
>>> Currency.AED.code
'AED'
>>> Currency.AED.name
'United Arab Emirates dirham'
>>> Currency.AED.symbol
'د.إ'
```
### Recipient Account Requirements
In this example, we get the requirements for a recipient account that should receive 100 GBP from us.
```python
>>> requirements = client.recipient_accounts.get_requirements_for_currency(source=Currency.GBP, target=Currency.GBP, source_amount=100)
>>> list(sorted([requirement.type for requirement in requirements]))
['email', 'iban', 'sort_code']
```
Genrally, for thie currency, we can send money to a recipient specified by email, IBAN or sort code.
### Webhook signature verification
```python
from flask import abort, request
from wise_banking_api_client.webhooks import validate_request
@app.route("/payments/wise/webhooks")
def handle_wise_webhook():
try:
validate_request(request)
except Exception as e:
logger.error(f"Wise webhook request validation failed: {e}")
abort(400)
...
```
### Request an Example Quote
You can request quote examples as stated in [Create an un-authenticated quote](https://docs.wise.com/api-docs/api-reference/quote#create-not-authenticated).
In this example, we want to transfer `GBP` to `USD` and make sure we have `110USD` in the end.
The example quote requires less information than a real quote.
```python
>>> from wise_banking_api_client import ExampleQuoteRequest
>>> quote_request = ExampleQuoteRequest(
... sourceCurrency="GBP",
... targetCurrency="USD",
... sourceAmount=None,
... targetAmount=110,
... )
>>> example_quote = client.quotes.example(quote_request)
>>> example_quote.rate
1.25155
>>> example_quote.rateExpirationTime
datetime.datetime(2024, 12, 31, 19, 21, 44, tzinfo=datetime.timezone.utc)
>>> example_quote.profile == None # Example quotes are not bound to a profile
True
>>> example_quote.rateType
'FIXED'
```
### Request a Quote
To create a transfer, you first need a quote.
You can read on how to create [Create an authenticated quote](https://docs.wise.com/api-docs/api-reference/quote#create-authenticated).
In this example, we create a quote for the personal account. This is the same quote as the one above.
We also provide pay-out and pay-in information.
The `targetAccount` is None because we don't know the recipient yet.
```python
>>> from wise_banking_api_client import QuoteRequest, PaymentMethod
>>> quote_request = QuoteRequest(
... sourceCurrency="EUR",
... targetCurrency="EUR",
... sourceAmount=None,
... targetAmount=1,
... targetAccount=None,
... payOut=PaymentMethod.BANK_TRANSFER,
... preferredPayIn=PaymentMethod.BANK_TRANSFER,
... )
>>> quote = client.quotes.create(quote_request, profile=profiles.personal[0])
>>> quote.user
12970746
>>> quote.status
'PENDING'
>>> quote.sourceCurrency
'GBP'
>>> quote.targetCurrency
'USD'
>>> quote.sourceAmount is None # the source amount depends on the payment option
True
>>> quote.targetAmount
110.0
>>> quote.rate
1.24232
>>> quote.rateType
'FIXED'
>>> quote.payOut
'BANK_TRANSFER'
>>> len(quote.paymentOptions) # we have many payment options
20
```
### Get Recipient Requirements
The quote above lacks the recipient information.
The reason is that there are requirements to the recipient account that
depend on the quote.
We can get these requirements using `get_requirements_for_quote`.
```python
>>> requirements = client.recipient_accounts.get_requirements_for_quote(quote)
>>> [requirement.type for requirement in requirements]
['aba', 'fedwire_local', 'swift_code', 'email']
```
In the example above, we see different requirements for transferring money to a bank account
in the USA. We can use `aba`, `fedwire_local`, `swift_code` and `email`.
If we look at the first requirement, we see that we require 10 fields.
```python
>>> ach = requirements[0]
>>> ach.title
'ACH'
>>> len(requirements[0].fields)
10
>>> ach.fields[0].name
'Recipient type'
>>> ach.fields[0].group[0].required
True
>>> ach.fields[0].group[0].type # the fields are grouped and this is a select with values
'select'
>>> [v.key for v in ach.fields[0].group[0].valuesAllowed] # the JSON value for the details
['PRIVATE', 'BUSINESS']
>>> [v.name for v in ach.fields[0].group[0].valuesAllowed] # what to show to the user
['Person', 'Business']
```
### Create an Email Recipient
> Wise: Please contact us before attempting to use email recipients. We do not recommend using this feature except for certain uses cases.
Because `email` is in the requirements, we can create an email recipient for the quote.
```python
>>> email = requirements[-1]
>>> len(email.required_fields)
2
>>> email.required_fields[0].group[0].key
'email'
>>> email.required_fields[0].group[0].name
'Email (Optional)'
>>> email.required_fields[1].group[0].key
'accountHolderName'
>>> email.required_fields[1].group[0].name
'Full name of the account holder'
```
Below, we create the recipient and get a response.
The response includes all the data that we sent and optional fields.
```python
>>> from wise_banking_api_client import Recipient, RecipientDetails, CurrencyCode, AccountRequirementType
>>> email_recipient = Recipient(
... currency=CurrencyCode.EUR,
... type=AccountRequirementType.email,
... profile=profiles.personal[0].id,
... accountHolderName="John Doe",
... ownedByCustomer=False,
... details=RecipientDetails(email="john@doe.com")
... )
>>> created_response = client.recipient_accounts.create_recipient(email_recipient)
>>> created_response.id # the response has an id
700614969
```
In the following, we get the actual recipient account as the user sees it e.g. in the app.
```python
>>> recipient = client.recipient_accounts.get(created_response)
>>> recipient.id
700614969
>>> recipient.accountSummary
'john@doe.com'
>>> recipient.currency
'EUR'
>>> recipient.email
'john@doe.com'
>>> recipient.legalEntityType
'PERSON'
>>> recipient.ownedByCustomer
False
```
![](img/email-recipient.png)
### Create an IBAN recipient
Email is discouraged. We can do better!
Belows we go through the flow of creating an IBAN recipient and updating the quote.
We use the business profile for this.
```python
>>> business_profile = profiles.business[0]
```
1. Create an EUR/IBAN quote. We transfer 1000 GBP to EUR.
```python
>>> quote_request = QuoteRequest(sourceCurrency="GBP",targetCurrency="EUR", sourceAmount=1000)
>>> quote = client.quotes.create(quote_request, business_profile)
>>> quote.id
'f8301dde-cdb4-46c0-b944-3a07c7807d47'
```
2. Get the recipient requirements for the quote.
```python
>>> requirements = client.recipient_accounts.get_requirements_for_quote(quote)
>>> requirements.iban.required_keys
['IBAN', 'accountHolderName', 'legalType']
```
3. Create an IBAN recipient.
```python
>>> from wise_banking_api_client import Recipient, RecipientDetails, CurrencyCode, AccountRequirementType, LegalType
>>> iban_recipient = Recipient(
... currency=CurrencyCode.EUR,
... type=AccountRequirementType.iban,
... profile=business_profile.id,
... accountHolderName="Max Mustermann",
... ownedByCustomer=False,
... details=RecipientDetails(
... legalType=LegalType.PRIVATE,
... IBAN="DE75512108001245126199"
... )
... )
>>> created_iban_recipient = client.recipient_accounts.create_recipient(iban_recipient)
>>> created_iban_recipient.id
700615308
```
4. Update the quote so that it works with the IBAN recipient.
```python
>>> quote = client.quotes.update(created_iban_recipient, quote)
>>> quote.status
'PENDING'
```
5. Check the requirements again.
The fields tell you if you need to check the requirements again.
For example, the recipient type changes the required fields.
```python
>>> requirements = client.recipient_accounts.get_requirements_for_quote(quote)
>>> requirements.iban.required_keys
['IBAN', 'accountHolderName', 'legalType']
```
Above, we saw the flow of creating an IBAN recipient tied to the quote.
### Check the Prices
A Quote includes different pricing options.
These depend on the `payIn` and `payOut` options of the quote or the transfer.
Below, we show all the available options to pay for the transfer, sorted by price.
```python
>>> for po in sorted(quote.enabled_payments, key=lambda po: po.fee.total):
... print(f"fee: {po.fee.total: <6} payIn: {po.payIn: <21} payOut: {po.payOut}")
fee: 3.69 payIn: BALANCE payOut: BANK_TRANSFER
fee: 3.88 payIn: BANK_TRANSFER payOut: BANK_TRANSFER
fee: 3.88 payIn: PISP payOut: BANK_TRANSFER
fee: 3.88 payIn: SWIFT payOut: BANK_TRANSFER
fee: 7.56 payIn: VISA_DEBIT_OR_PREPAID payOut: BANK_TRANSFER
fee: 10.42 payIn: DEBIT payOut: BANK_TRANSFER
fee: 10.42 payIn: MC_DEBIT_OR_PREPAID payOut: BANK_TRANSFER
fee: 10.42 payIn: CARD payOut: BANK_TRANSFER
fee: 10.42 payIn: MAESTRO payOut: BANK_TRANSFER
fee: 16.28 payIn: MC_CREDIT payOut: BANK_TRANSFER
fee: 19.09 payIn: VISA_BUSINESS_DEBIT payOut: BANK_TRANSFER
fee: 26.09 payIn: CREDIT payOut: BANK_TRANSFER
fee: 26.09 payIn: APPLE_PAY payOut: BANK_TRANSFER
fee: 26.09 payIn: VISA_CREDIT payOut: BANK_TRANSFER
fee: 26.09 payIn: GOOGLE_PAY payOut: BANK_TRANSFER
fee: 35.43 payIn: MC_BUSINESS_DEBIT payOut: BANK_TRANSFER
fee: 43.14 payIn: MC_BUSINESS_CREDIT payOut: BANK_TRANSFER
fee: 43.14 payIn: VISA_BUSINESS_CREDIT payOut: BANK_TRANSFER
fee: 55.66 payIn: INTERNATIONAL_DEBIT payOut: BANK_TRANSFER
fee: 55.66 payIn: INTERNATIONAL_CREDIT payOut: BANK_TRANSFER
```
We can see that to pay with the **balance** by **bank transfer** is the cheapest option.
### Create a Transfer
We can now create a transfer.
1. Create a transfer request with the minimal required data of the transfer.
```python
>>> from wise_banking_api_client import TransferRequest, TransferDetails
>>> transfer_request = TransferRequest(
... targetAccount=created_iban_recipient.id,
... quoteUuid=quote.id,
... details=TransferDetails(
... reference="Geschenk"
... )
... )
>>> transfer = client.transfers.create(transfer_request)
>>> transfer.status
'incoming_payment_waiting'
```
At this point, we can see the transfer in the [user interface](https://sandbox.transferwise.tech/):
![](img/created-transfer.png)
2. Fund the transfer.
Funding transfers requires [SCA] authentication.
You can read on how to [configure SCA here][SCA].
You can use the default key located at `wise_banking_api_client.DEFAULT_PRIVATE_KEY` for
the sandbox API and upload it to your account's business profile.
```python
>>> from wise_banking_api_client import DEFAULT_PRIVATE_KEY
>>> client = Client(api_key="...", private_key_file=DEFAULT_PRIVATE_KEY)
>>> funding_result = client.transfers.fund(transfer)
>>> funding_result.status
'COMPLETED'
```
3. Sandbox Transfer Simulation
The sandbox API allows you to simulate transfers.
```python
>>> transfer.status
'incoming_payment_waiting'
>>> client.simulate_transfer.to_funds_converted(transfer) # simulate the transfer change
>>> transfer = client.transfers.get(transfer) # update the transfer
>>> transfer.status # check the status again
'processing'
```
[SCA]: https://docs.wise.com/api-docs/features/strong-customer-authentication-2fa/personal-token-sca
## Run tests
```sh
# Within the wise_banking_api_client working directory
pip install tox
tox
```
You can also run the tests against the sandbox API:
```sh
WISE_API_KEY="12345678-1234-1234-1234-123456789abcde" tox -e py312
```
## Changelog
### v0.0.1
- Initial release
## License
The code is released under the [AGPL License](LICENSE).
Raw data
{
"_id": null,
"home_page": null,
"name": "wise-banking-api-client",
"maintainer": null,
"docs_url": null,
"requires_python": "~=3.12",
"maintainer_email": "Nicco Kunzmann <niccokunzmann@rambler.ru>",
"keywords": "balance, payments, transfer, wise",
"author": null,
"author_email": "James Addison <jay@jp-hosting.net>, Nicco Kunzmann <niccokunzmann@rambler.ru>",
"download_url": "https://files.pythonhosted.org/packages/e8/b3/d4d7a9fd5e140bd7ff29b204ffe4fe56e675c32c81e7fcb0313d0d1a6076/wise_banking_api_client-0.0.1.tar.gz",
"platform": null,
"description": "wise-banking-api-client\n=======================\n\n[![](https://badge.fury.io/py/wise-banking-api-client.svg)](https://pypi.org/project/wise-banking-api-client/)\n[![](https://img.shields.io/pypi/pyversions/wise-banking-api-client)](https://pypi.org/project/wise-banking-api-client)\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/foss-fund/wise-banking-api-client/test-and-publish.yml?logo=github)](https://github.com/foss-fund/wise-banking-api-client/actions)\n![](https://img.shields.io/badge/code%20style-black-000000.svg)\n\nAn unofficial Python client library for the [Wise API](https://docs.wise.com/api-docs/api-reference).\n\nThe classes, functions and interfaces that this library provides are very much in-development and prone to change.\nThis is a fork of [pywisetransfer].\n\n[pywisetransfer]: https://github.com/jayaddison/pywisetransfer\n\n## Installation\n\n```bash\npip install wise-banking-api-client\n```\n\n## Command Line\n\nYou can use some of the functions on the command line.\nBy installing `wise_banking_api_client[cli]`, you also install the `wise` command.\n\n```bash\npip install wise_banking_api_client[cli]\n```\n\n### Help and Information\n\nGet help:\n\n```bash\nwise --help\n```\n\n### Generate a Key Pair\n\nWe can generate a key pair.\nNote that this needs the `openssl` command installed.\n\n```sh\n$ wise new-key\nwriting RSA key\nprivate key: wise.com.private.pem\npublic key: wise.com.public.pem\nYou can now upload the public key to https://wise.com or https://sandbox.transferwise.tech.\n```\n\n### Check API Key\n\nYou can check if your API key and private key work.\n\n```sh\n$ WISE_API_KEY=\"your api key\" python -m wise_banking_api_client check\nPermissions on sandbox: read+write+sca\nPermissions on live: none\n```\n\n## Python Package\n\nAfter installation, you should be able to import the package.\n\n```python\n>>> from wise_banking_api_client import Client\n\n```\n\nWise provides [two APIs](https://docs.wise.com/api-docs/api-reference#environments): `live` and `sandbox`.\nYou can use either of these environments with this library.\n\n### API Key\n\nIn order to use the API, you need to obtain an API key.\nThis key looks like this: `12345678-1234-1234-1234-123456789abcde`\n\nTo use the sandbox API, log into [sandbox.transferwise.tech].\nThen, go to Settings \ud83e\udc1a Developer Tools \ud83e\udc1a API-Tokens.\nCreate and copy a new API key.\n\nTo use your own account, log into [wise.com](https://wise.com).\nThen click on your account at the top right \ud83e\udc1a Integrations and Tools \ud83e\udc1a Developer Tools \ud83e\udc1a API Tokens and add a new token.\n\n[sandbox.transferwise.tech]: https://sandbox.transferwise.tech\n\n### API Requests\n\nThe API requests are made using the [requests](https://requests.readthedocs.io/en/latest/) library.\nFirst of all, you create a `Client` object:\n\n- Create a `Client` object with your API key for the `live` environment:\n\n ```python\n >>> client = Client(api_key=\"your-api-key-here\", environment=\"live\")\n\n ```\n\n- Create a `Client` object with your API key for the `sandbox` environment:\n\n ```python\n >>> client = Client(api_key=\"your-api-key-here\")\n\n ```\n\n- Create a `Client` object which interacts with the recorded API which is used for the tests:\n\n ```python\n >>> from wise_banking_api_client.test import TestClient\n >>> client = TestClient()\n\n ```\n\n After this, all calls to the real Wise API are blocked by the `responses` library.\n To stop using the recorded API:\n\n ```python\n client.stop()\n ```\n\n## Examples\n\nThis section provides a few examples of how to use this package.\n\n### Profiles\n\nThis library follows the **[Wise API Reference]**.\nSo, if you find e.g. profile here, you can look up all the values of it in the [Wise API Reference].\n\nIf you create a sandbox account, you should have two profiles: `business` and `personal`.\n\n```python\n>>> for profile in client.profiles.list():\n... print(f\"id: {profile.id}\")\n... print(f\"type: {profile.type}\")\n... print(f\"name: {profile.details.name}\")\n... \nid: 28577318\ntype: personal\nname: Teresa Adams\nid: 28577319\ntype: business\nname: Law and Daughters 6423\n\n```\n\n### Receive Money\n\nOne profile can have several accounts in different currencies.\nThis shows which currencies are accepted and how to receive money.\n\n```python\n>>> for profile in client.profiles.list():\n... accounts = client.account_details.list(profile_id=profile.id)\n... print(f\"type: {profile.type}\")\n... for account in accounts:\n... print(\n... f\" currency: {account.currency.code}\"\n... f\" receive with: {', '.join(feature.title for feature in account.bankFeatures if feature.supported)}\")\n... \ntype: personal\n currency: EUR receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe\n currency: GBP receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe\n currency: USD receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe\n currency: AUD receive with: Receive locally, Set up Direct Debits\n currency: NZD receive with: Receive locally\n currency: CAD receive with: Receive locally, Set up Direct Debits\n currency: HUF receive with: Receive locally\n currency: MYR receive with:\n currency: RON receive with: Receive locally\n currency: SGD receive with: Receive locally\n currency: TRY receive with: Receive locally\ntype: business\n currency: EUR receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe\n currency: GBP receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe\n currency: USD receive with: Receive locally, Receive internationally (Swift), Set up Direct Debits, Receive from PayPal and Stripe\n currency: AUD receive with: Receive locally, Set up Direct Debits\n currency: NZD receive with: Receive locally\n currency: CAD receive with: Receive locally, Set up Direct Debits\n currency: HUF receive with: Receive locally\n currency: MYR receive with:\n currency: RON receive with: Receive locally\n currency: SGD receive with: Receive locally\n currency: TRY receive with: Receive locally\n\n```\n\n[Wise API Reference]: https://docs.wise.com/api-docs/api-reference\n\n### Balance\n\nThis code retrieves the balance for each currency in each account.\n\n```python\n>>> profiles = client.profiles.list()\n>>> for profile in profiles:\n... print(f\"type: {profile.type} {', '.join(f'{balance.totalWorth.value}{balance.totalWorth.currency}' for balance in client.balances.list(profile=profile))}\")\n... \ntype: personal 1000000.0GBP, 1000000.0EUR, 1000000.0USD, 1000000.0AUD\ntype: business 1000000.0GBP, 1000000.0EUR, 1000000.0USD, 1000000.0AUD\n\n```\n\n### Currencies\n\nWise supports many [currencies](https://docs.wise.com/api-docs/api-reference/currencies).\n\n```python\n>>> currencies = client.currencies.list()\n>>> AED = currencies[0]\n>>> AED.code\n'AED'\n>>> AED.name\n'United Arab Emirates dirham'\n>>> AED.symbol\n'\u062f.\u0625'\n\n```\n\nAbove are the up-to-date currencies.\nYou can also use those in the package.\n\n```python\n>>> from wise_banking_api_client import Currency\n>>> Currency.AED.code\n'AED'\n>>> Currency.AED.name\n'United Arab Emirates dirham'\n>>> Currency.AED.symbol\n'\u062f.\u0625'\n\n```\n\n### Recipient Account Requirements\n\nIn this example, we get the requirements for a recipient account that should receive 100 GBP from us.\n\n```python\n>>> requirements = client.recipient_accounts.get_requirements_for_currency(source=Currency.GBP, target=Currency.GBP, source_amount=100)\n>>> list(sorted([requirement.type for requirement in requirements]))\n['email', 'iban', 'sort_code']\n\n```\n\nGenrally, for thie currency, we can send money to a recipient specified by email, IBAN or sort code.\n\n### Webhook signature verification\n\n```python\nfrom flask import abort, request\nfrom wise_banking_api_client.webhooks import validate_request\n\n@app.route(\"/payments/wise/webhooks\")\ndef handle_wise_webhook():\n try:\n validate_request(request)\n except Exception as e:\n logger.error(f\"Wise webhook request validation failed: {e}\")\n abort(400)\n\n ...\n```\n\n### Request an Example Quote\n\nYou can request quote examples as stated in [Create an un-authenticated quote](https://docs.wise.com/api-docs/api-reference/quote#create-not-authenticated).\n\nIn this example, we want to transfer `GBP` to `USD` and make sure we have `110USD` in the end.\nThe example quote requires less information than a real quote.\n\n```python\n>>> from wise_banking_api_client import ExampleQuoteRequest\n>>> quote_request = ExampleQuoteRequest(\n... sourceCurrency=\"GBP\",\n... targetCurrency=\"USD\",\n... sourceAmount=None,\n... targetAmount=110,\n... )\n>>> example_quote = client.quotes.example(quote_request)\n>>> example_quote.rate\n1.25155\n>>> example_quote.rateExpirationTime\ndatetime.datetime(2024, 12, 31, 19, 21, 44, tzinfo=datetime.timezone.utc)\n>>> example_quote.profile == None # Example quotes are not bound to a profile\nTrue\n>>> example_quote.rateType\n'FIXED'\n\n```\n\n### Request a Quote\n\nTo create a transfer, you first need a quote.\nYou can read on how to create [Create an authenticated quote](https://docs.wise.com/api-docs/api-reference/quote#create-authenticated).\n\nIn this example, we create a quote for the personal account. This is the same quote as the one above.\nWe also provide pay-out and pay-in information.\nThe `targetAccount` is None because we don't know the recipient yet.\n\n```python\n>>> from wise_banking_api_client import QuoteRequest, PaymentMethod\n>>> quote_request = QuoteRequest(\n... sourceCurrency=\"EUR\",\n... targetCurrency=\"EUR\",\n... sourceAmount=None,\n... targetAmount=1,\n... targetAccount=None,\n... payOut=PaymentMethod.BANK_TRANSFER,\n... preferredPayIn=PaymentMethod.BANK_TRANSFER,\n... )\n>>> quote = client.quotes.create(quote_request, profile=profiles.personal[0])\n>>> quote.user\n12970746\n>>> quote.status\n'PENDING'\n>>> quote.sourceCurrency\n'GBP'\n>>> quote.targetCurrency \n'USD'\n>>> quote.sourceAmount is None # the source amount depends on the payment option\nTrue\n>>> quote.targetAmount\n110.0\n>>> quote.rate\n1.24232\n>>> quote.rateType\n'FIXED'\n>>> quote.payOut\n'BANK_TRANSFER'\n>>> len(quote.paymentOptions) # we have many payment options\n20\n\n```\n\n### Get Recipient Requirements\n\nThe quote above lacks the recipient information.\nThe reason is that there are requirements to the recipient account that\ndepend on the quote.\nWe can get these requirements using `get_requirements_for_quote`.\n\n```python\n>>> requirements = client.recipient_accounts.get_requirements_for_quote(quote)\n>>> [requirement.type for requirement in requirements]\n['aba', 'fedwire_local', 'swift_code', 'email']\n\n```\n\nIn the example above, we see different requirements for transferring money to a bank account\nin the USA. We can use `aba`, `fedwire_local`, `swift_code` and `email`.\n\nIf we look at the first requirement, we see that we require 10 fields.\n\n```python\n>>> ach = requirements[0]\n>>> ach.title\n'ACH'\n>>> len(requirements[0].fields)\n10\n>>> ach.fields[0].name\n'Recipient type'\n>>> ach.fields[0].group[0].required\nTrue\n>>> ach.fields[0].group[0].type # the fields are grouped and this is a select with values\n'select'\n>>> [v.key for v in ach.fields[0].group[0].valuesAllowed] # the JSON value for the details\n['PRIVATE', 'BUSINESS']\n>>> [v.name for v in ach.fields[0].group[0].valuesAllowed] # what to show to the user\n['Person', 'Business']\n\n```\n\n### Create an Email Recipient\n\n> Wise: Please contact us before attempting to use email recipients. We do not recommend using this feature except for certain uses cases.\n\nBecause `email` is in the requirements, we can create an email recipient for the quote.\n\n```python\n>>> email = requirements[-1]\n>>> len(email.required_fields)\n2\n>>> email.required_fields[0].group[0].key\n'email'\n>>> email.required_fields[0].group[0].name\n'Email (Optional)'\n>>> email.required_fields[1].group[0].key\n'accountHolderName'\n>>> email.required_fields[1].group[0].name\n'Full name of the account holder'\n\n```\n\nBelow, we create the recipient and get a response.\nThe response includes all the data that we sent and optional fields.\n\n```python\n>>> from wise_banking_api_client import Recipient, RecipientDetails, CurrencyCode, AccountRequirementType\n>>> email_recipient = Recipient(\n... currency=CurrencyCode.EUR,\n... type=AccountRequirementType.email,\n... profile=profiles.personal[0].id,\n... accountHolderName=\"John Doe\",\n... ownedByCustomer=False,\n... details=RecipientDetails(email=\"john@doe.com\")\n... )\n>>> created_response = client.recipient_accounts.create_recipient(email_recipient)\n>>> created_response.id # the response has an id\n700614969\n\n```\n\nIn the following, we get the actual recipient account as the user sees it e.g. in the app.\n\n```python\n>>> recipient = client.recipient_accounts.get(created_response)\n>>> recipient.id\n700614969\n>>> recipient.accountSummary\n'john@doe.com'\n>>> recipient.currency\n'EUR'\n>>> recipient.email\n'john@doe.com'\n>>> recipient.legalEntityType\n'PERSON'\n>>> recipient.ownedByCustomer\nFalse\n\n```\n\n![](img/email-recipient.png)\n\n### Create an IBAN recipient\n\nEmail is discouraged. We can do better!\nBelows we go through the flow of creating an IBAN recipient and updating the quote.\n\nWe use the business profile for this.\n\n```python\n>>> business_profile = profiles.business[0]\n\n```\n\n1. Create an EUR/IBAN quote. We transfer 1000 GBP to EUR.\n\n ```python\n >>> quote_request = QuoteRequest(sourceCurrency=\"GBP\",targetCurrency=\"EUR\", sourceAmount=1000)\n >>> quote = client.quotes.create(quote_request, business_profile)\n >>> quote.id\n 'f8301dde-cdb4-46c0-b944-3a07c7807d47'\n\n ```\n\n2. Get the recipient requirements for the quote.\n\n ```python\n >>> requirements = client.recipient_accounts.get_requirements_for_quote(quote)\n >>> requirements.iban.required_keys\n ['IBAN', 'accountHolderName', 'legalType']\n\n ```\n\n3. Create an IBAN recipient.\n\n ```python\n >>> from wise_banking_api_client import Recipient, RecipientDetails, CurrencyCode, AccountRequirementType, LegalType\n >>> iban_recipient = Recipient(\n ... currency=CurrencyCode.EUR,\n ... type=AccountRequirementType.iban,\n ... profile=business_profile.id,\n ... accountHolderName=\"Max Mustermann\",\n ... ownedByCustomer=False,\n ... details=RecipientDetails(\n ... legalType=LegalType.PRIVATE,\n ... IBAN=\"DE75512108001245126199\"\n ... )\n ... )\n >>> created_iban_recipient = client.recipient_accounts.create_recipient(iban_recipient)\n >>> created_iban_recipient.id\n 700615308\n\n ```\n\n4. Update the quote so that it works with the IBAN recipient.\n\n ```python\n >>> quote = client.quotes.update(created_iban_recipient, quote)\n >>> quote.status\n 'PENDING'\n\n ```\n\n5. Check the requirements again.\n\n The fields tell you if you need to check the requirements again.\n For example, the recipient type changes the required fields.\n\n ```python\n >>> requirements = client.recipient_accounts.get_requirements_for_quote(quote)\n >>> requirements.iban.required_keys\n ['IBAN', 'accountHolderName', 'legalType']\n\n ```\n\nAbove, we saw the flow of creating an IBAN recipient tied to the quote.\n\n### Check the Prices\n\nA Quote includes different pricing options.\nThese depend on the `payIn` and `payOut` options of the quote or the transfer.\n\nBelow, we show all the available options to pay for the transfer, sorted by price.\n\n```python\n>>> for po in sorted(quote.enabled_payments, key=lambda po: po.fee.total):\n... print(f\"fee: {po.fee.total: <6} payIn: {po.payIn: <21} payOut: {po.payOut}\")\nfee: 3.69 payIn: BALANCE payOut: BANK_TRANSFER\nfee: 3.88 payIn: BANK_TRANSFER payOut: BANK_TRANSFER\nfee: 3.88 payIn: PISP payOut: BANK_TRANSFER\nfee: 3.88 payIn: SWIFT payOut: BANK_TRANSFER\nfee: 7.56 payIn: VISA_DEBIT_OR_PREPAID payOut: BANK_TRANSFER\nfee: 10.42 payIn: DEBIT payOut: BANK_TRANSFER\nfee: 10.42 payIn: MC_DEBIT_OR_PREPAID payOut: BANK_TRANSFER\nfee: 10.42 payIn: CARD payOut: BANK_TRANSFER\nfee: 10.42 payIn: MAESTRO payOut: BANK_TRANSFER\nfee: 16.28 payIn: MC_CREDIT payOut: BANK_TRANSFER\nfee: 19.09 payIn: VISA_BUSINESS_DEBIT payOut: BANK_TRANSFER\nfee: 26.09 payIn: CREDIT payOut: BANK_TRANSFER\nfee: 26.09 payIn: APPLE_PAY payOut: BANK_TRANSFER\nfee: 26.09 payIn: VISA_CREDIT payOut: BANK_TRANSFER\nfee: 26.09 payIn: GOOGLE_PAY payOut: BANK_TRANSFER\nfee: 35.43 payIn: MC_BUSINESS_DEBIT payOut: BANK_TRANSFER\nfee: 43.14 payIn: MC_BUSINESS_CREDIT payOut: BANK_TRANSFER\nfee: 43.14 payIn: VISA_BUSINESS_CREDIT payOut: BANK_TRANSFER\nfee: 55.66 payIn: INTERNATIONAL_DEBIT payOut: BANK_TRANSFER\nfee: 55.66 payIn: INTERNATIONAL_CREDIT payOut: BANK_TRANSFER\n\n```\n\nWe can see that to pay with the **balance** by **bank transfer** is the cheapest option.\n\n### Create a Transfer\n\nWe can now create a transfer.\n\n1. Create a transfer request with the minimal required data of the transfer.\n\n ```python\n >>> from wise_banking_api_client import TransferRequest, TransferDetails\n >>> transfer_request = TransferRequest(\n ... targetAccount=created_iban_recipient.id,\n ... quoteUuid=quote.id,\n ... details=TransferDetails(\n ... reference=\"Geschenk\"\n ... )\n ... )\n >>> transfer = client.transfers.create(transfer_request)\n >>> transfer.status\n 'incoming_payment_waiting'\n\n ```\n\n At this point, we can see the transfer in the [user interface](https://sandbox.transferwise.tech/):\n\n ![](img/created-transfer.png)\n\n2. Fund the transfer.\n\n Funding transfers requires [SCA] authentication.\n You can read on how to [configure SCA here][SCA].\n You can use the default key located at `wise_banking_api_client.DEFAULT_PRIVATE_KEY` for\n the sandbox API and upload it to your account's business profile.\n\n ```python\n >>> from wise_banking_api_client import DEFAULT_PRIVATE_KEY\n >>> client = Client(api_key=\"...\", private_key_file=DEFAULT_PRIVATE_KEY)\n >>> funding_result = client.transfers.fund(transfer)\n >>> funding_result.status\n 'COMPLETED'\n\n ```\n\n3. Sandbox Transfer Simulation\n\n The sandbox API allows you to simulate transfers.\n\n ```python\n >>> transfer.status\n 'incoming_payment_waiting'\n >>> client.simulate_transfer.to_funds_converted(transfer) # simulate the transfer change\n >>> transfer = client.transfers.get(transfer) # update the transfer\n >>> transfer.status # check the status again\n 'processing'\n\n ```\n\n[SCA]: https://docs.wise.com/api-docs/features/strong-customer-authentication-2fa/personal-token-sca\n\n## Run tests\n\n```sh\n# Within the wise_banking_api_client working directory\npip install tox\ntox\n```\n\nYou can also run the tests against the sandbox API:\n\n```sh\nWISE_API_KEY=\"12345678-1234-1234-1234-123456789abcde\" tox -e py312\n```\n\n## Changelog\n\n### v0.0.1\n\n- Initial release\n\n## License\n\nThe code is released under the [AGPL License](LICENSE).\n",
"bugtrack_url": null,
"license": null,
"summary": "Typed Python client library for the API of the bank Wise",
"version": "0.0.1",
"project_urls": {
"Issues": "https://github.com/foss-fund/wise-banking-api-client/issues",
"Repository": "https://github.com/foss-fund/wise-banking-api-client",
"source_archive": "https://github.com/foss-fund/wise-banking-api-client/archive/591f194093733e06472a708964ba4b496facb45f.zip"
},
"split_keywords": [
"balance",
" payments",
" transfer",
" wise"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "86b7e1e7e4bcc23280ce895f38d61cd857aaebce2b44f0eae711943374c3d697",
"md5": "651ae5c8c195bdc6e43331acce0a3801",
"sha256": "fec184bb6acfe5de79075dd34591d0647a2aa1f6ee4cee5fa11d66f73d5b307f"
},
"downloads": -1,
"filename": "wise_banking_api_client-0.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "651ae5c8c195bdc6e43331acce0a3801",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "~=3.12",
"size": 354729,
"upload_time": "2025-01-16T22:15:39",
"upload_time_iso_8601": "2025-01-16T22:15:39.360235Z",
"url": "https://files.pythonhosted.org/packages/86/b7/e1e7e4bcc23280ce895f38d61cd857aaebce2b44f0eae711943374c3d697/wise_banking_api_client-0.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "e8b3d4d7a9fd5e140bd7ff29b204ffe4fe56e675c32c81e7fcb0313d0d1a6076",
"md5": "15abb36ba15f61cb91d4849629f38d85",
"sha256": "9c482f2e4273d3a1e4276feb5c0ddf444369f03e824c7c843cca0044798004a5"
},
"downloads": -1,
"filename": "wise_banking_api_client-0.0.1.tar.gz",
"has_sig": false,
"md5_digest": "15abb36ba15f61cb91d4849629f38d85",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "~=3.12",
"size": 511361,
"upload_time": "2025-01-16T22:15:44",
"upload_time_iso_8601": "2025-01-16T22:15:44.461998Z",
"url": "https://files.pythonhosted.org/packages/e8/b3/d4d7a9fd5e140bd7ff29b204ffe4fe56e675c32c81e7fcb0313d0d1a6076/wise_banking_api_client-0.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-16 22:15:44",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "foss-fund",
"github_project": "wise-banking-api-client",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "annotated-types",
"specs": [
[
"==",
"0.7.0"
]
]
},
{
"name": "apiron",
"specs": [
[
"==",
"8.0.0.post1"
]
]
},
{
"name": "certifi",
"specs": [
[
"==",
"2024.12.14"
]
]
},
{
"name": "cffi",
"specs": [
[
"==",
"1.17.1"
]
]
},
{
"name": "charset-normalizer",
"specs": [
[
"==",
"3.4.1"
]
]
},
{
"name": "cryptography",
"specs": [
[
"==",
"44.0.0"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.10"
]
]
},
{
"name": "munch",
"specs": [
[
"==",
"4.0.0"
]
]
},
{
"name": "pycparser",
"specs": [
[
"==",
"2.22"
]
]
},
{
"name": "pydantic",
"specs": [
[
"==",
"2.10.5"
]
]
},
{
"name": "pydantic-core",
"specs": [
[
"==",
"2.27.2"
]
]
},
{
"name": "requests",
"specs": [
[
"==",
"2.32.3"
]
]
},
{
"name": "typing-extensions",
"specs": [
[
"==",
"4.12.2"
]
]
},
{
"name": "urllib3",
"specs": [
[
"==",
"2.3.0"
]
]
}
],
"tox": true,
"lcname": "wise-banking-api-client"
}