├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── examples ├── General │ └── use_logger.py ├── Merchant │ ├── bill_requests.py │ ├── invoice_requests.py │ ├── ledger_requests.py │ ├── refund_requests.py │ └── settlement_requests.py ├── Payout │ ├── payout_requests.py │ └── recipient_requests.py ├── Pos │ ├── bill_requests.py │ └── invoice_requests.py ├── Public │ ├── rate_requests.py │ └── wallet_requests.py └── client_provider.py ├── pyproject.toml ├── pytest.ini ├── src ├── __init__.py └── bitpay │ ├── .gitignore │ ├── __init__.py │ ├── bitpay_setup.py │ ├── client.py │ ├── clients │ ├── __init__.py │ ├── bill_client.py │ ├── bitpay_client.py │ ├── currency_client.py │ ├── invoice_client.py │ ├── ledger_client.py │ ├── payout_client.py │ ├── payout_recipient_client.py │ ├── rate_client.py │ ├── refund_client.py │ ├── response_parser.py │ ├── settlement_client.py │ └── wallet_client.py │ ├── config.py │ ├── environment.py │ ├── exceptions │ ├── __init__.py │ ├── bitpay_api_exception.py │ ├── bitpay_exception.py │ ├── bitpay_exception_provider.py │ ├── bitpay_generic_exception.py │ └── bitpay_validation_exception.py │ ├── logger │ ├── __init__.py │ ├── bitpay_logger.py │ ├── logger_provider.py │ └── logging_logger.py │ ├── models │ ├── __init__.py │ ├── bill │ │ ├── __init__.py │ │ ├── bill.py │ │ ├── bill_status.py │ │ └── item.py │ ├── bitpay_model.py │ ├── currency.py │ ├── facade.py │ ├── invoice │ │ ├── __init__.py │ │ ├── buyer.py │ │ ├── buyer_fields.py │ │ ├── buyer_provided_info.py │ │ ├── invoice.py │ │ ├── invoice_event_token.py │ │ ├── invoice_refund_addresses.py │ │ ├── invoice_status.py │ │ ├── invoice_webhook.py │ │ ├── itemized_details.py │ │ ├── miner_fees.py │ │ ├── miner_fees_item.py │ │ ├── refund.py │ │ ├── refund_info.py │ │ ├── refund_status.py │ │ ├── refund_webhook.py │ │ ├── shopper.py │ │ ├── supported_transaction_currencies.py │ │ ├── supported_transaction_currency.py │ │ ├── transaction.py │ │ └── universal_codes.py │ ├── ledger │ │ ├── __init__.py │ │ ├── buyer.py │ │ ├── ledger.py │ │ └── ledger_entry.py │ ├── payout │ │ ├── __init__.py │ │ ├── payout.py │ │ ├── payout_group.py │ │ ├── payout_group_failed.py │ │ ├── payout_recipient.py │ │ ├── payout_recipients.py │ │ ├── payout_status.py │ │ ├── payout_transaction.py │ │ ├── payout_webhook.py │ │ └── recipient_status.py │ ├── rate │ │ ├── __init__.py │ │ ├── rate.py │ │ └── rates.py │ ├── settlement │ │ ├── __init__.py │ │ ├── invoice_data.py │ │ ├── payout_info.py │ │ ├── refund_info.py │ │ ├── settlement.py │ │ ├── settlement_ledger_entry.py │ │ └── with_holdings.py │ └── wallet │ │ ├── __init__.py │ │ ├── currencies.py │ │ ├── currency_qr.py │ │ └── wallet.py │ └── utils │ ├── __init__.py │ ├── guid_generator.py │ ├── key_utils.py │ ├── model_util.py │ └── token_container.py └── tests ├── __init__.py ├── functional ├── .gitignore ├── __init__.py ├── json │ ├── cancel_invoice_response.json │ ├── cancel_payout_group_response.json │ ├── cancel_refund_response.json │ ├── create_bill_by_pos_request.json │ ├── create_bill_request.json │ ├── create_bill_response.json │ ├── create_invoice_by_pos_request.json │ ├── create_invoice_request.json │ ├── create_invoice_response.json │ ├── create_payout_group_request.json │ ├── create_payout_group_response.json │ ├── create_refund_response.json │ ├── get_bill_response.json │ ├── get_bills_response.json │ ├── get_currencies_response.json │ ├── get_invoice_event_token.json │ ├── get_invoice_response.json │ ├── get_invoices_response.json │ ├── get_ledger_entries_response.json │ ├── get_ledgers.json │ ├── get_payout_recipient_response.json │ ├── get_payout_recipients_response.json │ ├── get_payout_response.json │ ├── get_payouts.json │ ├── get_rates_bch_response.json │ ├── get_rates_response.json │ ├── get_refund_response.json │ ├── get_refunds_response.json │ ├── get_settlement_reconciliation_report_response.json │ ├── get_settlement_response.json │ ├── get_settlements_response.json │ ├── get_supported_wallets_response.json │ ├── pay_invoice_response.json │ ├── submit_payout_recipients_request.json │ ├── submit_payout_recipients_response.json │ ├── submit_payout_request.json │ ├── submit_payout_response.json │ ├── success_response.json │ ├── update_bill_request.json │ ├── update_payout_recipient_request.json │ └── update_payout_recipient_response.json └── test_client.py └── unit ├── __init__.py ├── bitpay.config.json ├── clients ├── response_with_errors.json └── test_response_parser.py ├── json ├── cancel_invoice_response.json ├── cancel_payout_group_response.json ├── cancel_refund_response.json ├── create_bill_by_pos_request.json ├── create_bill_request.json ├── create_bill_response.json ├── create_invoice_by_pos_request.json ├── create_invoice_request.json ├── create_invoice_response.json ├── create_payout_group_request.json ├── create_payout_group_response.json ├── create_refund_response.json ├── get_bill_response.json ├── get_bills_response.json ├── get_currencies_response.json ├── get_invoice_event_token.json ├── get_invoice_response.json ├── get_invoices_response.json ├── get_ledger_entries_response.json ├── get_ledgers.json ├── get_payout_recipient_response.json ├── get_payout_recipients_response.json ├── get_payout_response.json ├── get_payouts.json ├── get_rates_bch_response.json ├── get_rates_response.json ├── get_refund_response.json ├── get_refunds_response.json ├── get_settlement_reconciliation_report_response.json ├── get_settlement_response.json ├── get_settlements_response.json ├── get_supported_wallets_response.json ├── pay_invoice_response.json ├── submit_payout_recipients_request.json ├── submit_payout_recipients_response.json ├── submit_payout_request.json ├── submit_payout_response.json ├── success_response.json ├── update_bill_request.json ├── update_payout_recipient_request.json └── update_payout_recipient_response.json ├── logger ├── __init__.py └── test_logger_provider.py ├── models ├── __init__.py ├── bill │ ├── __init__.py │ ├── test_bill.py │ └── test_item.py ├── bitpay_model_test.py ├── invoice │ ├── __init__.py │ ├── test_buyer.py │ ├── test_buyer_provided_info.py │ ├── test_invoice.py │ ├── test_invoice_event_token.py │ ├── test_itemized_details.py │ ├── test_miner_fees.py │ ├── test_miner_fees_item.py │ ├── test_refund.py │ ├── test_refund_info.py │ ├── test_shopper.py │ ├── test_supported_transaction_currencies.py │ ├── test_supported_transaction_currency.py │ ├── test_transaction.py │ └── test_universal_codes.py ├── invoice_model.json ├── ledger │ ├── __init__.py │ ├── test_buyer.py │ ├── test_ledger.py │ └── test_ledger_entry.py ├── payout │ ├── __init__.py │ ├── test_payout.py │ ├── test_payout_recipient.py │ ├── test_payout_recipients.py │ └── test_payout_transaction.py ├── rate │ ├── __init__.py │ ├── test_rate.py │ └── test_rates.py ├── settlement │ ├── __init__.py │ ├── test_invoice_data.py │ ├── test_payout_info.py │ ├── test_refund_info.py │ ├── test_settlement.py │ ├── test_settlement_ledger_entry.py │ └── test_with_holdings.py └── wallet │ ├── __init__.py │ ├── test_currencies.py │ ├── test_currency_qr.py │ └── test_wallet.py └── test_client.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-python@v5 11 | with: 12 | python-version: 3.9 13 | - name: Install pipenv 14 | run: | 15 | pip3 install pipenv 16 | pip3 install --upgrade pip 17 | - name: Install dependencies 18 | run: | 19 | pipenv install --dev 20 | pipenv run pip3 install typing-extensions 21 | - name: Check code format 22 | run: pipenv run black --check src/ 23 | - name: Run static analysis 24 | run: | 25 | pipenv run mypy --install-types --non-interactive src/ 26 | pipenv run mypy -p src 27 | 28 | test: 29 | runs-on: ubuntu-latest 30 | 31 | strategy: 32 | matrix: 33 | python-version: [3.9, '3.10', 3.11, 3.12, 3.13] 34 | 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: actions/setup-python@v5 38 | with: 39 | python-version: ${{ matrix.python-version }} 40 | - name: Install pipenv 41 | run: | 42 | pip3 install pipenv 43 | pip3 install --upgrade pip 44 | - name: Install dependencies 45 | run: | 46 | pipenv install --dev 47 | pipenv run pip3 install typing-extensions 48 | - name: Run unit tests 49 | run: pipenv run pytest -vv -s -m unit 50 | - name: Run a test build 51 | run: | 52 | pipenv run pip3 install --upgrade build 53 | pipenv run python -m build 54 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: 4 | - published 5 | 6 | name: release 7 | 8 | jobs: 9 | package: 10 | name: Package release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: 3.9 17 | - run: | 18 | pip3 install --upgrade build 19 | python -m build 20 | - uses: actions/upload-artifact@v4 21 | with: 22 | path: ./dist 23 | 24 | pypi-publish: 25 | needs: ['package'] 26 | name: Upload release to PyPI 27 | runs-on: ubuntu-latest 28 | environment: release 29 | permissions: 30 | id-token: write 31 | steps: 32 | - uses: actions/download-artifact@v4 33 | - name: Publish package distributions to PyPI 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | with: 36 | packages_dir: artifact/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 BitPay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | ecdsa = "==0.19.1" 8 | requests = "==2.32.3" 9 | pydantic = "==2.11.3" 10 | 11 | [dev-packages] 12 | black = "==25.1.0" 13 | pytest = "==8.3.5" 14 | pytest-mock = "==3.14.0" 15 | mypy = "==1.15.0" 16 | pytest-mypy-plugins = "==3.2.0" 17 | pytest-cov = "==6.1.1" 18 | 19 | [requires] 20 | python_version = "3" 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # BitPay Python Client 4 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/bitpay/python-bitpay-client/master/LICENSE) 5 | [![PyPI](https://img.shields.io/pypi/v/bitpay.svg?style=flat-square)](https://pypi.org/project/bitpay) 6 | 7 | Full implementation of the BitPay Payment Gateway. This library implements BitPay's [Cryptographically Secure RESTful API](https://developer.bitpay.com/reference/concepts). 8 | 9 | ## Support 10 | 11 | * https://github.com/bitpay/python-bitpay-client/issues 12 | * https://support.bitpay.com 13 | 14 | ## Contribute 15 | 16 | To contribute to this project, please fork and submit a pull request. 17 | 18 | ## License 19 | 20 | MIT License 21 | 22 | Copyright (c) 2021 BitPay 23 | 24 | Permission is hereby granted, free of charge, to any person obtaining a copy 25 | of this software and associated documentation files (the "Software"), to deal 26 | in the Software without restriction, including without limitation the rights 27 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | copies of the Software, and to permit persons to whom the Software is 29 | furnished to do so, subject to the following conditions: 30 | 31 | The above copyright notice and this permission notice shall be included in all 32 | copies or substantial portions of the Software. 33 | 34 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 39 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 | SOFTWARE. 41 | -------------------------------------------------------------------------------- /examples/General/use_logger.py: -------------------------------------------------------------------------------- 1 | from bitpay.client import Client 2 | from bitpay.logger.bitpay_logger import BitPayLogger 3 | from bitpay.logger.logger_provider import LoggerProvider 4 | 5 | 6 | class UseLogger: 7 | 8 | def execute(self) -> None: 9 | LoggerProvider.set_logger(BitPayLogger()) 10 | client = Client.create_pos_client(self, "someToken") 11 | 12 | client.get_invoice(self, "someId") 13 | -------------------------------------------------------------------------------- /examples/Merchant/bill_requests.py: -------------------------------------------------------------------------------- 1 | from bitpay.models.bill.bill import Bill 2 | from bitpay.models.facade import Facade 3 | from examples.client_provider import ClientProvider 4 | 5 | 6 | class BillRequests: 7 | 8 | def create_bill(self) -> None: 9 | client = ClientProvider.create() 10 | 11 | bill = Bill() 12 | bill.name = 'someName' 13 | bill.email = 'someEmail@email.com' 14 | bill.address1 = 'SomeAddress' 15 | bill.city = 'MyCity' 16 | # ... 17 | 18 | client.create_bill(bill, Facade.MERCHANT) 19 | 20 | def get_bill(self) -> None: 21 | client = ClientProvider.create() 22 | 23 | bill = client.get_bill('someBillId', Facade.MERCHANT) 24 | 25 | bills = client.get_bills('draft') 26 | 27 | def update_bill(self) -> None: 28 | client = ClientProvider.create() 29 | 30 | bill = Bill() 31 | bill.email = 'myNew@email.com' 32 | 33 | result = client.update_bill(bill, 'someBillId') 34 | 35 | def deliver_bill_via_email(self) -> None: 36 | client = ClientProvider.create() 37 | 38 | result = client.deliver_bill('someBillId', 'someBillToken') 39 | -------------------------------------------------------------------------------- /examples/Merchant/invoice_requests.py: -------------------------------------------------------------------------------- 1 | from bitpay.models.facade import Facade 2 | from bitpay.models.invoice.buyer import Buyer 3 | from bitpay.models.invoice.invoice import Invoice 4 | from examples.client_provider import ClientProvider 5 | 6 | 7 | class InvoiceRequests: 8 | def create_invoice(self) -> None: 9 | invoice = Invoice() 10 | invoice.full_notifications = True 11 | invoice.extended_notifications = True 12 | invoice.notification_url = "https://test/lJnJg9WW7MtG9GZlPVdj" 13 | invoice.redirect_url = "https://test/lJnJg9WW7MtG9GZlPVdj" 14 | invoice.notification_email = "my@email.com" 15 | 16 | buyer = Buyer() 17 | buyer.name = "Test" 18 | buyer.email = "test@email.com" 19 | buyer.address1 = "168 General Grove" 20 | buyer.country = "AD" 21 | buyer.locality = "Port Horizon" 22 | buyer.notify = True 23 | buyer.phone = "+990123456789" 24 | buyer.postal_code = "KY7 1TH" 25 | buyer.region = "New Port" 26 | 27 | invoice.buyer = buyer 28 | 29 | client = ClientProvider.create() 30 | 31 | result = client.create_invoice(invoice, Facade.MERCHANT) 32 | 33 | def get_invoice(self) -> None: 34 | client = ClientProvider.create() 35 | 36 | invoice_by_id = client.get_invoice('someInvoiceId') 37 | 38 | invoice_by_guid = client.get_invoice_by_guid('someGuid') 39 | 40 | invoices = client.get_invoices('2023-04-14', '2023-04-17') 41 | 42 | def update_invoice(self) -> None: 43 | client = ClientProvider.create() 44 | 45 | result = client.update_invoice('someInvoiceId', None, '123123213') 46 | 47 | def cancel_invoice(self) -> None: 48 | client = ClientProvider.create() 49 | 50 | client.cancel_invoice('someInvoiceId') 51 | 52 | client.cancel_invoice_by_guid('someGuid') 53 | 54 | def request_invoice_webhook_to_be_resent(self) -> None: 55 | client = ClientProvider.create() 56 | 57 | client.request_invoice_notifications('someInvoiceId') 58 | -------------------------------------------------------------------------------- /examples/Merchant/ledger_requests.py: -------------------------------------------------------------------------------- 1 | from examples.client_provider import ClientProvider 2 | 3 | 4 | class LedgerRequests: 5 | def get_ledgers(self) -> None: 6 | client = ClientProvider.create() 7 | 8 | result = client.get_ledgers() 9 | 10 | def get_ledger_entries(self) -> None: 11 | client = ClientProvider.create() 12 | 13 | result = client.get_ledger_entries('USD', '2023-08-14', '2023-08-22') 14 | -------------------------------------------------------------------------------- /examples/Merchant/refund_requests.py: -------------------------------------------------------------------------------- 1 | from examples.client_provider import ClientProvider 2 | 3 | 4 | class RefundRequests: 5 | def create_refund(self) -> None: 6 | client = ClientProvider.create() 7 | 8 | result = client.create_refund('invoiceId', 12.34) 9 | 10 | def update_refund(self) -> None: 11 | client = ClientProvider.create() 12 | 13 | client.update_refund('refundId', 'created') 14 | 15 | client.update_refund_by_guid('someGuid', 'created') 16 | 17 | def get_refund(self) -> None: 18 | client = ClientProvider.create() 19 | 20 | client.get_refund('refundId') 21 | 22 | client.get_refund_by_guid('someGuid') 23 | 24 | def cancel_refund(self) -> None: 25 | client = ClientProvider.create() 26 | 27 | result = client.cancel_refund('someRefundId') 28 | 29 | def request_refund_notification_to_be_resent(self) -> None: 30 | client = ClientProvider.create() 31 | 32 | result = client.request_refund_notification('someRefundId') 33 | -------------------------------------------------------------------------------- /examples/Merchant/settlement_requests.py: -------------------------------------------------------------------------------- 1 | from examples.client_provider import ClientProvider 2 | 3 | 4 | class SettlementRequests: 5 | def get_settlement(self) -> None: 6 | client = ClientProvider.create() 7 | 8 | settlement = client.get_settlement('someSettlementId') 9 | 10 | settlements = client.get_settlements('USD', '2023-08-14', '2023-08-22') 11 | 12 | def fetch_reconciliation_report(self) -> None: 13 | client = ClientProvider.create() 14 | 15 | client.get_settlement_reconciliation_report('settlementId', 'settlementToken') 16 | -------------------------------------------------------------------------------- /examples/Payout/payout_requests.py: -------------------------------------------------------------------------------- 1 | from bitpay.models.payout.payout import Payout 2 | from examples.client_provider import ClientProvider 3 | 4 | 5 | class PayoutRequests: 6 | def createPayout(self) -> None: 7 | client = ClientProvider.create() 8 | 9 | payout = Payout() 10 | payout.notification_email = 'myEmail@email.com' 11 | payout.notification_url = 'https://my-url.com' 12 | 13 | result = client.submit_payout(payout) 14 | 15 | payouts = client.create_payout_group([payout]) 16 | 17 | def get_payout(self) -> None: 18 | client = ClientProvider.create() 19 | 20 | payout = client.get_payout('myPayoutId') 21 | 22 | payouts = client.get_payouts('2023-08-14', '2023-08-22') 23 | 24 | def cancel_payout(self) -> None: 25 | client = ClientProvider.create() 26 | 27 | client.cancel_payout('payoutId') 28 | 29 | # payout_group = payout.group_id 30 | client.cancel_payout_group('payoutGroupId') 31 | 32 | def request_payout_webhook_to_be_resent(self) -> None: 33 | client = ClientProvider.create() 34 | 35 | client.request_payout_notification('somePayoutId') 36 | -------------------------------------------------------------------------------- /examples/Payout/recipient_requests.py: -------------------------------------------------------------------------------- 1 | from bitpay.models.payout.payout_recipient import PayoutRecipient 2 | from bitpay.models.payout.payout_recipients import PayoutRecipients 3 | from examples.client_provider import ClientProvider 4 | 5 | 6 | class RecipientRequests: 7 | def invite_recipients(self) -> None: 8 | client = ClientProvider.create() 9 | 10 | recipient = PayoutRecipient() 11 | recipient.email = 'some@email.com' 12 | 13 | result = client.submit_payout_recipients( 14 | PayoutRecipients(**{"recipients": [recipient]}) 15 | ) 16 | 17 | def get_recipient(self) -> None: 18 | client = ClientProvider.create() 19 | 20 | result = client.get_payout_recipient('recipientId') 21 | 22 | def update_recipient(self) -> None: 23 | client = ClientProvider.create() 24 | 25 | recipient = PayoutRecipient() 26 | recipient.email = 'some@email.com' 27 | 28 | result = client.update_payout_recipient('recipientId', recipient) 29 | 30 | def remove_recipient(self) -> None: 31 | client = ClientProvider.create() 32 | 33 | result = client.delete_payout_recipient('recipientId') 34 | -------------------------------------------------------------------------------- /examples/Pos/bill_requests.py: -------------------------------------------------------------------------------- 1 | from bitpay.client import Client 2 | from bitpay.environment import Environment 3 | from bitpay.models.bill.bill import Bill 4 | from bitpay.models.facade import Facade 5 | 6 | 7 | class BillRequests: 8 | def create_bill(self) -> None: 9 | client = Client.create_pos_client('somePosToken', Environment.TEST) 10 | 11 | bill = Bill() 12 | bill.name = 'someName' 13 | bill.email = 'someEmail@email.com' 14 | bill.address1 = 'SomeAddress' 15 | bill.city = 'MyCity' 16 | # ... 17 | 18 | result = client.create_bill(bill, Facade.POS, False) 19 | 20 | def get_bill(self) -> None: 21 | client = Client.create_pos_client('somePosToken', Environment.TEST) 22 | 23 | result = client.get_bill('someBillId', Facade.POS, False) 24 | 25 | def deliver_bill_via_email(self) -> None: 26 | client = Client.create_pos_client('somePosToken', Environment.TEST) 27 | 28 | result = client.deliver_bill('someBillId', 'token') 29 | -------------------------------------------------------------------------------- /examples/Pos/invoice_requests.py: -------------------------------------------------------------------------------- 1 | from bitpay.client import Client 2 | from bitpay.environment import Environment 3 | from bitpay.models.facade import Facade 4 | from bitpay.models.invoice.buyer import Buyer 5 | from bitpay.models.invoice.invoice import Invoice 6 | 7 | 8 | class InvoiceRequests: 9 | def create_invoice(self) -> None: 10 | invoice = Invoice() 11 | invoice.full_notifications = True 12 | invoice.extended_notifications = True 13 | invoice.notification_url = "https://test/lJnJg9WW7MtG9GZlPVdj" 14 | invoice.redirect_url = "https://test/lJnJg9WW7MtG9GZlPVdj" 15 | invoice.notification_email = "my@email.com" 16 | 17 | buyer = Buyer() 18 | buyer.name = "Test" 19 | buyer.email = "test@email.com" 20 | buyer.address1 = "168 General Grove" 21 | buyer.country = "AD" 22 | buyer.locality = "Port Horizon" 23 | buyer.notify = True 24 | buyer.phone = "+990123456789" 25 | buyer.postal_code = "KY7 1TH" 26 | buyer.region = "New Port" 27 | 28 | invoice.buyer = buyer 29 | 30 | client = Client.create_pos_client('somePosToken', Environment.TEST) 31 | 32 | result = client.create_invoice(invoice, Facade.POS, False) 33 | 34 | def get_invoice(self) -> None: 35 | client = Client.create_pos_client('somePosToken', Environment.TEST) 36 | 37 | invoice_by_id = client.get_invoice('someInvoiceId', Facade.POS, False) 38 | -------------------------------------------------------------------------------- /examples/Public/rate_requests.py: -------------------------------------------------------------------------------- 1 | from examples.client_provider import ClientProvider 2 | 3 | 4 | class RateRequests: 5 | def get_rate(self) -> None: 6 | client = ClientProvider.create() 7 | 8 | rates = client.get_rates() 9 | 10 | currency_rates = client.get_currency_rates('BTC') 11 | 12 | currency_pair_rate = client.get_currency_pair_rate('BTC', 'USD') 13 | -------------------------------------------------------------------------------- /examples/Public/wallet_requests.py: -------------------------------------------------------------------------------- 1 | from examples.client_provider import ClientProvider 2 | 3 | 4 | class WalletRequests: 5 | def get_supported_wallets(self) -> None: 6 | client = ClientProvider.create() 7 | 8 | rates = client.get_supported_wallets() 9 | -------------------------------------------------------------------------------- /examples/client_provider.py: -------------------------------------------------------------------------------- 1 | from bitpay.client import Client 2 | 3 | 4 | class ClientProvider: 5 | 6 | @staticmethod 7 | def create() -> Client: 8 | return Client.create_client_by_config_file_path("some/path") 9 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "bitpay" 7 | version = "7.0.3" 8 | authors = [ 9 | { name="Antonio Buedo", email="sales-engineering@bitpay.com" }, 10 | ] 11 | requires-python = ">=3.9" 12 | dependencies = [ 13 | "ecdsa >= 0.19.0", 14 | "requests >= 2.32.3", 15 | "pydantic == 2.11.3" 16 | ] 17 | description = "Accept bitcoin with BitPay" 18 | readme = "README.md" 19 | keywords=["bitcoin", "payments", "crypto", "cash", "ethereum", "online payments"] 20 | classifiers = [ 21 | "Programming Language :: Python :: 3.9", 22 | "Programming Language :: Python :: 3.10", 23 | "Programming Language :: Python :: 3.11", 24 | "Programming Language :: Python :: 3.12", 25 | "Programming Language :: Python :: 3.13", 26 | "Development Status :: 5 - Production/Stable", 27 | "Intended Audience :: Developers", 28 | "Natural Language :: English", 29 | "Environment :: Web Environment", 30 | "Intended Audience :: Developers", 31 | "License :: OSI Approved :: MIT License", 32 | "Operating System :: OS Independent", 33 | "Topic :: Software Development :: Libraries :: Python Modules", 34 | "Topic :: Office/Business :: Financial", 35 | ] 36 | 37 | [project.urls] 38 | "Homepage" = "https://github.com/bitpay/python-bitpay-client" 39 | "Bug Tracker" = "https://github.com/bitpay/python-bitpay-client/issues" 40 | 41 | [project.optional-dependencies] 42 | dev = ["black", "pytest", "pytest-mock", "mypy", "pytest-mypy-plugins", "pytest-cov"] 43 | 44 | [tool.setuptools] 45 | package-dir={""= "src"} 46 | 47 | [tool.setuptools.packages.find] 48 | where = ["src"] 49 | 50 | [tool.mypy] 51 | disallow_untyped_defs = true 52 | ignore_missing_imports = true 53 | no_implicit_optional = true 54 | check_untyped_defs = true 55 | show_error_codes = true 56 | warn_unused_ignores = true 57 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | minversion = 7.0 3 | testpaths = tests 4 | python_files = test_*.py 5 | pythonpath = src 6 | addopts = "--import-mode=importlib" 7 | markers = 8 | unit: Unit test 9 | functional: Functional test -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/__init__.py -------------------------------------------------------------------------------- /src/bitpay/.gitignore: -------------------------------------------------------------------------------- 1 | bitpay.config.json -------------------------------------------------------------------------------- /src/bitpay/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/__init__.py -------------------------------------------------------------------------------- /src/bitpay/clients/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/clients/__init__.py -------------------------------------------------------------------------------- /src/bitpay/clients/bill_client.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | from bitpay.clients.bitpay_client import BitPayClient 4 | from bitpay.clients.response_parser import ResponseParser 5 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 6 | from bitpay.exceptions.bitpay_generic_exception import BitPayGenericException 7 | from bitpay.models.bill.bill import Bill 8 | from bitpay.models.facade import Facade 9 | from bitpay.utils.token_container import TokenContainer 10 | 11 | 12 | class BillClient: 13 | __bitpay_client = BitPayClient 14 | __token_container = TokenContainer 15 | 16 | def __init__( 17 | self, bitpay_client: BitPayClient, token_container: TokenContainer 18 | ) -> None: 19 | self.__bitpay_client = bitpay_client 20 | self.__token_container = token_container 21 | 22 | def create( 23 | self, bill: Bill, facade: Facade = Facade.MERCHANT, sign_request: bool = True 24 | ) -> Bill: 25 | """ 26 | Create a BitPay Bill. 27 | 28 | :param Bill bill: A Bill object with request parameters defined. 29 | :param str facade: The facade used to create it. 30 | :param bool sign_request: Signed request. 31 | :return: A BitPay generated Bill object. 32 | :rtype: Bill 33 | :raises BitPayApiException 34 | :raises BitPayGenericException 35 | """ 36 | bill.token = self.__token_container.get_access_token(facade) 37 | response = self.__bitpay_client.post("bills", bill.to_json(), sign_request) 38 | response_json = ResponseParser.response_to_json_string(response) 39 | 40 | try: 41 | return Bill(**response_json) 42 | except Exception as exe: 43 | BitPayExceptionProvider.throw_deserialize_resource_exception( 44 | "Bill", str(exe) 45 | ) 46 | 47 | def get( 48 | self, bill_id: str, facade: Facade = Facade.MERCHANT, sign_request: bool = True 49 | ) -> Bill: 50 | """ 51 | Retrieve a BitPay bill by bill id using the specified facade. 52 | 53 | :param str bill_id: The id of the bill to retrieve. 54 | :param str facade: The facade used to create it. 55 | :param bool sign_request: Signed request. 56 | :return: A BitPay Bill object. 57 | :rtype: Bill 58 | :raises BitPayApiException 59 | :raises BitPayGenericException 60 | """ 61 | params = {"token": self.__token_container.get_access_token(facade)} 62 | response = self.__bitpay_client.get("bills/%s" % bill_id, params, sign_request) 63 | response_json = ResponseParser.response_to_json_string(response) 64 | 65 | try: 66 | return Bill(**response_json) 67 | except Exception as exe: 68 | BitPayExceptionProvider.throw_deserialize_resource_exception( 69 | "Bill", str(exe) 70 | ) 71 | 72 | def get_bills(self, status: Optional[str] = None) -> List[Bill]: 73 | """ 74 | Retrieve a collection of BitPay bills. 75 | 76 | :param str status: The status to filter the bills. 77 | :return: A list of BitPay Bill objects. 78 | :rtype: [Bill] 79 | :raises BitPayApiException 80 | :raises BitPayGenericException 81 | """ 82 | params = {"token": self.__token_container.get_access_token(Facade.MERCHANT)} 83 | if status is not None: 84 | params["status"] = status 85 | response = self.__bitpay_client.get("bills", params, True) 86 | response_json = ResponseParser.response_to_json_string(response) 87 | 88 | bills = [] 89 | 90 | try: 91 | for bill_data in response_json: 92 | bills.append(Bill(**bill_data)) 93 | except Exception as exe: 94 | BitPayExceptionProvider.throw_deserialize_resource_exception( 95 | "Bill", str(exe) 96 | ) 97 | 98 | return bills 99 | 100 | def update(self, bill: Bill, bill_id: str) -> Bill: 101 | """ 102 | Update a BitPay Bill. 103 | 104 | :param Bill bill: A Bill object with the parameters to update defined. 105 | :param str bill_id: The Id of the Bill to update. 106 | :return: An updated Bill object. 107 | :rtype: Bill 108 | :raises BitPayApiException 109 | :raises BitPayGenericException 110 | """ 111 | if bill.token is None: 112 | BitPayExceptionProvider.throw_missing_parameter_exception() 113 | 114 | response = self.__bitpay_client.update("bills/%s" % bill_id, bill.to_json()) 115 | response_json = ResponseParser.response_to_json_string(response) 116 | 117 | try: 118 | return Bill(**response_json) 119 | except Exception as exe: 120 | BitPayExceptionProvider.throw_deserialize_resource_exception( 121 | "Bill", str(exe) 122 | ) 123 | 124 | def deliver(self, bill_id: str, bill_token: str) -> bool: 125 | """ 126 | Deliver a BitPay Bill. 127 | 128 | :param str bill_id: The id of the requested bill. 129 | :param str bill_token: The token of the requested bill. 130 | :return: A response status returned from the API. 131 | :rtype: bool 132 | :raises BitPayApiException 133 | :raises BitPayGenericException 134 | """ 135 | params = {"token": bill_token} 136 | response = self.__bitpay_client.post( 137 | "bills/%s" % bill_id + "/deliveries", params 138 | ) 139 | response_json = ResponseParser.response_to_json_string(response) 140 | 141 | try: 142 | return response_json.lower() == "success" 143 | except Exception as exe: 144 | BitPayExceptionProvider.throw_deserialize_resource_exception( 145 | "Bill", str(exe) 146 | ) 147 | raise BitPayGenericException 148 | -------------------------------------------------------------------------------- /src/bitpay/clients/bitpay_client.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib 3 | from datetime import datetime, timezone 4 | from typing import Dict, Any, Optional 5 | 6 | import requests 7 | from requests import Response 8 | 9 | from bitpay.config import Config 10 | from bitpay.logger.logger_provider import LoggerProvider 11 | from bitpay.utils.key_utils import sign, get_compressed_public_key_from_pem 12 | from urllib.parse import urlparse 13 | 14 | 15 | class BitPayClient: 16 | __headers: Dict[str, str] = {} 17 | __base_url: str 18 | __ec_key: Optional[str] = None 19 | __proxy: Optional[str] = None 20 | __platform_info: Optional[str] = None 21 | 22 | def __init__( 23 | self, 24 | base_url: str, 25 | ec_key: Optional[str] = None, 26 | proxy: Optional[str] = None, 27 | platform_info: Optional[str] = None, 28 | ): 29 | self.__base_url = base_url 30 | self.__ec_key = ec_key 31 | self.__proxy = proxy 32 | self.__platform_info = platform_info 33 | self.init() 34 | 35 | def init(self) -> None: 36 | self.__headers = { 37 | "x-accept-version": Config.BITPAY_API_VERSION.value, 38 | "x-bitpay-plugin-info": Config.BITPAY_PLUGIN_INFO.value, 39 | "x-bitpay-api-frame": Config.BITPAY_API_FRAME.value, 40 | "x-bitpay-api-frame-version": Config.BITPAY_API_FRAME_VERSION.value, 41 | "content-type": "application/json", 42 | "X-accept-version": "2.0.0", 43 | } 44 | if self.__proxy is not None: 45 | self.__headers["proxy"] = self.__proxy 46 | 47 | if self.__platform_info is not None: 48 | self.__headers["x-bitpay-platform-info"] = self.__platform_info 49 | 50 | def post( 51 | self, uri: str, form_data: Any = {}, signature_required: bool = True 52 | ) -> Response: 53 | """ 54 | :param uri: Url at which user wants to post the data 55 | :param form_data: the data to be posted 56 | :param signature_required: Signature of the full request URL concatenated 57 | with the request body 58 | :return: json response 59 | :raises BitPayGenericException 60 | :raises BitPayApiException 61 | """ 62 | full_url = self.__base_url + uri 63 | 64 | LoggerProvider.get_logger().log_request("POST", uri, form_data) 65 | 66 | form_data = json.dumps(form_data, default=json_serialize) 67 | if signature_required: 68 | self.__headers["x-signature"] = sign(full_url + form_data, self.__ec_key) 69 | self.__headers["x-identity"] = get_compressed_public_key_from_pem( 70 | self.__ec_key 71 | ) 72 | 73 | return requests.post(full_url, data=form_data, headers=self.__headers) 74 | 75 | def get( 76 | self, 77 | uri: str, 78 | parameters: Optional[dict] = None, 79 | signature_required: bool = True, 80 | ) -> Response: 81 | """ 82 | 83 | :param uri: Url from which user wants to get the data 84 | :param parameters: These are query parameters 85 | :param signature_required: Signature of the full request URL concatenated 86 | :return: json response 87 | :raises BitPayGenericException 88 | :raises BitPayApiException 89 | """ 90 | full_url = self.__base_url + uri 91 | if parameters is not None: 92 | full_url = "%s?%s" % (full_url, urllib.parse.urlencode(parameters)) 93 | 94 | if signature_required: 95 | self.__headers["x-signature"] = sign(full_url, self.__ec_key) 96 | self.__headers["x-identity"] = get_compressed_public_key_from_pem( 97 | self.__ec_key 98 | ) 99 | 100 | return requests.get(full_url, headers=self.__headers) 101 | 102 | def delete(self, uri: str, parameters: Optional[dict] = None) -> Response: 103 | """ 104 | 105 | :param uri: Url from which user wants to delete the data 106 | :param parameters: These are query parameters 107 | :return: json response 108 | :raises BitPayGenericException 109 | :raises BitPayApiException 110 | """ 111 | full_url = self.__base_url + uri 112 | 113 | if parameters is not None: 114 | full_url = "%s?%s" % (full_url, urllib.parse.urlencode(parameters)) 115 | 116 | self.__headers["x-signature"] = sign(full_url, self.__ec_key) 117 | self.__headers["x-identity"] = get_compressed_public_key_from_pem(self.__ec_key) 118 | 119 | return requests.delete(full_url, headers=self.__headers) 120 | 121 | def update(self, uri: str, form_data: Any = {}) -> Response: 122 | """ 123 | :param uri: Url from which user wants to delete the data 124 | :param form_data: the data to be updated 125 | :return: json response 126 | :raises BitPayGenericException 127 | :raises BitPayApiException 128 | """ 129 | 130 | full_url = self.__base_url + uri 131 | form_data = json.dumps(form_data, default=json_serialize) 132 | 133 | self.__headers["x-signature"] = sign(full_url + form_data, self.__ec_key) 134 | self.__headers["x-identity"] = get_compressed_public_key_from_pem(self.__ec_key) 135 | 136 | return requests.put(full_url, data=form_data, headers=self.__headers) 137 | 138 | 139 | def json_serialize(obj) -> Any: # type: ignore 140 | if isinstance(obj, datetime): 141 | return obj.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") 142 | raise TypeError("Type %s not serializable" % type(obj)) 143 | -------------------------------------------------------------------------------- /src/bitpay/clients/currency_client.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from bitpay.clients.bitpay_client import BitPayClient 4 | from bitpay.clients.response_parser import ResponseParser 5 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 6 | from bitpay.models.currency import Currency 7 | 8 | 9 | class CurrencyClient: 10 | __bitpay_client = BitPayClient 11 | 12 | def __init__(self, bitpay_client: BitPayClient): 13 | self.__bitpay_client = bitpay_client 14 | 15 | def get_currencies(self) -> Dict[str, Currency]: 16 | """ 17 | Fetch the supported currencies. 18 | 19 | :return: A list of BitPay Invoice objects. 20 | :rtype: [Currency] 21 | :raises BitPayApiException 22 | :raises BitPayGenericException 23 | """ 24 | response = self.__bitpay_client.get("currencies", None, False) 25 | response_json = ResponseParser.response_to_json_string(response) 26 | 27 | currencies = {} 28 | 29 | try: 30 | for currency in response_json: 31 | currency_obj = Currency(**currency) 32 | currencies[currency_obj.code] = currency_obj 33 | except Exception as exe: 34 | BitPayExceptionProvider.throw_deserialize_resource_exception( 35 | "Currency", str(exe) 36 | ) 37 | 38 | return currencies 39 | -------------------------------------------------------------------------------- /src/bitpay/clients/ledger_client.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from bitpay.clients.bitpay_client import BitPayClient 4 | from bitpay.clients.response_parser import ResponseParser 5 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 6 | from bitpay.models.facade import Facade 7 | from bitpay.models.ledger.ledger import Ledger 8 | from bitpay.models.ledger.ledger_entry import LedgerEntry 9 | from bitpay.utils.token_container import TokenContainer 10 | 11 | 12 | class LedgerClient: 13 | __bitpay_client = BitPayClient 14 | __token_container = TokenContainer 15 | 16 | def __init__(self, bitpay_client: BitPayClient, token_container: TokenContainer): 17 | self.__bitpay_client = bitpay_client 18 | self.__token_container = token_container 19 | 20 | def get_entries( 21 | self, currency: str, start_date: str, end_date: str 22 | ) -> List[LedgerEntry]: 23 | """ 24 | Retrieve a list of ledgers by date range using the merchant facade. 25 | 26 | :param str currency: The three digit currency string for the ledger to retrieve. 27 | :param str start_date: The first date for the query filter. 28 | :param str end_date: The last date for the query filter. 29 | :return: A LedgerEntry object populated with the BitPay ledger entries list. 30 | :rtype: [LedgerEntry] 31 | :raises BitPayApiException 32 | :raises BitPayGenericException 33 | """ 34 | params = { 35 | "token": self.__token_container.get_access_token(Facade.MERCHANT), 36 | "currency": currency, 37 | "startDate": start_date, 38 | "endDate": end_date, 39 | } 40 | response = self.__bitpay_client.get("ledgers/%s" % currency, params) 41 | response_json = ResponseParser.response_to_json_string(response) 42 | 43 | try: 44 | ledgers = [] 45 | for ledger in response_json: 46 | ledgers.append(LedgerEntry(**ledger)) 47 | except Exception as exe: 48 | BitPayExceptionProvider.throw_deserialize_resource_exception( 49 | "Ledger", str(exe) 50 | ) 51 | raise 52 | 53 | return ledgers 54 | 55 | def get_ledgers(self) -> List[Ledger]: 56 | """ 57 | Retrieve a list of ledgers using the merchant facade. 58 | 59 | :return: A list of Ledger objects populated with the currency and 60 | current balance of each one. 61 | :rtype [Ledger] 62 | :raises BitPayApiException 63 | :raises BitPayGenericException 64 | """ 65 | params = {"token": self.__token_container.get_access_token(Facade.MERCHANT)} 66 | response = self.__bitpay_client.get("ledgers", params) 67 | response_json = ResponseParser.response_to_json_string(response) 68 | 69 | try: 70 | ledgers = [] 71 | for ledger in response_json: 72 | ledgers.append(Ledger(**ledger)) 73 | except Exception as exe: 74 | BitPayExceptionProvider.throw_deserialize_resource_exception( 75 | "Ledger", str(exe) 76 | ) 77 | raise 78 | 79 | return ledgers 80 | -------------------------------------------------------------------------------- /src/bitpay/clients/rate_client.py: -------------------------------------------------------------------------------- 1 | from bitpay.clients.bitpay_client import BitPayClient 2 | from bitpay.clients.response_parser import ResponseParser 3 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 4 | from bitpay.models.rate.rate import Rate 5 | from bitpay.models.rate.rates import Rates 6 | 7 | 8 | class RateClient: 9 | __bitpay_client = BitPayClient 10 | 11 | def __init__(self, bitpay_client: BitPayClient): 12 | self.__bitpay_client = bitpay_client 13 | 14 | def get_rates(self) -> Rates: 15 | """ 16 | Retrieve the exchange rate table maintained by BitPay. See https://bitpay.com/bitcoin-exchange-rates. 17 | 18 | :return: A Rates object populated with the BitPay exchange rate table. 19 | :rtype: [Rates] 20 | :raises BitPayApiException 21 | :raises BitPayGenericException 22 | """ 23 | response = self.__bitpay_client.get("rates", None, False) 24 | response_json = ResponseParser.response_to_json_string(response) 25 | 26 | rates = [] 27 | 28 | try: 29 | for rate in response_json: 30 | rates.append(Rate(**rate)) 31 | return Rates(rates=rates) 32 | except Exception as exe: 33 | BitPayExceptionProvider.throw_deserialize_resource_exception( 34 | "Rates", str(exe) 35 | ) 36 | 37 | def get_currency_rates(self, base_currency: str) -> Rates: 38 | """ 39 | Retrieve all the rates for a given cryptocurrency 40 | 41 | :param str base_currency: The cryptocurrency for which you want to fetch the rates. 42 | Current supported values are BTC, BCH, ETH, XRP, DOGE and LTC 43 | :return: A Rates object populated with the currency rates for the requested baseCurrency. 44 | :rtype: [Rates] 45 | :raises BitPayApiException 46 | :raises BitPayGenericException 47 | """ 48 | response = self.__bitpay_client.get("rates/%s" % base_currency, None, False) 49 | response_json = ResponseParser.response_to_json_string(response) 50 | 51 | try: 52 | if isinstance(response_json, dict): 53 | response_json = [response_json] 54 | 55 | rates = [] 56 | for rate in response_json: 57 | rates.append(Rate(**rate)) 58 | return Rates(rates=rates) 59 | except Exception as exe: 60 | BitPayExceptionProvider.throw_deserialize_resource_exception( 61 | "Rates", str(exe) 62 | ) 63 | 64 | def get_currency_pair_rate(self, base_currency: str, currency: str) -> Rate: 65 | """ 66 | Retrieve the rate for a cryptocurrency / fiat pair 67 | 68 | :param str base_currency: The cryptocurrency for which you want to fetch the fiat-equivalent rate. 69 | Current supported values are BTC, BCH, ETH, XRP, DOGE and LTC 70 | :param str currency: The fiat currency for which you want to fetch the baseCurrency rate 71 | :return: A rate object populated with the currency rate for the requested baseCurrency. 72 | :rtype: Rate 73 | :raises BitPayApiException 74 | :raises BitPayGenericException 75 | """ 76 | response = self.__bitpay_client.get( 77 | "rates/%s" % base_currency + "/%s" % currency, None, False 78 | ) 79 | response_json = ResponseParser.response_to_json_string(response) 80 | 81 | try: 82 | return Rate(**response_json) 83 | except Exception as exe: 84 | BitPayExceptionProvider.throw_deserialize_resource_exception( 85 | "Rate", str(exe) 86 | ) 87 | -------------------------------------------------------------------------------- /src/bitpay/clients/response_parser.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from requests import Response 4 | 5 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 6 | from bitpay.logger.logger_provider import LoggerProvider 7 | 8 | 9 | class ResponseParser: 10 | @staticmethod 11 | def response_to_json_string(response: Response) -> Any: 12 | """ 13 | :raises BitPayApiException 14 | """ 15 | if not response.json(): 16 | BitPayExceptionProvider.throw_api_exception_with_message( 17 | "HTTP response is null" 18 | ) 19 | 20 | response_obj = response.json() 21 | 22 | request = response.request 23 | LoggerProvider.get_logger().log_response( 24 | request.method, request.url, response_obj 25 | ) 26 | 27 | if "status" in response_obj: 28 | if response_obj["status"] == "error": 29 | BitPayExceptionProvider.throw_api_exception_with_message( 30 | response_obj["error"], response_obj["code"] 31 | ) 32 | if "data" in response_obj and len(response_obj["data"]) == 0: 33 | return response_obj 34 | 35 | if "error" in response_obj: 36 | BitPayExceptionProvider.throw_api_exception_with_message( 37 | response_obj["error"] 38 | ) 39 | 40 | if "errors" in response_obj: 41 | final_message = "" 42 | for error in response_obj["errors"]: 43 | error_value = error.get("error", "") 44 | param_value = error.get("param", "") 45 | 46 | if error_value.endswith("."): 47 | error_value = error_value[:-1] 48 | 49 | if error_value: 50 | result = f"{error_value} {param_value}".strip() 51 | else: 52 | result = param_value.strip() 53 | 54 | if not result.endswith("."): 55 | result += "." 56 | 57 | if not final_message == "": 58 | result = " " + result 59 | 60 | final_message += result 61 | BitPayExceptionProvider.throw_api_exception_with_message(final_message) 62 | 63 | if "success" in response_obj: 64 | return response_obj["success"] 65 | 66 | if "data" in response_obj: 67 | return response_obj["data"] 68 | 69 | return response_obj 70 | -------------------------------------------------------------------------------- /src/bitpay/clients/settlement_client.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | from bitpay.clients.bitpay_client import BitPayClient 4 | from bitpay.clients.response_parser import ResponseParser 5 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 6 | from bitpay.models.facade import Facade 7 | from bitpay.models.settlement.settlement import Settlement 8 | from bitpay.utils.token_container import TokenContainer 9 | 10 | 11 | class SettlementClient: 12 | __bitpay_client = BitPayClient 13 | __token_container = TokenContainer 14 | 15 | def __init__(self, bitpay_client: BitPayClient, token_container: TokenContainer): 16 | self.__bitpay_client = bitpay_client 17 | self.__token_container = token_container 18 | 19 | def get_settlements( 20 | self, 21 | currency: Optional[str] = None, 22 | date_start: Optional[str] = None, 23 | date_end: Optional[str] = None, 24 | status: Optional[str] = None, 25 | limit: int = 100, 26 | offset: int = 0, 27 | ) -> List[Settlement]: 28 | """ 29 | Retrieves settlement reports for the calling merchant filtered by query. The `limit` 30 | and `offset` parameters specify pages for large query sets. 31 | 32 | :param str currency: The three digit currency string for the ledger to retrieve. 33 | :param str date_start: The start date for the query. 34 | :param str date_end: The end date for the query. 35 | :param str status: Can be `processing`, `completed`, or `failed`. 36 | :param int limit: Maximum number of settlements to retrieve. 37 | :param int offset: Offset for paging 38 | :return: A list of BitPay Settlement objects 39 | :rtype: [Settlement] 40 | :raises BitPayApiException 41 | :raises BitPayGenericException 42 | """ 43 | params = { 44 | "token": self.__token_container.get_access_token(Facade.MERCHANT), 45 | "limit": limit, 46 | "offset": offset, 47 | } 48 | 49 | if date_start is not None: 50 | params["startDate"] = date_start 51 | 52 | if date_end is not None: 53 | params["endDate"] = date_end 54 | 55 | if currency is not None: 56 | params["currency"] = currency 57 | 58 | if status is not None: 59 | params["status"] = status 60 | 61 | response = self.__bitpay_client.get("settlements", params, True) 62 | response_json = ResponseParser.response_to_json_string(response) 63 | 64 | settlements = [] 65 | 66 | try: 67 | for settlement in response_json: 68 | settlements.append(Settlement(**settlement)) 69 | except Exception as exe: 70 | BitPayExceptionProvider.throw_deserialize_resource_exception( 71 | "Settlement", str(exe) 72 | ) 73 | 74 | return settlements 75 | 76 | def get(self, settlement_id: str) -> Settlement: 77 | """ 78 | Retrieves a summary of the specified settlement. 79 | 80 | :param str settlement_id: Settlement Id 81 | :return: A BitPay Settlement object. 82 | :rtype: Settlement 83 | :raises BitPayApiException 84 | :raises BitPayGenericException 85 | """ 86 | params = {"token": self.__token_container.get_access_token(Facade.MERCHANT)} 87 | response = self.__bitpay_client.get("settlements/%s" % settlement_id, params) 88 | response_json = ResponseParser.response_to_json_string(response) 89 | 90 | try: 91 | settlement = Settlement(**response_json) 92 | except Exception as exe: 93 | BitPayExceptionProvider.throw_deserialize_resource_exception( 94 | "Settlement", str(exe) 95 | ) 96 | raise 97 | 98 | return settlement 99 | 100 | def get_reconciliation_report( 101 | self, settlement_id: str, settlement_token: str 102 | ) -> Settlement: 103 | """ 104 | Gets a detailed reconciliation report of the activity within the settlement period 105 | 106 | :param str settlement_id: Settlement id to generate report for. 107 | :param str settlement_token: Settlement token to generate report for. 108 | :return: A detailed BitPay Settlement object. 109 | :rtype: Settlement 110 | :raises BitPayApiException 111 | :raises BitPayGenericException 112 | """ 113 | params = {"token": settlement_token} 114 | response = self.__bitpay_client.get( 115 | "settlements/%s" % settlement_id + "/reconciliationReport", params 116 | ) 117 | response_json = ResponseParser.response_to_json_string(response) 118 | 119 | try: 120 | settlement = Settlement(**response_json) 121 | except Exception as exe: 122 | BitPayExceptionProvider.throw_deserialize_resource_exception( 123 | "Settlement", str(exe) 124 | ) 125 | raise 126 | 127 | return settlement 128 | -------------------------------------------------------------------------------- /src/bitpay/clients/wallet_client.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from bitpay.clients.bitpay_client import BitPayClient 4 | from bitpay.clients.response_parser import ResponseParser 5 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 6 | from bitpay.models.wallet.wallet import Wallet 7 | 8 | 9 | class WalletClient: 10 | __bitpay_client = BitPayClient 11 | 12 | def __init__(self, bitpay_client: BitPayClient): 13 | self.__bitpay_client = bitpay_client 14 | 15 | def get_supported_wallets(self) -> List[Wallet]: 16 | """ 17 | Retrieve all supported wallets. 18 | 19 | :return: A list of wallet objets. 20 | :rtype: [Wallet] 21 | :raises BitPayApiException 22 | :raises BitPayGenericException 23 | """ 24 | response = self.__bitpay_client.get("supportedWallets/", None, False) 25 | response_json = ResponseParser.response_to_json_string(response) 26 | 27 | try: 28 | wallets = [] 29 | for wallet in response_json: 30 | wallets.append(Wallet(**wallet)) 31 | except Exception as exe: 32 | BitPayExceptionProvider.throw_deserialize_resource_exception( 33 | "Wallet", str(exe) 34 | ) 35 | 36 | return wallets 37 | -------------------------------------------------------------------------------- /src/bitpay/config.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Config(Enum): 5 | TEST_URL = "https://test.bitpay.com/" 6 | PROD_URL = "https://bitpay.com/" 7 | BITPAY_API_VERSION = "2.0.0" 8 | BITPAY_PLUGIN_INFO = "BitPay_Python_Client_v7.0.3" 9 | BITPAY_API_FRAME = "std" 10 | BITPAY_API_FRAME_VERSION = "1.0.0" 11 | -------------------------------------------------------------------------------- /src/bitpay/environment.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Environment(Enum): 5 | TEST = "Test" 6 | PROD = "Prod" 7 | -------------------------------------------------------------------------------- /src/bitpay/exceptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/exceptions/__init__.py -------------------------------------------------------------------------------- /src/bitpay/exceptions/bitpay_api_exception.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from bitpay.exceptions.bitpay_exception import BitPayException 4 | 5 | 6 | class BitPayApiException(BitPayException): 7 | """ 8 | BitPayApiException 9 | """ 10 | 11 | __message: str 12 | __code: Union[str, None] = None 13 | 14 | def __init__(self, message: str, code: Union[str, None]): 15 | """ 16 | Construct the BitPayApiException. 17 | 18 | :param message: The Exception message to throw. 19 | :param code: [optional] The API Exception. 20 | """ 21 | self.__code = code 22 | super().__init__(message) 23 | 24 | def get_code(self) -> Union[str, None]: 25 | """ 26 | :return: Error code provided by the BitPay REST API 27 | """ 28 | return self.__code 29 | -------------------------------------------------------------------------------- /src/bitpay/exceptions/bitpay_exception.py: -------------------------------------------------------------------------------- 1 | """ 2 | BitPay Exception gets raised when some unexpected error occurs while processing a request 3 | """ 4 | 5 | 6 | class BitPayException(Exception): 7 | """ 8 | BitPayException 9 | """ 10 | 11 | __message: str 12 | 13 | def __init__(self, message: str): 14 | """ 15 | Construct the BitPayException. 16 | 17 | :param message: The Exception message to throw. 18 | """ 19 | self.__message = message 20 | super().__init__(message) 21 | 22 | def get_message(self) -> str: 23 | return self.__message 24 | -------------------------------------------------------------------------------- /src/bitpay/exceptions/bitpay_exception_provider.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from bitpay.exceptions.bitpay_api_exception import BitPayApiException 4 | from bitpay.exceptions.bitpay_generic_exception import BitPayGenericException 5 | from bitpay.exceptions.bitpay_validation_exception import BitPayValidationException 6 | from bitpay.logger.logger_provider import LoggerProvider 7 | 8 | 9 | class BitPayExceptionProvider: 10 | @staticmethod 11 | def throw_generic_exception_with_message(message: str) -> None: 12 | BitPayExceptionProvider.log_error_message(message) 13 | raise BitPayGenericException(message) 14 | 15 | @staticmethod 16 | def throw_api_exception_with_message( 17 | message: str, code: Union[str, None] = None 18 | ) -> None: 19 | BitPayExceptionProvider.log_error_message(message) 20 | raise BitPayApiException(message, code) 21 | 22 | @staticmethod 23 | def throw_validation_exception(message: str) -> None: 24 | BitPayExceptionProvider.log_error_message(message) 25 | raise BitPayValidationException(message) 26 | 27 | @staticmethod 28 | def throw_missing_parameter_exception() -> None: 29 | message = "Missing required parameter" 30 | BitPayExceptionProvider.log_error_message(message) 31 | raise BitPayValidationException(message) 32 | 33 | @staticmethod 34 | def throw_deserialize_resource_exception( 35 | resource: str, exception_message: str 36 | ) -> None: 37 | message = "Failed to deserialize BitPay server response ( %s ) : %s" % ( 38 | resource, 39 | exception_message, 40 | ) 41 | BitPayExceptionProvider.throw_generic_exception_with_message(message) 42 | 43 | @staticmethod 44 | def throw_deserialize_exception(exception_message: str) -> None: 45 | message = "Failed to deserialize BitPay server response : " + exception_message 46 | BitPayExceptionProvider.throw_generic_exception_with_message(message) 47 | 48 | @staticmethod 49 | def throw_encode_exception(exception_message: str) -> None: 50 | message = "Failed to encode params : " + exception_message 51 | BitPayExceptionProvider.throw_generic_exception_with_message(message) 52 | 53 | @staticmethod 54 | def throw_serialize_resource_exception( 55 | resource: str, exception_message: str 56 | ) -> None: 57 | message = "Failed to serialize ( %s ) : %s" % (resource, exception_message) 58 | BitPayExceptionProvider.throw_generic_exception_with_message(message) 59 | 60 | @staticmethod 61 | def throw_serialize_params_exception(error_message: str) -> None: 62 | message = "Failed to serialize params : " + error_message 63 | BitPayExceptionProvider.throw_generic_exception_with_message(message) 64 | 65 | @staticmethod 66 | def throw_invalid_currency_exception(currency_code: str) -> None: 67 | message = "Currency code %s must be a type of Model.Currency" % currency_code 68 | BitPayExceptionProvider.throw_validation_exception(message) 69 | 70 | @staticmethod 71 | def log_error_message(message: str) -> None: 72 | LoggerProvider.get_logger().log_error(message) 73 | -------------------------------------------------------------------------------- /src/bitpay/exceptions/bitpay_generic_exception.py: -------------------------------------------------------------------------------- 1 | from bitpay.exceptions.bitpay_exception import BitPayException 2 | 3 | 4 | class BitPayGenericException(BitPayException): 5 | def __init__(self, message: str): 6 | super().__init__(message) 7 | -------------------------------------------------------------------------------- /src/bitpay/exceptions/bitpay_validation_exception.py: -------------------------------------------------------------------------------- 1 | from bitpay.exceptions.bitpay_generic_exception import BitPayGenericException 2 | 3 | 4 | class BitPayValidationException(BitPayGenericException): 5 | def __init__(self, message: str): 6 | super().__init__(message) 7 | -------------------------------------------------------------------------------- /src/bitpay/logger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/logger/__init__.py -------------------------------------------------------------------------------- /src/bitpay/logger/bitpay_logger.py: -------------------------------------------------------------------------------- 1 | class BitPayLogger: 2 | def log_request(self, method: str, endpoint: str, json: dict) -> None: 3 | pass 4 | 5 | def log_response(self, method: str, endpoint: str, json: dict) -> None: 6 | pass 7 | 8 | def log_error(self, message: str) -> None: 9 | pass 10 | -------------------------------------------------------------------------------- /src/bitpay/logger/logger_provider.py: -------------------------------------------------------------------------------- 1 | from bitpay.exceptions.bitpay_generic_exception import BitPayGenericException 2 | from bitpay.logger.bitpay_logger import BitPayLogger 3 | 4 | 5 | class LoggerProvider: 6 | __logger = None 7 | 8 | def __init__(self) -> None: 9 | raise BitPayGenericException("This class should not be instantiated.") 10 | 11 | @staticmethod 12 | def get_logger() -> BitPayLogger: 13 | if LoggerProvider.__logger is None: 14 | LoggerProvider.__logger = BitPayLogger() 15 | return LoggerProvider.__logger 16 | 17 | @staticmethod 18 | def set_logger(logger: BitPayLogger) -> None: 19 | LoggerProvider.__logger = logger 20 | -------------------------------------------------------------------------------- /src/bitpay/logger/logging_logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from bitpay.logger.bitpay_logger import BitPayLogger 4 | 5 | 6 | class LoggingLogger(BitPayLogger): 7 | def __init__(self) -> None: 8 | logging.basicConfig( 9 | level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s" 10 | ) 11 | 12 | def log_request(self, method: str, endpoint: str, json: dict) -> None: 13 | logging.info( 14 | "Request method: " 15 | + method 16 | + " Endpoint: " 17 | + endpoint 18 | + " Json: " 19 | + str(json) 20 | ) 21 | 22 | def log_response(self, method: str, endpoint: str, json: dict) -> None: 23 | logging.info( 24 | "Response method: " 25 | + method 26 | + " Endpoint: " 27 | + endpoint 28 | + " Json: " 29 | + str(json) 30 | ) 31 | 32 | def log_error(self, message: str) -> None: 33 | logging.error(message) 34 | -------------------------------------------------------------------------------- /src/bitpay/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/models/__init__.py -------------------------------------------------------------------------------- /src/bitpay/models/bill/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/models/bill/__init__.py -------------------------------------------------------------------------------- /src/bitpay/models/bill/bill.py: -------------------------------------------------------------------------------- 1 | """ 2 | Bill 3 | """ 4 | 5 | from datetime import datetime, timezone 6 | from typing import List, Union 7 | 8 | from pydantic import field_serializer 9 | 10 | from .item import Item 11 | from ..bitpay_model import BitPayModel 12 | 13 | 14 | class Bill(BitPayModel): 15 | """ 16 | Bills are payment requests addressed to specific buyers. 17 | Bill line items have fixed prices, typically denominated 18 | in fiat currency. 19 | """ 20 | 21 | currency: Union[str, None] = None 22 | token: Union[str, None] = None 23 | email: Union[str, None] = None 24 | items: Union[List[Item], None] = None 25 | number: Union[str, None] = None 26 | name: Union[str, None] = None 27 | address1: Union[str, None] = None 28 | address2: Union[str, None] = None 29 | city: Union[str, None] = None 30 | state: Union[str, None] = None 31 | zip: Union[str, None] = None 32 | country: Union[str, None] = None 33 | cc: Union[List[str], None] = None 34 | phone: Union[str, None] = None 35 | due_date: Union[datetime, None] = None 36 | pass_processing_fee: bool = False 37 | status: Union[str, None] = None 38 | url: Union[str, None] = None 39 | created_date: Union[datetime, None] = None 40 | id: Union[str, None] = None 41 | merchant: Union[str, None] = None 42 | 43 | @field_serializer("due_date", "created_date") 44 | def serialize_datetime(self, dt: datetime) -> str: 45 | return super().serialize_datetime_to_iso8601(dt) 46 | -------------------------------------------------------------------------------- /src/bitpay/models/bill/bill_status.py: -------------------------------------------------------------------------------- 1 | """ 2 | BillStatus 3 | """ 4 | 5 | from enum import Enum 6 | 7 | 8 | class BillStatus(Enum): 9 | """ 10 | Bill Status 11 | """ 12 | 13 | DRAFT = "draft" 14 | SENT = "sent" 15 | NEW = "new" 16 | PAID = "paid" 17 | COMPLETE = "complete" 18 | -------------------------------------------------------------------------------- /src/bitpay/models/bill/item.py: -------------------------------------------------------------------------------- 1 | """ 2 | Item 3 | """ 4 | 5 | from typing import Union 6 | from bitpay.models.bitpay_model import BitPayModel 7 | 8 | 9 | class Item(BitPayModel): 10 | """ 11 | List of line items 12 | """ 13 | 14 | id: Union[str, None] = None 15 | description: Union[str, None] = None 16 | price: Union[float, None] = None 17 | quantity: Union[int, None] = None 18 | -------------------------------------------------------------------------------- /src/bitpay/models/bitpay_model.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Union 3 | 4 | from pydantic import BaseModel, field_validator, ConfigDict 5 | 6 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 7 | from bitpay.utils.model_util import ModelUtil 8 | 9 | 10 | def snake_to_camel(snake_case: str) -> str: 11 | return ModelUtil.convert_snake_case_fields_to_camel_case(snake_case) 12 | 13 | 14 | class BitPayModel(BaseModel): 15 | model_config = ConfigDict(alias_generator=snake_to_camel, populate_by_name=True) 16 | 17 | def to_json(self) -> dict: 18 | """ 19 | :return: data in json 20 | """ 21 | return self.model_dump(exclude_unset=True, by_alias=True) 22 | 23 | def serialize_datetime_to_iso8601(self, dt: datetime) -> str: 24 | return dt.isoformat(timespec="microseconds")[:-4] + "Z" 25 | 26 | @field_validator("currency", check_fields=False) 27 | def validate_currency(cls, currency_code: Union[str, None]) -> Union[str, None]: 28 | """ 29 | :raises BitPayGenericException 30 | """ 31 | from bitpay.models.currency import Currency 32 | 33 | if currency_code is None: 34 | return currency_code 35 | currency_code = currency_code.upper() 36 | if Currency.is_valid(currency_code) is False: 37 | BitPayExceptionProvider.throw_generic_exception_with_message( 38 | "currency code must be a type of Model.Currency" 39 | ) 40 | return currency_code 41 | -------------------------------------------------------------------------------- /src/bitpay/models/currency.py: -------------------------------------------------------------------------------- 1 | """ 2 | Currency 3 | """ 4 | 5 | from typing import Union 6 | 7 | from bitpay.models.bitpay_model import BitPayModel 8 | 9 | 10 | class Currency(BitPayModel): 11 | """ 12 | Currency: Crypto and fiat 13 | """ 14 | 15 | # Crypto 16 | BCH: str = "BCH" 17 | BTC: str = "BTC" 18 | ETH: str = "ETH" 19 | USDC: str = "USDC" 20 | GUSD: str = "GUSD" 21 | PAX: str = "PAX" 22 | XRP: str = "XRP" 23 | BUSD: str = "BUSD" 24 | DOGE: str = "DOGE" 25 | WBTC: str = "WBTC" 26 | DAI: str = "DAI" 27 | LTC: str = "LTC" 28 | SHIB: str = "SHIB" 29 | 30 | # Fiat 31 | AED: str = "AED" 32 | AFN: str = "AFN" 33 | ALL: str = "ALL" 34 | AMD: str = "AMD" 35 | ANG: str = "ANG" 36 | AOA: str = "AOA" 37 | ARS: str = "ARS" 38 | AUD: str = "AUD" 39 | AWG: str = "AWG" 40 | AZN: str = "AZN" 41 | BAM: str = "BAM" 42 | BBD: str = "BBD" 43 | BDT: str = "BDT" 44 | BGN: str = "BGN" 45 | BHD: str = "BHD" 46 | BIF: str = "BIF" 47 | BMD: str = "BMD" 48 | BND: str = "BND" 49 | BOB: str = "BOB" 50 | BOV: str = "BOV" 51 | BRL: str = "BRL" 52 | BSD: str = "BSD" 53 | BTN: str = "BTN" 54 | BWP: str = "BWP" 55 | BYR: str = "BYR" 56 | BZD: str = "BZD" 57 | CAD: str = "CAD" 58 | CDF: str = "CDF" 59 | CHE: str = "CHE" 60 | CHF: str = "CHF" 61 | CHW: str = "CHW" 62 | CLF: str = "CLF" 63 | CLP: str = "CLP" 64 | CNY: str = "CNY" 65 | COP: str = "COP" 66 | COU: str = "COU" 67 | CRC: str = "CRC" 68 | CUC: str = "CUC" 69 | CUP: str = "CUP" 70 | CVE: str = "CVE" 71 | CZK: str = "CZK" 72 | DJF: str = "DJF" 73 | DKK: str = "DKK" 74 | DOP: str = "DOP" 75 | DZD: str = "DZD" 76 | EGP: str = "EGP" 77 | ERN: str = "ERN" 78 | ETB: str = "ETB" 79 | EUR: str = "EUR" 80 | FJD: str = "FJD" 81 | FKP: str = "FKP" 82 | GBP: str = "GBP" 83 | GEL: str = "GEL" 84 | GHS: str = "GHS" 85 | GIP: str = "GIP" 86 | GMD: str = "GMD" 87 | GNF: str = "GNF" 88 | GTQ: str = "GTQ" 89 | GYD: str = "GYD" 90 | HKD: str = "HKD" 91 | HNL: str = "HNL" 92 | HRK: str = "HRK" 93 | HTG: str = "HTG" 94 | HUF: str = "HUF" 95 | IDR: str = "IDR" 96 | ILS: str = "ILS" 97 | INR: str = "INR" 98 | IQD: str = "IQD" 99 | IRR: str = "IRR" 100 | ISK: str = "ISK" 101 | JMD: str = "JMD" 102 | JOD: str = "JOD" 103 | JPY: str = "JPY" 104 | KES: str = "KES" 105 | KGS: str = "KGS" 106 | KHR: str = "KHR" 107 | KMF: str = "KMF" 108 | KPW: str = "KPW" 109 | KRW: str = "KRW" 110 | KWD: str = "KWD" 111 | KYD: str = "KYD" 112 | KZT: str = "KZT" 113 | LAK: str = "LAK" 114 | LBP: str = "LBP" 115 | LKR: str = "LKR" 116 | LRD: str = "LRD" 117 | LSL: str = "LSL" 118 | LYD: str = "LYD" 119 | MAD: str = "MAD" 120 | MDL: str = "MDL" 121 | MGA: str = "MGA" 122 | MKD: str = "MKD" 123 | MMK: str = "MMK" 124 | MNT: str = "MNT" 125 | MOP: str = "MOP" 126 | MRU: str = "MRU" 127 | MUR: str = "MUR" 128 | MVR: str = "MVR" 129 | MWK: str = "MWK" 130 | MXN: str = "MXN" 131 | MXV: str = "MXV" 132 | MYR: str = "MYR" 133 | MZN: str = "MZN" 134 | NAD: str = "NAD" 135 | NGN: str = "NGN" 136 | NIO: str = "NIO" 137 | NOK: str = "NOK" 138 | NPR: str = "NPR" 139 | NZD: str = "NZD" 140 | OMR: str = "OMR" 141 | PAB: str = "PAB" 142 | PEN: str = "PEN" 143 | PGK: str = "PGK" 144 | PHP: str = "PHP" 145 | PKR: str = "PKR" 146 | PLN: str = "PLN" 147 | PYG: str = "PYG" 148 | QAR: str = "QAR" 149 | RON: str = "RON" 150 | RSD: str = "RSD" 151 | RUB: str = "RUB" 152 | RWF: str = "RWF" 153 | SAR: str = "SAR" 154 | SBD: str = "SBD" 155 | SCR: str = "SCR" 156 | SDG: str = "SDG" 157 | SEK: str = "SEK" 158 | SGD: str = "SGD" 159 | SHP: str = "SHP" 160 | SLL: str = "SLL" 161 | SOS: str = "SOS" 162 | SRD: str = "SRD" 163 | SSP: str = "SSP" 164 | STN: str = "STN" 165 | SVC: str = "SVC" 166 | SYP: str = "SYP" 167 | SZL: str = "SZL" 168 | THB: str = "THB" 169 | TJS: str = "TJS" 170 | TMT: str = "TMT" 171 | TND: str = "TND" 172 | TOP: str = "TOP" 173 | TRY: str = "TRY" 174 | TTD: str = "TTD" 175 | TWD: str = "TWD" 176 | TZS: str = "TZS" 177 | UAH: str = "UAH" 178 | UGX: str = "UGX" 179 | USD: str = "USD" 180 | USN: str = "USN" 181 | UYI: str = "UYI" 182 | UYU: str = "UYU" 183 | UZS: str = "UZS" 184 | VEF: str = "VEF" 185 | VND: str = "VND" 186 | VUV: str = "VUV" 187 | WST: str = "WST" 188 | XAF: str = "XAF" 189 | XCD: str = "XCD" 190 | XDR: str = "XDR" 191 | XOF: str = "XOF" 192 | XPF: str = "XPF" 193 | XSU: str = "XSU" 194 | XUA: str = "XUA" 195 | YER: str = "YER" 196 | ZAR: str = "ZAR" 197 | ZMW: str = "ZMW" 198 | ZWL: str = "ZWL" 199 | 200 | code: str 201 | symbol: Union[str, None] = None 202 | precision: int 203 | name: str 204 | plural: str 205 | alts: str 206 | minimum: float 207 | sanctioned: bool = False 208 | decimals: int 209 | display_code: Union[str, None] = None 210 | chain: Union[str, None] = None 211 | max_supply: Union[float, None] = None 212 | tranche_decimals: Union[int, None] = None 213 | contract_address: Union[str, None] = None 214 | 215 | @classmethod 216 | def is_valid(cls, value: str) -> bool: 217 | if cls.model_fields.get(value) is None: 218 | return False 219 | return True 220 | -------------------------------------------------------------------------------- /src/bitpay/models/facade.py: -------------------------------------------------------------------------------- 1 | """ 2 | Facade 3 | """ 4 | 5 | from enum import Enum 6 | 7 | 8 | class Facade(Enum): 9 | """ 10 | Facade 11 | """ 12 | 13 | MERCHANT = "merchant" 14 | PAYOUT = "payout" 15 | POS = "pos" 16 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/models/invoice/__init__.py -------------------------------------------------------------------------------- /src/bitpay/models/invoice/buyer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Buyer 3 | """ 4 | 5 | from typing import Union 6 | from bitpay.models.bitpay_model import BitPayModel 7 | 8 | 9 | class Buyer(BitPayModel): 10 | """ 11 | Allows merchant to pass buyer related information in the invoice object 12 | """ 13 | 14 | name: Union[str, None] = None 15 | address1: Union[str, None] = None 16 | address2: Union[str, None] = None 17 | locality: Union[str, None] = None 18 | region: Union[str, None] = None 19 | postal_code: Union[str, None] = None 20 | country: Union[str, None] = None 21 | email: Union[str, None] = None 22 | phone: Union[str, None] = None 23 | notify: bool = False 24 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/buyer_fields.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from bitpay.models.bitpay_model import BitPayModel 4 | 5 | 6 | class BuyerFields(BitPayModel): 7 | buyer_address1: Union[str, None] = None 8 | buyer_address2: Union[str, None] = None 9 | buyer_city: Union[str, None] = None 10 | buyer_country: Union[str, None] = None 11 | buyer_email: Union[str, None] = None 12 | buyer_name: Union[str, None] = None 13 | buyer_notify: bool = False 14 | buyer_phone: Union[str, None] = None 15 | buyer_state: Union[str, None] = None 16 | buyer_zip: Union[str, None] = None 17 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/buyer_provided_info.py: -------------------------------------------------------------------------------- 1 | """ 2 | BuyerProvidedInfo: Information collected from the buyer during the process of paying 3 | an invoice. Initially this object is empty. 4 | """ 5 | 6 | from typing import Union 7 | 8 | from bitpay.models.bitpay_model import BitPayModel 9 | 10 | 11 | class BuyerProvidedInfo(BitPayModel): 12 | """ 13 | Information collected from the buyer during the process of paying an invoice. 14 | Initially this object is empty. 15 | """ 16 | 17 | name: Union[str, None] = None 18 | phone_number: Union[str, None] = None 19 | selected_wallet: Union[str, None] = None 20 | email_address: Union[str, None] = None 21 | selected_transaction_currency: Union[str, None] = None 22 | sms: Union[str, None] = None 23 | sms_verified: bool = False 24 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/invoice.py: -------------------------------------------------------------------------------- 1 | """ 2 | Invoice 3 | """ 4 | 5 | from decimal import * 6 | from typing import List, Union, Dict 7 | from pydantic import Field 8 | from .buyer import Buyer 9 | from .buyer_provided_info import BuyerProvidedInfo 10 | from .invoice_refund_addresses import InvoiceRefundAddress 11 | from .miner_fees import MinerFees 12 | from .refund_info import RefundInfo 13 | from .shopper import Shopper 14 | from .supported_transaction_currencies import SupportedTransactionCurrencies 15 | from .transaction import Transaction 16 | from .itemized_details import ItemizedDetails 17 | from .universal_codes import UniversalCodes 18 | from ..bitpay_model import BitPayModel 19 | 20 | 21 | class Invoice(BitPayModel): 22 | """ 23 | Invoices are time-sensitive payment requests addressed to specific buyers. 24 | An invoice has a fixed price,typically denominated in fiat currency. 25 | It also has an equivalent price in the supported cryptocurrencies, 26 | calculated by BitPay, at a locked exchange rate with an expiration 27 | time of 15 minutes. 28 | """ 29 | 30 | currency: Union[str, None] = None 31 | guid: Union[str, None] = None 32 | token: Union[str, None] = None 33 | price: Union[float, None] = None 34 | pos_data: Union[str, None] = None 35 | notification_url: Union[str, None] = Field(alias="notificationURL", default=None) 36 | transaction_speed: Union[str, None] = None 37 | full_notifications: bool = False 38 | notification_email: Union[str, None] = None 39 | redirect_url: Union[str, None] = Field(alias="redirectURL", default=None) 40 | order_id: Union[str, None] = None 41 | item_desc: Union[str, None] = None 42 | item_code: Union[str, None] = None 43 | physical: Union[bool, None] = False 44 | payment_currencies: Union[List[str], None] = None 45 | payment_subtotals: Union[Dict[str, Decimal], None] = None 46 | payment_totals: Union[Dict[str, Decimal], None] = None 47 | payment_display_totals: Union[Dict[str, str], None] = None 48 | payment_display_subtotals: Union[Dict[str, str], None] = None 49 | payment_codes: Union[Dict[str, Dict[str, str]], None] = None 50 | acceptance_window: Union[int, None] = None 51 | buyer: Union[Buyer, None] = None 52 | refund_addresses: Union[List[Dict[str, InvoiceRefundAddress]], None] = None 53 | close_url: Union[str, None] = Field(alias="closeURL", default=None) 54 | auto_redirect: Union[bool, None] = False 55 | json_paypro_required: Union[bool, None] = False 56 | id: Union[str, None] = None 57 | url: Union[str, None] = None 58 | status: Union[str, None] = None 59 | low_fee_detected: Union[bool, None] = False 60 | invoice_time: Union[int, None] = None 61 | expiration_time: Union[int, None] = None 62 | current_time: Union[int, None] = None 63 | transactions: Union[List[Transaction], None] = None 64 | exception_status: Union[str, bool, None] = None 65 | target_confirmations: Union[int, None] = None 66 | refund_address_request_pending: bool = False 67 | buyer_provided_email: Union[str, None] = None 68 | buyer_provided_info: Union[BuyerProvidedInfo, None] = None 69 | buyer_sms: Union[str, None] = None 70 | supported_transaction_currencies: Union[SupportedTransactionCurrencies, None] = None 71 | miner_fees: Union[MinerFees, None] = None 72 | non_pay_pro_payment_received: Union[bool, None] = False 73 | shopper: Union[Shopper, None] = None 74 | bill_id: Union[str, None] = None 75 | refund_info: Union[List[RefundInfo], None] = None 76 | extended_notifications: Union[bool, None] = False 77 | transaction_currency: Union[str, None] = None 78 | underpaid_amount: Union[float, None] = None 79 | overpaid_amount: Union[float, None] = None 80 | amount_paid: Union[float, None] = None 81 | display_amount_paid: Union[str, None] = None 82 | exchange_rates: Union[Dict[str, Dict[str, float]], None] = None 83 | merchant_name: Union[str, None] = None 84 | forced_buyer_selected_wallet: Union[str, None] = None 85 | forced_buyer_selected_transaction_currency: Union[str, None] = None 86 | itemized_details: Union[List[ItemizedDetails], None] = None 87 | universal_codes: Union[UniversalCodes, None] = None 88 | is_cancelled: Union[bool, None] = False 89 | bitpay_id_required: Union[bool, None] = False 90 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/invoice_event_token.py: -------------------------------------------------------------------------------- 1 | from typing import List, Union 2 | from bitpay.models.bitpay_model import BitPayModel 3 | 4 | 5 | class InvoiceEventToken(BitPayModel): 6 | token: Union[str, None] = None 7 | events: List[str] = [] 8 | actions: List[str] = [] 9 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/invoice_refund_addresses.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from pydantic import field_serializer 3 | from typing import Union 4 | 5 | from bitpay.models.bitpay_model import BitPayModel 6 | 7 | 8 | class InvoiceRefundAddress(BitPayModel): 9 | date: Union[datetime, None] = None 10 | email: Union[str, None] = None 11 | tag: Union[int, None] = None 12 | type: Union[str, None] = None 13 | 14 | @field_serializer("date") 15 | def serialize_datetime(self, dt: datetime) -> str: 16 | return super().serialize_datetime_to_iso8601(dt) 17 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/invoice_status.py: -------------------------------------------------------------------------------- 1 | """ 2 | InvoiceStatus 3 | """ 4 | 5 | from enum import Enum 6 | 7 | 8 | class InvoiceStatus(Enum): 9 | """ 10 | Invoice Status 11 | """ 12 | 13 | NEW = "new" 14 | PAID = "paid" 15 | CONFIRMED = "confirmed" 16 | COMPLETE = "complete" 17 | EXPIRED = "expired" 18 | INVALID = "invalid" 19 | DECLINED = "declined" 20 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/invoice_webhook.py: -------------------------------------------------------------------------------- 1 | from decimal import * 2 | from typing import Union, Dict 3 | 4 | from bitpay.models.bitpay_model import BitPayModel 5 | from bitpay.models.invoice.buyer_fields import BuyerFields 6 | 7 | 8 | class InvoiceWebhook(BitPayModel): 9 | amount_paid: Union[float, None] = None 10 | buyer_fields: Union[BuyerFields, None] = None 11 | currency: Union[str, None] = None 12 | currency_time: Union[str, None] = None 13 | exception_status: Union[str, None] = None 14 | exchange_rates: Union[Dict[str, Dict[str, float]], None] = None 15 | id: Union[str, None] = None 16 | invoice_time: Union[str, None] = None 17 | order_id: Union[str, None] = None 18 | payment_subtotals: Union[Dict[str, Decimal], None] = None 19 | payment_totals: Union[Dict[str, Decimal], None] = None 20 | pos_data: Union[str, None] = None 21 | price: Union[float, None] = None 22 | status: Union[str, None] = None 23 | transaction_currency: Union[str, None] = None 24 | url: Union[str, None] = None 25 | in_invoice_id: Union[str, None] = None 26 | in_payment_request: Union[str, None] = None 27 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/itemized_details.py: -------------------------------------------------------------------------------- 1 | """ 2 | ItemizedDetails 3 | """ 4 | 5 | from typing import Union 6 | 7 | from bitpay.models.bitpay_model import BitPayModel 8 | 9 | 10 | class ItemizedDetails(BitPayModel): 11 | """ 12 | object containing line item details for display 13 | """ 14 | 15 | amount: Union[float, None] = None 16 | description: Union[str, None] = None 17 | is_fee: bool = False 18 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/miner_fees.py: -------------------------------------------------------------------------------- 1 | """ 2 | MinerFees 3 | """ 4 | 5 | from pydantic import Field 6 | 7 | from .miner_fees_item import MinerFeesItem 8 | from bitpay.utils.model_util import ModelUtil 9 | from ..bitpay_model import BitPayModel 10 | 11 | 12 | class MinerFees(BitPayModel): 13 | """ 14 | The total amount of fees that the purchaser will pay to cover BitPay's 15 | UTXO sweep cost for an invoice. The key is the currency and the value 16 | is an amount in satoshis. This is referenced as "Network Cost" on an 17 | invoice,see this support article for more information 18 | """ 19 | 20 | btc: MinerFeesItem = Field(alias="BTC", default=MinerFeesItem()) 21 | bch: MinerFeesItem = Field(alias="BCH", default=MinerFeesItem()) 22 | eth: MinerFeesItem = Field(alias="ETH", default=MinerFeesItem()) 23 | usdc: MinerFeesItem = Field(alias="USDC", default=MinerFeesItem()) 24 | gusd: MinerFeesItem = Field(alias="GUSD", default=MinerFeesItem()) 25 | pax: MinerFeesItem = Field(alias="PAX", default=MinerFeesItem()) 26 | doge: MinerFeesItem = Field(alias="DOGE", default=MinerFeesItem()) 27 | ltc: MinerFeesItem = Field(alias="LTC", default=MinerFeesItem()) 28 | busd: MinerFeesItem = Field(alias="BUSD", default=MinerFeesItem()) 29 | xrp: MinerFeesItem = Field(alias="XRP", default=MinerFeesItem()) 30 | dai: MinerFeesItem = Field(alias="DAI", default=MinerFeesItem()) 31 | wbtc: MinerFeesItem = Field(alias="WBTC", default=MinerFeesItem()) 32 | matic: MinerFeesItem = Field(alias="MATIC", default=MinerFeesItem()) 33 | usdc_m: MinerFeesItem = Field(alias="USDC_m", default=MinerFeesItem()) 34 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/miner_fees_item.py: -------------------------------------------------------------------------------- 1 | """ 2 | MinerFeesItem 3 | """ 4 | 5 | from typing import Union 6 | from bitpay.models.bitpay_model import BitPayModel 7 | from bitpay.utils.model_util import ModelUtil 8 | 9 | 10 | class MinerFeesItem(BitPayModel): 11 | """ 12 | The total amount of fees that the purchaser will pay to cover BitPay's 13 | UTXO sweep cost for an invoice. The key is the currency and the value is 14 | an amount in satoshis. This is referenced as "Network Cost" on an invoice, 15 | see this support article for more information 16 | """ 17 | 18 | satoshis_per_byte: Union[float, None] = None 19 | total_fee: Union[int, None] = None 20 | fiat_amount: Union[float, None] = None 21 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/refund.py: -------------------------------------------------------------------------------- 1 | """ 2 | Refund 3 | """ 4 | 5 | from datetime import datetime 6 | from typing import Union 7 | 8 | from pydantic import Field, field_serializer 9 | 10 | from bitpay.models.bitpay_model import BitPayModel 11 | 12 | 13 | class Refund(BitPayModel): 14 | """ 15 | Refund an invoice 16 | """ 17 | 18 | id: Union[str, None] = None 19 | guid: Union[str, None] = None 20 | amount: float = 0.0 21 | currency: Union[str, None] = None 22 | request_date: Union[datetime, None] = None 23 | status: Union[str, None] = None 24 | 25 | preview: bool = False 26 | immediate: bool = False 27 | reference: Union[str, None] = None 28 | buyer_pays_refund_fee: bool = False 29 | refund_fee: Union[float, None] = None 30 | last_refund_notification: Union[datetime, None] = None 31 | invoice: Union[str, None] = None 32 | notification_url: Union[str, None] = Field(alias="notificationURL", default=None) 33 | refund_address: Union[str, None] = None 34 | support_request: Union[str, None] = None 35 | transaction_currency: Union[str, None] = None 36 | transaction_amount: Union[float, None] = None 37 | transaction_refund_fee: Union[float, None] = None 38 | txid: Union[str, None] = None 39 | type: Union[str, None] = None 40 | 41 | @field_serializer("request_date", "last_refund_notification") 42 | def serialize_datetime(self, dt: datetime) -> str: 43 | return super().serialize_datetime_to_iso8601(dt) 44 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/refund_info.py: -------------------------------------------------------------------------------- 1 | """ 2 | RefundInfo 3 | """ 4 | 5 | from typing import List, Union, Dict 6 | from bitpay.models.bitpay_model import BitPayModel 7 | 8 | 9 | class RefundInfo(BitPayModel): 10 | """ 11 | Information about refund 12 | """ 13 | 14 | support_request: Union[str, None] = None 15 | currency: Union[str, None] = None 16 | amounts: Union[List[Dict[str, float]], None] = None 17 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/refund_status.py: -------------------------------------------------------------------------------- 1 | """ 2 | RefundStatus 3 | """ 4 | 5 | from enum import Enum 6 | 7 | 8 | class RefundStatus(Enum): 9 | """ 10 | Refund Status 11 | """ 12 | 13 | PENDING = "pending" 14 | SUCCESS = "success" 15 | FAILURE = "failure" 16 | PREVIEW = "preview" 17 | CREATED = "created" 18 | CANCELLED = "cancelled" 19 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/refund_webhook.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Union 3 | 4 | from pydantic import field_serializer 5 | 6 | from bitpay.models.bitpay_model import BitPayModel 7 | 8 | 9 | class RefundWebhook(BitPayModel): 10 | amount: Union[float, None] = None 11 | buyer_pays_refund_fee: Union[bool, None] = None 12 | currency: Union[str, None] = None 13 | id: Union[str, None] = None 14 | immediate: Union[bool, None] = None 15 | invoice: Union[str, None] = None 16 | last_refund_notification: Union[datetime, None] = None 17 | refund_fee: Union[float, None] = None 18 | request_date: Union[datetime, None] = None 19 | status: Union[str, None] = None 20 | support_request: Union[str, None] = None 21 | reference: Union[str, None] = None 22 | guid: Union[str, None] = None 23 | refund_address: Union[str, None] = None 24 | type: Union[str, None] = None 25 | txid: Union[str, None] = None 26 | transaction_currency: Union[str, None] = None 27 | transaction_amount: Union[float, None] = None 28 | transaction_refund_fee: Union[float, None] = None 29 | 30 | @field_serializer("request_date", "last_refund_notification") 31 | def serialize_datetime(self, dt: datetime) -> str: 32 | return super().serialize_datetime_to_iso8601(dt) 33 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/shopper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shopper 3 | """ 4 | 5 | from typing import Union 6 | 7 | from bitpay.models.bitpay_model import BitPayModel 8 | 9 | 10 | class Shopper(BitPayModel): 11 | """ 12 | shopper 13 | """ 14 | 15 | user: Union[str, None] = None 16 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/supported_transaction_currencies.py: -------------------------------------------------------------------------------- 1 | """ 2 | SupportedTransactionCurrencies 3 | """ 4 | 5 | from pydantic import Field 6 | 7 | from .supported_transaction_currency import SupportedTransactionCurrency 8 | from ..bitpay_model import BitPayModel 9 | 10 | 11 | class SupportedTransactionCurrencies(BitPayModel): 12 | """ 13 | currency selected for payment 14 | """ 15 | 16 | btc: SupportedTransactionCurrency = Field( 17 | alias="BTC", default=SupportedTransactionCurrency() 18 | ) 19 | bch: SupportedTransactionCurrency = Field( 20 | alias="BCH", default=SupportedTransactionCurrency() 21 | ) 22 | eth: SupportedTransactionCurrency = Field( 23 | alias="ETH", default=SupportedTransactionCurrency() 24 | ) 25 | usdc: SupportedTransactionCurrency = Field( 26 | alias="USDC", default=SupportedTransactionCurrency() 27 | ) 28 | gusd: SupportedTransactionCurrency = Field( 29 | alias="GUSD", default=SupportedTransactionCurrency() 30 | ) 31 | busd: SupportedTransactionCurrency = Field( 32 | alias="BUSD", default=SupportedTransactionCurrency() 33 | ) 34 | pax: SupportedTransactionCurrency = Field( 35 | alias="PAX", default=SupportedTransactionCurrency() 36 | ) 37 | xrp: SupportedTransactionCurrency = Field( 38 | alias="XRP", default=SupportedTransactionCurrency() 39 | ) 40 | doge: SupportedTransactionCurrency = Field( 41 | alias="DOGE", default=SupportedTransactionCurrency() 42 | ) 43 | ltc: SupportedTransactionCurrency = Field( 44 | alias="LTC", default=SupportedTransactionCurrency() 45 | ) 46 | wbtc: SupportedTransactionCurrency = Field( 47 | alias="WBTC", default=SupportedTransactionCurrency() 48 | ) 49 | dai: SupportedTransactionCurrency = Field( 50 | alias="DAI", default=SupportedTransactionCurrency() 51 | ) 52 | euroc: SupportedTransactionCurrency = Field( 53 | alias="EUROC", default=SupportedTransactionCurrency() 54 | ) 55 | matic: SupportedTransactionCurrency = Field( 56 | alias="MATIC", default=SupportedTransactionCurrency() 57 | ) 58 | matic_e: SupportedTransactionCurrency = Field( 59 | alias="MATIC_e", default=SupportedTransactionCurrency() 60 | ) 61 | eth_m: SupportedTransactionCurrency = Field( 62 | alias="ETH_m", default=SupportedTransactionCurrency() 63 | ) 64 | usdc_m: SupportedTransactionCurrency = Field( 65 | alias="USDC_m", default=SupportedTransactionCurrency() 66 | ) 67 | busd_m: SupportedTransactionCurrency = Field( 68 | alias="BUSD_m", default=SupportedTransactionCurrency() 69 | ) 70 | dai_m: SupportedTransactionCurrency = Field( 71 | alias="DAI_m", default=SupportedTransactionCurrency() 72 | ) 73 | wbtc_m: SupportedTransactionCurrency = Field( 74 | alias="WBTC_m", default=SupportedTransactionCurrency() 75 | ) 76 | shib_m: SupportedTransactionCurrency = Field( 77 | alias="SHIB_m", default=SupportedTransactionCurrency() 78 | ) 79 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/supported_transaction_currency.py: -------------------------------------------------------------------------------- 1 | """ 2 | SupportedTransactionCurrency 3 | """ 4 | 5 | from typing import Union 6 | 7 | from bitpay.models.bitpay_model import BitPayModel 8 | 9 | 10 | class SupportedTransactionCurrency(BitPayModel): 11 | """ 12 | currency selected for payment is enabled 13 | """ 14 | 15 | enabled: Union[bool, None] = None 16 | reason: Union[str, None] = None 17 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/transaction.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Union, Dict 3 | from bitpay.models.bitpay_model import BitPayModel 4 | 5 | from pydantic import field_serializer 6 | 7 | 8 | class Transaction(BitPayModel): 9 | amount: Union[float, None] = None 10 | confirmations: Union[int, None] = None 11 | time: Union[datetime, None] = None 12 | received_time: Union[datetime, None] = None 13 | txid: Union[str, None] = None 14 | ex_rates: Union[Dict[str, float], None] = None 15 | output_index: Union[int, None] = None 16 | 17 | @field_serializer("time", "received_time") 18 | def serialize_datetime_to_iso8601(self, dt: datetime) -> str: 19 | return super().serialize_datetime_to_iso8601(dt) 20 | -------------------------------------------------------------------------------- /src/bitpay/models/invoice/universal_codes.py: -------------------------------------------------------------------------------- 1 | """ 2 | UniversalCodes 3 | """ 4 | 5 | from typing import Union 6 | 7 | from bitpay.models.bitpay_model import BitPayModel 8 | from bitpay.utils.model_util import ModelUtil 9 | 10 | 11 | class UniversalCodes(BitPayModel): 12 | """ 13 | object containing wallet-specific URLs for payment protocol 14 | """ 15 | 16 | payment_string: Union[str, None] = None 17 | verification_link: Union[str, None] = None 18 | -------------------------------------------------------------------------------- /src/bitpay/models/ledger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/models/ledger/__init__.py -------------------------------------------------------------------------------- /src/bitpay/models/ledger/buyer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Buyer 3 | """ 4 | 5 | from typing import Union 6 | from bitpay.models.bitpay_model import BitPayModel 7 | 8 | 9 | class Buyer(BitPayModel): 10 | """ 11 | Allows merchant to pass buyer related information in the invoice object 12 | """ 13 | 14 | name: Union[str, None] = None 15 | address1: Union[str, None] = None 16 | address2: Union[str, None] = None 17 | city: Union[str, None] = None 18 | state: Union[str, None] = None 19 | zip: Union[str, None] = None 20 | country: Union[str, None] = None 21 | email: Union[str, None] = None 22 | phone: Union[str, None] = None 23 | notify: bool = False 24 | -------------------------------------------------------------------------------- /src/bitpay/models/ledger/ledger.py: -------------------------------------------------------------------------------- 1 | """ 2 | Ledger 3 | """ 4 | 5 | from typing import Union 6 | 7 | from bitpay.models.bitpay_model import BitPayModel 8 | 9 | 10 | class Ledger(BitPayModel): 11 | """ 12 | Ledgers are records of money movement. 13 | """ 14 | 15 | currency: Union[str, None] = None 16 | balance: Union[float, None] = None 17 | -------------------------------------------------------------------------------- /src/bitpay/models/ledger/ledger_entry.py: -------------------------------------------------------------------------------- 1 | """ 2 | LedgerEntry 3 | """ 4 | 5 | from datetime import datetime 6 | from typing import Union 7 | from pydantic import field_validator, field_serializer 8 | from bitpay.models.bitpay_model import BitPayModel 9 | from bitpay.models.ledger.buyer import Buyer 10 | 11 | 12 | class LedgerEntry(BitPayModel): 13 | """ 14 | Ledger entry 15 | """ 16 | 17 | type: Union[str, None] = None 18 | amount: Union[int, None] = None 19 | code: Union[int, None] = None 20 | description: Union[str, None] = None 21 | timestamp: Union[datetime, None] = None 22 | currency: Union[str, None] = None 23 | tx_type: Union[str, None] = None 24 | scale: Union[int, None] = None 25 | id: Union[str, None] = None 26 | support_request: Union[str, None] = None 27 | invoice_id: Union[str, None] = None 28 | 29 | # Buyer 30 | buyer_fields: Union[Buyer, None] = None 31 | 32 | invoice_amount: Union[float, None] = None 33 | invoice_currency: Union[str, None] = None 34 | transaction_currency: Union[str, None] = None 35 | 36 | @field_validator("amount") 37 | def convert_to_str(cls, value: Union[int, float, None]) -> Union[float, None]: 38 | if value is None: 39 | return None 40 | return float(value) 41 | 42 | @field_serializer("timestamp") 43 | def serialize_datetime_to_iso8601(self, dt: datetime) -> str: 44 | return super().serialize_datetime_to_iso8601(dt) 45 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/models/payout/__init__.py -------------------------------------------------------------------------------- /src/bitpay/models/payout/payout.py: -------------------------------------------------------------------------------- 1 | """ 2 | Payout 3 | """ 4 | 5 | from datetime import datetime 6 | from typing import List, Union, Dict 7 | from pydantic import Field, field_serializer 8 | from .payout_transaction import PayoutTransaction 9 | from ..bitpay_model import BitPayModel 10 | 11 | 12 | class Payout(BitPayModel): 13 | """ 14 | Payout 15 | """ 16 | 17 | amount: float = 0.0 18 | code: Union[int, None] = None 19 | id: Union[str, None] = None 20 | token: Union[str, None] = None 21 | currency: Union[str, None] = None 22 | effective_date: Union[datetime, None] = None 23 | reference: Union[str, None] = None 24 | notification_email: Union[str, None] = None 25 | notification_url: Union[str, None] = Field(alias="notificationURL", default=None) 26 | ledger_currency: Union[str, None] = None 27 | shopper_id: Union[str, None] = None 28 | recipient_id: Union[str, None] = None 29 | exchange_rates: Union[Dict[str, Dict[str, float]], None] = None 30 | email: Union[str, None] = None 31 | ignore_emails: Union[bool, None] = None 32 | label: Union[str, None] = None 33 | status: Union[str, None] = None 34 | message: Union[str, None] = None 35 | request_date: Union[datetime, None] = None 36 | date_executed: Union[datetime, None] = None 37 | transactions: Union[List[PayoutTransaction], None] = None 38 | account_id: Union[str, None] = None 39 | group_id: Union[str, None] = None 40 | 41 | @field_serializer("effective_date", "request_date", "date_executed") 42 | def serialize_datetime_to_iso8601(self, dt: datetime) -> str: 43 | return super().serialize_datetime_to_iso8601(dt) 44 | 45 | def to_json(self) -> dict: 46 | """ 47 | :return: data in json 48 | """ 49 | data = super().to_json() 50 | if "notificationUrl" in data: 51 | data["notificationURL"] = data.pop("notificationUrl") 52 | 53 | return data 54 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/payout_group.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from bitpay.models.bitpay_model import BitPayModel 3 | from bitpay.models.payout.payout import Payout 4 | from bitpay.models.payout.payout_group_failed import PayoutGroupFailed 5 | 6 | 7 | class PayoutGroup(BitPayModel): 8 | payouts: List[Payout] = [] 9 | failed: List[PayoutGroupFailed] = [] 10 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/payout_group_failed.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from bitpay.models.bitpay_model import BitPayModel 3 | 4 | 5 | class PayoutGroupFailed(BitPayModel): 6 | err_message: Union[str, None] = None 7 | payout_id: Union[str, None] = None 8 | payee: Union[str, None] = None 9 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/payout_recipient.py: -------------------------------------------------------------------------------- 1 | """ 2 | PayoutRecipient 3 | """ 4 | 5 | from typing import Union 6 | 7 | from pydantic import Field 8 | 9 | from bitpay.models.bitpay_model import BitPayModel 10 | 11 | 12 | class PayoutRecipient(BitPayModel): 13 | """ 14 | PayoutRecipient 15 | """ 16 | 17 | email: Union[str, None] = None 18 | label: Union[str, None] = None 19 | notification_url: Union[str, None] = Field(alias="notificationURL", default=None) 20 | status: Union[str, None] = None 21 | id: Union[str, None] = None 22 | shopper_id: Union[str, None] = None 23 | token: Union[str, None] = None 24 | guid: Union[str, None] = None 25 | 26 | def to_json(self) -> dict: 27 | """ 28 | :return: data in json 29 | """ 30 | data = super().to_json() 31 | if "notificationUrl" in data: 32 | data["notificationURL"] = data.pop("notificationUrl") 33 | 34 | return data 35 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/payout_recipients.py: -------------------------------------------------------------------------------- 1 | """ 2 | PayoutRecipients 3 | """ 4 | 5 | from typing import List, Union 6 | 7 | from .payout_recipient import PayoutRecipient 8 | from ..bitpay_model import BitPayModel 9 | 10 | 11 | class PayoutRecipients(BitPayModel): 12 | """ 13 | PayoutRecipients 14 | """ 15 | 16 | guid: Union[str, None] = None 17 | recipients: Union[List[PayoutRecipient], None] = None 18 | token: Union[str, None] = None 19 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/payout_status.py: -------------------------------------------------------------------------------- 1 | """ 2 | PayoutStatus 3 | """ 4 | 5 | from enum import Enum 6 | 7 | 8 | class PayoutStatus(Enum): 9 | NEW = "new" 10 | FUNDED = "funded" 11 | PROCESSING = "processing" 12 | COMPLETE = "complete" 13 | FAILED = "failed" 14 | CANCELLED = "cancelled" 15 | PAID = "paid" 16 | UNPAID = "unpaid" 17 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/payout_transaction.py: -------------------------------------------------------------------------------- 1 | """ 2 | PayoutTransaction 3 | """ 4 | 5 | from datetime import datetime 6 | from pydantic import field_serializer 7 | from typing import Union 8 | from bitpay.models.bitpay_model import BitPayModel 9 | 10 | 11 | class PayoutTransaction(BitPayModel): 12 | """ 13 | PayoutTransaction 14 | """ 15 | 16 | txid: Union[str, None] = None 17 | amount: Union[float, None] = None 18 | date: Union[datetime, None] = None 19 | confirmations: Union[int, None] = None 20 | 21 | @field_serializer("date") 22 | def serialize_datetime_to_iso8601(self, dt: datetime) -> str: 23 | return super().serialize_datetime_to_iso8601(dt) 24 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/payout_webhook.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from pydantic import field_serializer 3 | from typing import Union, Dict, List 4 | 5 | from bitpay.models.bitpay_model import BitPayModel 6 | from bitpay.models.payout.payout_transaction import PayoutTransaction 7 | 8 | 9 | class PayoutWebhook(BitPayModel): 10 | id: Union[str, None] = None 11 | recipient_id: Union[str, None] = None 12 | shopper_id: Union[str, None] = None 13 | price: Union[float, None] = None 14 | currency: Union[str, None] = None 15 | ledger_currency: Union[str, None] = None 16 | exchange_rates: Union[Dict[str, Dict[str, float]], None] = None 17 | email: Union[str, None] = None 18 | reference: Union[str, None] = None 19 | label: Union[str, None] = None 20 | notification_url: Union[str, None] = None 21 | notification_email: Union[str, None] = None 22 | effective_date: Union[datetime, None] = None 23 | request_date: Union[datetime, None] = None 24 | status: Union[str, None] = None 25 | transactions: Union[List[PayoutTransaction], None] = None 26 | account_id: Union[str, None] = None 27 | date_executed: Union[datetime, None] = None 28 | group_id: Union[str, None] = None 29 | 30 | @field_serializer("effective_date", "request_date") 31 | def serialize_datetime(self, dt: datetime) -> str: 32 | return super().serialize_datetime_to_iso8601(dt) 33 | -------------------------------------------------------------------------------- /src/bitpay/models/payout/recipient_status.py: -------------------------------------------------------------------------------- 1 | """ 2 | RecipientStatus 3 | """ 4 | 5 | from enum import Enum 6 | 7 | 8 | class RecipientStatus(Enum): 9 | """ 10 | Recipient Status 11 | """ 12 | 13 | INVITED = "invited" 14 | UNVERIFIED = "unverified" 15 | VERIFIED = "verified" 16 | ACTIVE = "active" 17 | PAUSED = "paused" 18 | REMOVED = "removed" 19 | -------------------------------------------------------------------------------- /src/bitpay/models/rate/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/models/rate/__init__.py -------------------------------------------------------------------------------- /src/bitpay/models/rate/rate.py: -------------------------------------------------------------------------------- 1 | """ 2 | Rate 3 | """ 4 | 5 | from typing import Union 6 | from bitpay.models.bitpay_model import BitPayModel 7 | 8 | 9 | class Rate(BitPayModel): 10 | """ 11 | Rate 12 | """ 13 | 14 | name: Union[str, None] = None 15 | code: Union[str, None] = None 16 | rate: Union[float, None] = None 17 | -------------------------------------------------------------------------------- /src/bitpay/models/rate/rates.py: -------------------------------------------------------------------------------- 1 | """ 2 | Rates 3 | """ 4 | 5 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 6 | from bitpay.models.bitpay_model import BitPayModel 7 | from bitpay.models.rate.rate import Rate 8 | from bitpay.models.currency import Currency 9 | from bitpay.exceptions.bitpay_exception import BitPayException 10 | from typing import List, Optional 11 | 12 | 13 | class Rates(BitPayModel): 14 | """ 15 | Rates:Rates are exchange rates, representing the number of fiat 16 | currency units equivalent to one BTC. 17 | """ 18 | 19 | rates: List[Rate] = [] 20 | 21 | def update(self, rate_client) -> None: # type: ignore 22 | rates = rate_client.get_rates() 23 | self.rates = rates.rates 24 | 25 | def get_rate(self, currency_code: str) -> Optional[float]: 26 | """ 27 | :raises BitPayGenericException 28 | """ 29 | if Currency.is_valid(currency_code) is False: 30 | BitPayExceptionProvider.throw_generic_exception_with_message( 31 | "currency code must be a type of Model.Currency" 32 | ) 33 | 34 | val = None 35 | for rate_obj in self.rates: 36 | if rate_obj.code == currency_code: 37 | val = rate_obj.rate 38 | return val 39 | -------------------------------------------------------------------------------- /src/bitpay/models/settlement/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/models/settlement/__init__.py -------------------------------------------------------------------------------- /src/bitpay/models/settlement/invoice_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | InvoiceData: Object containing relevant information from the paid invoice 3 | """ 4 | 5 | from datetime import datetime 6 | from pydantic import field_serializer 7 | from typing import Union, Dict 8 | from bitpay.models.bitpay_model import BitPayModel 9 | from bitpay.models.settlement.refund_info import RefundInfo 10 | 11 | 12 | class InvoiceData(BitPayModel): 13 | """ 14 | invoice data 15 | """ 16 | 17 | order_id: Union[str, None] = None 18 | date: Union[datetime, None] = None 19 | price: Union[float, None] = None 20 | currency: Union[str, None] = None 21 | transaction_currency: Union[str, None] = None 22 | overpaid_amount: Union[float, None] = None 23 | payout_percentage: Union[Dict[str, int], None] = None 24 | refund_info: Union[RefundInfo, None] = None 25 | 26 | @field_serializer("date") 27 | def serialize_datetime_to_iso8601(self, dt: datetime) -> str: 28 | return super().serialize_datetime_to_iso8601(dt) 29 | -------------------------------------------------------------------------------- /src/bitpay/models/settlement/payout_info.py: -------------------------------------------------------------------------------- 1 | """ 2 | PayoutInfo: Object containing the settlement info provided by the Merchant 3 | in his BitPay account settings 4 | """ 5 | 6 | from typing import Union 7 | from bitpay.models.bitpay_model import BitPayModel 8 | 9 | 10 | class PayoutInfo(BitPayModel): 11 | """ 12 | PayoutInfo 13 | """ 14 | 15 | name: Union[str, None] = None 16 | account: Union[str, None] = None 17 | routing: Union[str, None] = None 18 | merchant_ein: Union[str, None] = None 19 | label: Union[str, None] = None 20 | bank_country: Union[str, None] = None 21 | bank: Union[str, None] = None 22 | swift: Union[str, None] = None 23 | address: Union[str, None] = None 24 | city: Union[str, None] = None 25 | postal: Union[str, None] = None 26 | sort: Union[str, None] = None 27 | wire: bool = False 28 | bank_name: Union[str, None] = None 29 | bank_address: Union[str, None] = None 30 | bank_address2: Union[str, None] = None 31 | iban: Union[str, None] = None 32 | additional_information: Union[str, None] = None 33 | account_holder_name: Union[str, None] = None 34 | account_holder_address: Union[str, None] = None 35 | account_holder_address2: Union[str, None] = None 36 | account_holder_postal_code: Union[str, None] = None 37 | account_holder_city: Union[str, None] = None 38 | account_holder_country: Union[str, None] = None 39 | -------------------------------------------------------------------------------- /src/bitpay/models/settlement/refund_info.py: -------------------------------------------------------------------------------- 1 | """ 2 | RefundInfo: Object containing information about the refund executed for the invoice 3 | """ 4 | 5 | from typing import Union, Dict 6 | from bitpay.models.bitpay_model import BitPayModel 7 | 8 | 9 | class RefundInfo(BitPayModel): 10 | """ 11 | Information about refund 12 | """ 13 | 14 | support_request: Union[str, None] = None 15 | currency: Union[str, None] = None 16 | amounts: Union[Dict[str, float], None] = None 17 | -------------------------------------------------------------------------------- /src/bitpay/models/settlement/settlement.py: -------------------------------------------------------------------------------- 1 | """ 2 | Settlement: Settlements are transfers of payment profits from BitPay to bank 3 | accounts and cryptocurrency wallets owned by merchants, partners, etc. This 4 | endpoint exposes reports detailing these settlements. 5 | """ 6 | 7 | from datetime import datetime 8 | from pydantic import field_serializer 9 | from typing import List, Union 10 | from .payout_info import PayoutInfo 11 | from .settlement_ledger_entry import SettlementLedgerEntry 12 | from .with_holdings import WithHoldings 13 | from ..bitpay_model import BitPayModel 14 | 15 | 16 | class Settlement(BitPayModel): 17 | """ 18 | Settlement 19 | """ 20 | 21 | id: Union[str, None] = None 22 | account_id: Union[str, None] = None 23 | currency: Union[str, None] = None 24 | payout_info: Union[PayoutInfo, None] = None 25 | status: Union[str, None] = None 26 | date_created: Union[datetime, None] = None 27 | date_executed: Union[datetime, None] = None 28 | date_completed: Union[datetime, None] = None 29 | opening_date: Union[datetime, None] = None 30 | closing_date: Union[datetime, None] = None 31 | opening_balance: Union[float, None] = None 32 | ledger_entries_sum: Union[float, None] = None 33 | withholdings: Union[List[WithHoldings], None] = None 34 | withholdings_sum: Union[float, None] = None 35 | total_amount: Union[float, None] = None 36 | ledger_entries: Union[List[SettlementLedgerEntry], None] = None 37 | token: Union[str, None] = None 38 | 39 | @field_serializer("date_created", "date_executed", "date_completed", "closing_date") 40 | def serialize_datetime_to_iso8601(self, dt: datetime) -> str: 41 | return super().serialize_datetime_to_iso8601(dt) 42 | -------------------------------------------------------------------------------- /src/bitpay/models/settlement/settlement_ledger_entry.py: -------------------------------------------------------------------------------- 1 | """ 2 | SettlementLedgerEntry: ledger entries in the settlement, 3 | """ 4 | 5 | from datetime import datetime 6 | from pydantic import field_serializer 7 | from typing import Union 8 | from .invoice_data import InvoiceData 9 | from ..bitpay_model import BitPayModel 10 | 11 | 12 | class SettlementLedgerEntry(BitPayModel): 13 | """ 14 | SettlementLedgerEntry 15 | """ 16 | 17 | code: Union[int, None] = None 18 | invoice_id: Union[str, None] = None 19 | amount: Union[float, None] = None 20 | timestamp: Union[datetime, None] = None 21 | description: Union[str, None] = None 22 | invoice_data: Union[InvoiceData, None] = None 23 | 24 | @field_serializer("timestamp") 25 | def serialize_datetime_to_iso8601(self, dt: datetime) -> str: 26 | return super().serialize_datetime_to_iso8601(dt) 27 | -------------------------------------------------------------------------------- /src/bitpay/models/settlement/with_holdings.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from bitpay.models.bitpay_model import BitPayModel 3 | 4 | 5 | class WithHoldings(BitPayModel): 6 | """ 7 | Holdings 8 | """ 9 | 10 | amount: Union[float, None] = None 11 | code: Union[str, None] = None 12 | description: Union[str, None] = None 13 | notes: Union[str, None] = None 14 | label: Union[str, None] = None 15 | bank_country: Union[str, None] = None 16 | -------------------------------------------------------------------------------- /src/bitpay/models/wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/models/wallet/__init__.py -------------------------------------------------------------------------------- /src/bitpay/models/wallet/currencies.py: -------------------------------------------------------------------------------- 1 | """ 2 | Currencies 3 | """ 4 | 5 | from typing import Union 6 | from bitpay.models.bitpay_model import BitPayModel 7 | from bitpay.models.wallet.currency_qr import CurrencyQr 8 | 9 | 10 | class Currencies(BitPayModel): 11 | """ 12 | details of what currencies support payments for this wallet 13 | """ 14 | 15 | code: Union[str, None] = None 16 | p2p: Union[bool, None] = False 17 | dapp_browser: Union[bool, None] = False 18 | pay_pro: Union[bool, None] = False 19 | qr: Union[CurrencyQr, None] = None 20 | image: Union[str, None] = None 21 | withdrawal_fee: Union[str, None] = None 22 | wallet_connect: Union[bool, None] = False 23 | -------------------------------------------------------------------------------- /src/bitpay/models/wallet/currency_qr.py: -------------------------------------------------------------------------------- 1 | """ 2 | Currency Qr 3 | """ 4 | 5 | from typing import Union 6 | from bitpay.models.bitpay_model import BitPayModel 7 | 8 | 9 | class CurrencyQr(BitPayModel): 10 | """ 11 | Currency Qr 12 | """ 13 | 14 | type: Union[str, None] = None 15 | collapsed: Union[bool, None] = False 16 | -------------------------------------------------------------------------------- /src/bitpay/models/wallet/wallet.py: -------------------------------------------------------------------------------- 1 | from typing import List, Union 2 | from .currencies import Currencies 3 | from bitpay.utils.model_util import ModelUtil 4 | from ..bitpay_model import BitPayModel 5 | 6 | 7 | class Wallet(BitPayModel): 8 | """ 9 | supported wallets and supported currency details 10 | """ 11 | 12 | key: Union[str, None] = None 13 | display_name: Union[str, None] = None 14 | avatar: Union[str, None] = None 15 | pay_pro: Union[bool, None] = False 16 | currencies: Union[List[Currencies], None] = None 17 | image: Union[str, None] = None 18 | 19 | def to_json(self) -> dict: 20 | """ 21 | :return: data in json 22 | """ 23 | return ModelUtil.to_json(self) 24 | -------------------------------------------------------------------------------- /src/bitpay/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/src/bitpay/utils/__init__.py -------------------------------------------------------------------------------- /src/bitpay/utils/guid_generator.py: -------------------------------------------------------------------------------- 1 | from uuid import uuid4 2 | 3 | 4 | class GuidGenerator: 5 | def execute(self) -> str: 6 | return str(uuid4()) 7 | -------------------------------------------------------------------------------- /src/bitpay/utils/key_utils.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import hashlib 3 | from ecdsa import util as ecdsaUtil 4 | from ecdsa import SigningKey, SECP256k1 5 | 6 | 7 | def generate_pem(): # type: ignore 8 | s_k = SigningKey.generate(curve=SECP256k1) 9 | pem = s_k.to_pem() 10 | pem = pem.decode("utf-8") 11 | return pem 12 | 13 | 14 | def get_sin_from_pem(pem): # type: ignore 15 | public_key = get_compressed_public_key_from_pem(pem) 16 | version = get_version_from_compressed_key(public_key) 17 | checksum = get_checksum_from_version(version) 18 | return base58encode(version + checksum) 19 | 20 | 21 | def get_compressed_public_key_from_pem(pem): # type: ignore 22 | vks = SigningKey.from_pem(pem).get_verifying_key().to_string() 23 | bts = binascii.hexlify(vks) 24 | compressed = compress_key(bts) 25 | return compressed 26 | 27 | 28 | def sign(message, pem): # type: ignore 29 | message = message.encode() 30 | s_k = SigningKey.from_pem(pem) 31 | signed = s_k.sign( 32 | message, hashfunc=hashlib.sha256, sigencode=ecdsaUtil.sigencode_der 33 | ) 34 | return binascii.hexlify(signed).decode() 35 | 36 | 37 | def base58encode(hexastring): # type: ignore 38 | chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 39 | int_val = int(hexastring, 16) 40 | encoded = encode58("", int_val, chars) 41 | return encoded 42 | 43 | 44 | def encode58(string, int_val, chars): # type: ignore 45 | if int_val == 0: 46 | return string 47 | else: 48 | new_val, rem = divmod(int_val, 58) 49 | new_string = (chars[rem]) + string 50 | return encode58(new_string, new_val, chars) 51 | 52 | 53 | def get_checksum_from_version(version): # type: ignore 54 | return sha_digest(sha_digest(version))[0:8] 55 | 56 | 57 | def get_version_from_compressed_key(key): # type: ignore 58 | sh2 = sha_digest(key) 59 | rphash = hashlib.new("ripemd160") 60 | rphash.update(binascii.unhexlify(sh2)) 61 | rp1 = rphash.hexdigest() 62 | return "0F02" + rp1 63 | 64 | 65 | def sha_digest(hexastring): # type: ignore 66 | return hashlib.sha256(binascii.unhexlify(hexastring)).hexdigest() 67 | 68 | 69 | def compress_key(bts): # type: ignore 70 | intval = int(bts, 16) 71 | prefix = find_prefix(intval) 72 | return prefix + bts[0:64].decode("utf-8") 73 | 74 | 75 | def find_prefix(intval: int) -> str: 76 | if intval % 2 == 0: 77 | prefix = "02" 78 | else: 79 | prefix = "03" 80 | return prefix 81 | 82 | 83 | def change_camel_case_to_snake_case(string: str) -> str: 84 | snake_case = "".join( 85 | ["_" + i.lower() if i.isupper() else i for i in string] 86 | ).lstrip("_") 87 | return snake_case 88 | -------------------------------------------------------------------------------- /src/bitpay/utils/model_util.py: -------------------------------------------------------------------------------- 1 | # mypy: disable-error-code="misc, union-attr, assignment, arg-type" 2 | from typing import Any 3 | 4 | from bitpay.exceptions.bitpay_exception import BitPayException 5 | 6 | 7 | class ModelUtil: 8 | @staticmethod 9 | def to_json(model: object) -> dict: 10 | result = {} 11 | fields = vars(model) 12 | for name, value in fields.items(): 13 | if not value: 14 | continue 15 | 16 | key = ModelUtil.convert_snake_case_fields_to_camel_case(name) 17 | 18 | if isinstance(value, (int, float, str, bool)): 19 | result[key] = value 20 | elif isinstance(value, dict): 21 | result[key] = ModelUtil.to_json(value) 22 | elif isinstance(value, (list, tuple)): 23 | result[key] = [] 24 | for item in value: 25 | if isinstance(item, (int, float, str, bool)): 26 | result[key].append(item) 27 | else: 28 | result[key].append(ModelUtil.to_json(item)) 29 | else: 30 | result[key] = ModelUtil.to_json(value) 31 | 32 | return result 33 | 34 | @staticmethod 35 | def convert_snake_case_fields_to_camel_case(key: str) -> str: 36 | words = key.split("_") 37 | key = words[0] + "".join(word[:1].upper() + word[1:] for word in words[1:]) 38 | return key 39 | -------------------------------------------------------------------------------- /src/bitpay/utils/token_container.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from bitpay.exceptions.bitpay_exception import BitPayException 4 | from bitpay.exceptions.bitpay_exception_provider import BitPayExceptionProvider 5 | from bitpay.models.facade import Facade 6 | 7 | 8 | class TokenContainer: 9 | __data: Dict[Facade, str] = {} 10 | 11 | def get_access_token(self, facade: Facade) -> str: 12 | """ 13 | :raises BitPayGenericException 14 | """ 15 | try: 16 | return self.__data[facade] 17 | except Exception as exe: 18 | BitPayExceptionProvider.throw_generic_exception_with_message( 19 | "There is no token for the specified key: " + facade.value 20 | ) 21 | raise BitPayException 22 | 23 | def put(self, key: Facade, value: str) -> None: 24 | self.__data[key] = value 25 | 26 | def add_pos(self, token: str) -> None: 27 | self.__data[Facade.POS] = token 28 | 29 | def add_merchant(self, token: str) -> None: 30 | self.__data[Facade.MERCHANT] = token 31 | 32 | def add_payout(self, token: str) -> None: 33 | self.__data[Facade.PAYOUT] = token 34 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/__init__.py -------------------------------------------------------------------------------- /tests/functional/.gitignore: -------------------------------------------------------------------------------- 1 | bitpay.config.json 2 | email.txt -------------------------------------------------------------------------------- /tests/functional/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/functional/__init__.py -------------------------------------------------------------------------------- /tests/functional/json/cancel_payout_group_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "cancelled": [ 3 | { 4 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 5 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 6 | "accountId": "2tRxwvX5JkVbhqBLGyanmF", 7 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 8 | "amount": 10, 9 | "currency": "USD", 10 | "ledgerCurrency": "GBP", 11 | "email": "john@doe.com", 12 | "reference": "payout_20210527", 13 | "label": "John Doe", 14 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 15 | "notificationEmail": "merchant@email.com", 16 | "effectiveDate": "2021-05-27T09:00:00.000Z", 17 | "requestDate": "2021-05-27T10:47:37.834Z", 18 | "status": "cancelled", 19 | "transactions": [] 20 | }, 21 | { 22 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 23 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 24 | "accountId": "2tRxwvX5JkVbhqBLGyanmF", 25 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 26 | "amount": 20, 27 | "currency": "USD", 28 | "ledgerCurrency": "GBP", 29 | "email": "john@doe.com", 30 | "reference": "payout_20210527", 31 | "label": "John Doe 2", 32 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 33 | "notificationEmail": "merchant@email.com", 34 | "effectiveDate": "2021-05-27T09:00:00.000Z", 35 | "requestDate": "2021-05-27T10:47:37.834Z", 36 | "status": "cancelled", 37 | "transactions": [] 38 | } 39 | ], 40 | "failed": [ 41 | { 42 | "errMessage": "PayoutId is missing or invalid", 43 | "payoutId": "D8tgWzn1psUua4NYWW1vYo" 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /tests/functional/json/cancel_refund_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "WoE46gSLkJQS48RJEiNw3L", 3 | "invoice": "Hpqc63wvE1ZjzeeH4kEycF", 4 | "reference": "Test refund", 5 | "status": "cancelled", 6 | "amount": 10, 7 | "transactionCurrency": "BTC", 8 | "transactionAmount": 0.000594, 9 | "transactionRefundFee": 0.000002, 10 | "currency": "USD", 11 | "lastRefundNotification": "2021-08-29T20:45:35.368Z", 12 | "refundFee": 0.04, 13 | "immediate": false, 14 | "buyerPaysRefundFee": false, 15 | "requestDate": "2021-08-29T20:45:34.000Z" 16 | } -------------------------------------------------------------------------------- /tests/functional/json/create_bill_by_pos_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "address1": "2630 Hegal Place", 3 | "address2": "Apt 42", 4 | "cc": [ 5 | "jane@doe.com" 6 | ], 7 | "city": "Alexandria", 8 | "country": "US", 9 | "currency": "USD", 10 | "dueDate": "2021-5-31", 11 | "email": "some@email.com", 12 | "items": [ 13 | { 14 | "description": "Test Item 1", 15 | "price": 6.0, 16 | "quantity": 1 17 | }, 18 | { 19 | "description": "Test Item 2", 20 | "price": 4.0, 21 | "quantity": 1 22 | } 23 | ], 24 | "name": "John Doe", 25 | "number": "bill1234-ABCD", 26 | "passProcessingFee": true, 27 | "phone": "555-123-456", 28 | "state": "VA", 29 | "token": "somePosToken", 30 | "zip": "23242" 31 | } -------------------------------------------------------------------------------- /tests/functional/json/create_bill_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "address1": "2630 Hegal Place", 3 | "address2": "Apt 42", 4 | "cc": [ 5 | "jane@doe.com" 6 | ], 7 | "city": "Alexandria", 8 | "country": "US", 9 | "currency": "USD", 10 | "dueDate": "2021-5-31", 11 | "email": "some@email.com", 12 | "items": [ 13 | { 14 | "description": "Test Item 1", 15 | "price": 6.0, 16 | "quantity": 1 17 | }, 18 | { 19 | "description": "Test Item 2", 20 | "price": 4.0, 21 | "quantity": 1 22 | } 23 | ], 24 | "name": "John Doe", 25 | "number": "bill1234-ABCD", 26 | "passProcessingFee": true, 27 | "phone": "555-123-456", 28 | "state": "VA", 29 | "token": "someMerchantToken", 30 | "zip": "23242" 31 | } -------------------------------------------------------------------------------- /tests/functional/json/create_bill_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "draft", 3 | "url": "https://bitpay.com/bill?id=X6KJbe9RxAGWNReCwd1xRw&resource=bills", 4 | "number": "bill1234-ABCD", 5 | "createdDate": "2021-05-21T09:48:02.373Z", 6 | "dueDate": "2021-05-31T00:00:00.000Z", 7 | "currency": "USD", 8 | "email": "john@doe.com", 9 | "cc": [ 10 | "jane@doe.com" 11 | ], 12 | "passProcessingFee": true, 13 | "id": "X6KJbe9RxAGWNReCwd1xRw", 14 | "items": [ 15 | { 16 | "id": "EL4vx41Nxc5RYhbqDthjE", 17 | "description": "Test Item 1", 18 | "price": 6, 19 | "quantity": 1 20 | }, 21 | { 22 | "id": "6spPADZ2h6MfADvnhfsuBt", 23 | "description": "Test Item 2", 24 | "price": 4, 25 | "quantity": 1 26 | } 27 | ], 28 | "token": "qVVgRARN6fKtNZ7Tcq6qpoPBBE3NxdrmdMD883RyMK4Pf8EHENKVxCXhRwyynWveo" 29 | } -------------------------------------------------------------------------------- /tests/functional/json/create_invoice_by_pos_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "price": 2.16, 3 | "currency": "EUR", 4 | "orderId": "98e572ea-910e-415d-b6de-65f5090680f6", 5 | "fullNotifications": true, 6 | "extendedNotifications": true, 7 | "transactionSpeed": "medium", 8 | "notificationURL": "https://hookbin.com/lJnJg9WW7MtG9GZlPVdj", 9 | "redirectURL": "https://hookbin.com/lJnJg9WW7MtG9GZlPVdj", 10 | "posData": "98e572ea35hj356xft8y8cgh56h5090680f6", 11 | "itemDesc": "Ab tempora sed ut.", 12 | "buyer": { 13 | "name": "Bily Matthews", 14 | "email": "sandbox@bitpay.com", 15 | "address1": "168 General Grove", 16 | "address2": "sandbox@bitpay.com", 17 | "country": "AD", 18 | "locality": "Port Horizon", 19 | "notify": true, 20 | "phone": "+99477512690", 21 | "postalCode": "KY7 1TH", 22 | "region": "New Port", 23 | "buyerEmail": "sandbox1@bitpay.com" 24 | }, 25 | "guid": "chc9kj52-04g0-4b6f-941d-3a844e352758", 26 | "token": "somePosToken" 27 | } -------------------------------------------------------------------------------- /tests/functional/json/create_invoice_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "price": 2.16, 3 | "currency": "EUR", 4 | "orderId": "98e572ea-910e-415d-b6de-65f5090680f6", 5 | "fullNotifications": true, 6 | "extendedNotifications": true, 7 | "transactionSpeed": "medium", 8 | "notificationURL": "https://hookbin.com/lJnJg9WW7MtG9GZlPVdj", 9 | "redirectURL": "https://hookbin.com/lJnJg9WW7MtG9GZlPVdj", 10 | "posData": "98e572ea35hj356xft8y8cgh56h5090680f6", 11 | "itemDesc": "Ab tempora sed ut.", 12 | "buyer": { 13 | "name": "Bily Matthews", 14 | "email": "sandbox@bitpay.com", 15 | "address1": "168 General Grove", 16 | "address2": "sandbox@bitpay.com", 17 | "country": "AD", 18 | "locality": "Port Horizon", 19 | "notify": true, 20 | "phone": "+99477512690", 21 | "postalCode": "KY7 1TH", 22 | "region": "New Port", 23 | "buyerEmail": "sandbox1@bitpay.com" 24 | }, 25 | "guid": "chc9kj52-04g0-4b6f-941d-3a844e352758", 26 | "token": "someMerchantToken" 27 | } -------------------------------------------------------------------------------- /tests/functional/json/create_payout_group_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "instructions": [ 3 | { 4 | "amount": 10, 5 | "currency": "USD", 6 | "ledgerCurrency": "USD", 7 | "reference": "payout_20210527", 8 | "notificationURL": "https:\/\/yournotiticationURL.com\/wed3sa0wx1rz5bg0bv97851eqx", 9 | "notificationEmail": "merchant@email.com", 10 | "email": "john@doe.com", 11 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 12 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2" 13 | } 14 | ], 15 | "token": "somePayoutToken" 16 | } -------------------------------------------------------------------------------- /tests/functional/json/create_payout_group_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": [ 3 | { 4 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 5 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 6 | "accountId": "SJcWZCFq344DL8QnXpdBNM", 7 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 8 | "amount": 10, 9 | "currency": "USD", 10 | "ledgerCurrency": "GBP", 11 | "email": "john@doe.com", 12 | "reference": "payout_20210527", 13 | "label": "John Doe", 14 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 15 | "notificationEmail": "merchant@email.com", 16 | "effectiveDate": "2021-05-27T09:00:00.000Z", 17 | "requestDate": "2021-05-27T10:47:37.834Z", 18 | "status": "new", 19 | "groupId": "SxKRk4MdW8Ae3vsoR6UPQE", 20 | "transactions": [] 21 | } 22 | ], 23 | "failed": [ 24 | { 25 | "errMessage": "Ledger currency is required", 26 | "payee": "john@doe.com" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /tests/functional/json/create_refund_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid":"37bd36bd-6fcb-409c-a907-47f9244302aa", 3 | "id": "Eso8srxKJR5U71ahCspAAA", 4 | "invoice": "UZjwcYkWAKfTMn9J1yyfs4", 5 | "reference": "someReference", 6 | "status": "preview", 7 | "amount": 10, 8 | "transactionCurrency": "BTC", 9 | "transactionAmount": 7.19E-4, 10 | "transactionRefundFee": 2.0E-6, 11 | "currency": "USD", 12 | "refundFee": 0.03, 13 | "immediate": false, 14 | "buyerPaysRefundFee": false, 15 | "requestDate": "2022-11-14T12:20:47.000Z" 16 | } -------------------------------------------------------------------------------- /tests/functional/json/get_bill_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "draft", 3 | "url": "https://bitpay.com/bill?id=3Zpmji8bRKxWJo2NJbWX5H&resource=bills", 4 | "number": "bill1234-EFGH", 5 | "createdDate": "2021-05-21T09:51:04.126Z", 6 | "dueDate": "2021-05-31T00:00:00.000Z", 7 | "currency": "USD", 8 | "name": "John Doe", 9 | "address1": "2630 Hegal Place", 10 | "address2": "Apt 42", 11 | "city": "Alexandria", 12 | "state": "VA", 13 | "zip": "23242", 14 | "country": "US", 15 | "email": "john@doe.com", 16 | "cc": [ 17 | "jane@doe.com" 18 | ], 19 | "phone": "555-123-456", 20 | "passProcessingFee": true, 21 | "emailBill": true, 22 | "id": "X6KJbe9RxAGWNReCwd1xRw", 23 | "merchant": "7HyKWn3d4xdhAMQYAEVxVq", 24 | "items": [ 25 | { 26 | "id": "NV35GRWtrdB2cmGEjY4LKY", 27 | "description": "Test Item 1", 28 | "price": 6, 29 | "quantity": 1 30 | }, 31 | { 32 | "id": "Apy3i2TpzHRYP8tJCkrZMT", 33 | "description": "Test Item 2", 34 | "price": 4, 35 | "quantity": 1 36 | } 37 | ], 38 | "token": "6EBQR37MgDJPfEiLY3jtRq7eTP2aodR5V5wmXyyZhru5FM5yF4RCGKYQtnT7nhwHjA" 39 | } -------------------------------------------------------------------------------- /tests/functional/json/get_bills_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "status": "draft", 4 | "url": "https://bitpay.com/bill?id=X6KJbe9RxAGWNReCwd1xRw&resource=bills", 5 | "number": "bill1234-ABCD", 6 | "createdDate": "2021-05-21T09:48:02.373Z", 7 | "dueDate": "2021-05-31T00:00:00.000Z", 8 | "currency": "USD", 9 | "name": "John Doe", 10 | "address1": "2630 Hegal Place", 11 | "address2": "Apt 42", 12 | "city": "Alexandria", 13 | "state": "VA", 14 | "zip": "23242", 15 | "country": "US", 16 | "email": "john@doe.com", 17 | "cc": [ 18 | "jane@doe.com" 19 | ], 20 | "phone": "555-123-456", 21 | "passProcessingFee": true, 22 | "emailBill": true, 23 | "id": "X6KJbe9RxAGWNReCwd1xRw", 24 | "merchant": "7HyKWn3d4xdhAMQYAEVxVq", 25 | "items": [ 26 | { 27 | "id": "EL4vx41Nxc5RYhbqDthjE", 28 | "description": "Test Item 1", 29 | "price": 6, 30 | "quantity": 1 31 | }, 32 | { 33 | "id": "6spPADZ2h6MfADvnhfsuBt", 34 | "description": "Test Item 2", 35 | "price": 4, 36 | "quantity": 1 37 | } 38 | ], 39 | "token": "6EBQR37MgDJPfEiLY3jtRqBMYLg8XSDqhp2kp7VSDqCMHGHnsw4bqnnwQmtehzCvSo" 40 | }, 41 | { 42 | "status": "draft", 43 | "url": "https://bitpay.com/bill?id=3Zpmji8bRKxWJo2NJbWX5H&resource=bills", 44 | "number": "bill1234-EFGH", 45 | "createdDate": "2021-05-21T09:51:04.126Z", 46 | "dueDate": "2021-05-31T00:00:00.000Z", 47 | "currency": "USD", 48 | "name": "John Doe", 49 | "address1": "2630 Hegal Place", 50 | "address2": "Apt 42", 51 | "city": "Alexandria", 52 | "state": "VA", 53 | "zip": "23242", 54 | "country": "US", 55 | "email": "john@doe.com", 56 | "cc": [ 57 | "jane@doe.com" 58 | ], 59 | "phone": "555-123-456", 60 | "passProcessingFee": true, 61 | "emailBill": true, 62 | "id": "3Zpmji8bRKxWJo2NJbWX5H", 63 | "merchant": "7HyKWn3d4xdhAMQYAEVxVq", 64 | "items": [ 65 | { 66 | "id": "NV35GRWtrdB2cmGEjY4LKY", 67 | "description": "Test Item 1", 68 | "price": 6, 69 | "quantity": 1 70 | }, 71 | { 72 | "id": "Apy3i2TpzHRYP8tJCkrZMT", 73 | "description": "Test Item 2", 74 | "price": 4, 75 | "quantity": 1 76 | } 77 | ], 78 | "token": "6EBQR37MgDJPfEiLY3jtRq7eTP2aodR5V5wmXyyZhru5FM5yF4RCGKYQtnT7nhwHjA" 79 | } 80 | ] -------------------------------------------------------------------------------- /tests/functional/json/get_invoice_event_token.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://bitpay.com/events", 3 | "token": "4MuqDPt93i9Xbf8SnAPniwbGeNLW8A3ScgAmukFMgFUFRqTLuuhVdAFfePPysVqL2P", 4 | "events": [ 5 | "payment", 6 | "confirmation" 7 | ], 8 | "actions": [ 9 | "subscribe", 10 | "unsubscribe" 11 | ] 12 | } -------------------------------------------------------------------------------- /tests/functional/json/get_ledger_entries_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Invoice", 4 | "amount": 823000000, 5 | "code": 1000, 6 | "description": "20210510_fghij", 7 | "timestamp": "2021-05-10T20:08:52.919Z", 8 | "txType": "sale", 9 | "scale": 100000000, 10 | "invoiceId": "Hpqc63wvE1ZjzeeH4kEycF", 11 | "buyerFields": { 12 | "buyerName": "John Doe", 13 | "buyerAddress1": "2630 Hegal Place", 14 | "buyerAddress2": "Apt 42", 15 | "buyerCity": "Alexandria", 16 | "buyerState": "VA", 17 | "buyerZip": "23242", 18 | "buyerCountry": "US", 19 | "buyerPhone": "555-123-456", 20 | "buyerNotify": true, 21 | "buyerEmail": "john@doe.com" 22 | }, 23 | "invoiceAmount": 10, 24 | "invoiceCurrency": "USD", 25 | "transactionCurrency": "BCH", 26 | "id": "FR4rgfADCRNmAhtz1Ci4kU" 27 | }, 28 | { 29 | "type": "Invoice Fee", 30 | "amount": -8000000, 31 | "code": 1023, 32 | "description": "Invoice Fee", 33 | "timestamp": "2021-05-10T20:08:52.919Z", 34 | "txType": "Invoice Fee", 35 | "scale": 100000000, 36 | "invoiceId": "Hpqc63wvE1ZjzeeH4kEycF", 37 | "buyerFields": { 38 | "buyerName": "John Doe", 39 | "buyerAddress1": "2630 Hegal Place", 40 | "buyerAddress2": "Apt 42", 41 | "buyerCity": "Alexandria", 42 | "buyerState": "VA", 43 | "buyerZip": "23242", 44 | "buyerCountry": "US", 45 | "buyerPhone": "555-123-456", 46 | "buyerNotify": true, 47 | "buyerEmail": "john@doe.com" 48 | }, 49 | "invoiceAmount": 10, 50 | "invoiceCurrency": "USD", 51 | "transactionCurrency": "BCH", 52 | "id": "XCkhgHKP2pSme4qszMpM3B" 53 | }, 54 | { 55 | "type": "Invoice Refund", 56 | "supportRequest": "SYyrnbRCJ78V1DknHakKPo", 57 | "amount": -823000000, 58 | "code": 1020, 59 | "description": "Invoice Refund", 60 | "timestamp": "2021-05-12T13:00:45.063Z", 61 | "txType": "Invoice Refund", 62 | "scale": 100000000, 63 | "invoiceId": "Hpqc63wvE1ZjzeeH4kEycF", 64 | "buyerFields": { 65 | "buyerName": "John Doe", 66 | "buyerAddress1": "2630 Hegal Place", 67 | "buyerAddress2": "Apt 42", 68 | "buyerCity": "Alexandria", 69 | "buyerState": "VA", 70 | "buyerZip": "23242", 71 | "buyerCountry": "US", 72 | "buyerPhone": "555-123-456", 73 | "buyerNotify": true, 74 | "buyerEmail": "john@doe.com" 75 | }, 76 | "invoiceAmount": 10, 77 | "invoiceCurrency": "USD", 78 | "transactionCurrency": "BCH", 79 | "id": "PBqakmWMZ2H3RwhGq9vCsg" 80 | } 81 | ] -------------------------------------------------------------------------------- /tests/functional/json/get_ledgers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "currency": "EUR", 4 | "balance": 0 5 | }, 6 | { 7 | "currency": "USD", 8 | "balance": 2389.82 9 | }, 10 | { 11 | "currency": "BTC", 12 | "balance": 0.000287 13 | } 14 | ] -------------------------------------------------------------------------------- /tests/functional/json/get_payout_recipient_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "john.smith@email.com", 3 | "label": "Bob123", 4 | "status": "invited", 5 | "id": "JA4cEtmBxCp5cybtnh1rds", 6 | "shopperId": null, 7 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXcejgPXVmZ4Ae3oGaCGBFKQf" 8 | } -------------------------------------------------------------------------------- /tests/functional/json/get_payout_recipients_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "email": "alice@email.com", 4 | "label": "Alice", 5 | "status": "invited", 6 | "id": "JA4cEtmBxCp5cybtnh1rds", 7 | "shopperId": null, 8 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXcejgPXVmZ4Ae3oGaCGBFKQf" 9 | }, 10 | { 11 | "email": "bob@email.com", 12 | "label": "Bob", 13 | "status": "invited", 14 | "id": "X3icwc4tE8KJ5hEPNPpDXW", 15 | "shopperId": null, 16 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXrrBAB9vRY3BVxGLbAa6uEx7" 17 | } 18 | ] -------------------------------------------------------------------------------- /tests/functional/json/get_payout_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 3 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 4 | "accountId": "SJcWZCFq344DL8QnXpdBNM", 5 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 6 | "amount": 10, 7 | "currency": "USD", 8 | "ledgerCurrency": "GBP", 9 | "exchangeRates": { 10 | "BTC": { 11 | "USD": 39390.47, 12 | "GBP": 27883.962246420004 13 | } 14 | }, 15 | "email": "john@doe.com", 16 | "reference": "payout_20210527", 17 | "label": "John Doe", 18 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 19 | "notificationEmail": "merchant@email.com", 20 | "effectiveDate": "2021-05-27T09:00:00.000Z", 21 | "requestDate": "2021-05-27T10:47:37.834Z", 22 | "dateExecuted": "2021-05-27T09:00:00.000Z", 23 | "status": "complete", 24 | "transactions": [ 25 | { 26 | "txid": "db53d7e2bf3385a31257ce09396202d9c2823370a5ca186db315c45e24594057", 27 | "amount": 0.000254, 28 | "date": "2021-05-27T11:04:23.155Z" 29 | } 30 | ], 31 | "token": "6RZSTPtnzEaroAe2X4YijenRiqteRDNvzbT8NjtcHjUVd9FUFwa7dsX8RFgRDDC5SL" 32 | } -------------------------------------------------------------------------------- /tests/functional/json/get_payouts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 4 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 5 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 6 | "amount": 10, 7 | "currency": "USD", 8 | "ledgerCurrency": "GBP", 9 | "exchangeRates": { 10 | "BTC": { 11 | "USD": 39390.47, 12 | "GBP": 27883.962246420004 13 | } 14 | }, 15 | "email": "john@doe.com", 16 | "reference": "payout_20210527", 17 | "label": "John Doe", 18 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 19 | "notificationEmail": "merchant@email.com", 20 | "effectiveDate": "2021-05-27T09:00:00.000Z", 21 | "requestDate": "2021-05-27T10:47:37.834Z", 22 | "status": "complete", 23 | "transactions": [ 24 | { 25 | "txid": "db53d7e2bf3385a31257ce09396202d9c2823370a5ca186db315c45e24594057", 26 | "amount": 0.000254, 27 | "date": "2021-05-27T11:04:23.155Z" 28 | } 29 | ], 30 | "token": "9pVLfvdjt59q1JiY2JEsf2uzeeEpSqDwwfRAzuFr9CcrxZX25rTnP6HdRhsMBGLArz" 31 | }, 32 | { 33 | "id": "KMXZeQigXG6T5abzCJmTcH", 34 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 35 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 36 | "amount": 10, 37 | "currency": "USD", 38 | "ledgerCurrency": "GBP", 39 | "email": "jane@doe.com", 40 | "reference": "payout_20210528", 41 | "label": "Jane Doe", 42 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 43 | "notificationEmail": "merchant@email.com", 44 | "effectiveDate": "2021-05-28T09:00:00.000Z", 45 | "requestDate": "2021-05-28T10:23:43.765Z", 46 | "status": "cancelled", 47 | "transactions": [], 48 | "token": "9pVLfvdjt59q1JiY2JEsf2hr5FsjimfY4qRLFi85tMiXSCkJ9mQ2oSQqYKVangKaro" 49 | } 50 | ] -------------------------------------------------------------------------------- /tests/functional/json/get_rates_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "code":"BTC", 4 | "name":"Bitcoin", 5 | "rate":1 6 | }, 7 | { 8 | "code":"BCH", 9 | "name":"Bitcoin Cash", 10 | "rate":50.77 11 | }, 12 | { 13 | "code":"USD", 14 | "name":"US Dollar", 15 | "rate":41248.11 16 | }, 17 | { 18 | "code":"EUR", 19 | "name":"Eurozone Euro", 20 | "rate":33823.04 21 | }, 22 | { 23 | "code":"GBP", 24 | "name":"Pound Sterling", 25 | "rate":29011.49 26 | }, 27 | { 28 | "code":"JPY", 29 | "name":"Japanese Yen", 30 | "rate":4482741 31 | }, 32 | { 33 | "code":"CAD", 34 | "name":"Canadian Dollar", 35 | "rate":49670.85 36 | }, 37 | { 38 | "code":"AUD", 39 | "name":"Australian Dollar", 40 | "rate":53031.99 41 | }, 42 | { 43 | "code":"CNY", 44 | "name":"Chinese Yuan", 45 | "rate":265266.57 46 | } 47 | ] -------------------------------------------------------------------------------- /tests/functional/json/get_refund_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid": "37bd36bd-6fcb-409c-a907-47f9244302aa", 3 | "id": "WoE46gSLkJQS48RJEiNw3L", 4 | "invoice": "Hpqc63wvE1ZjzeeH4kEycF", 5 | "reference": "Test refund", 6 | "status": "created", 7 | "amount": 10, 8 | "transactionCurrency": "BTC", 9 | "transactionAmount": 0.000594, 10 | "transactionRefundFee": 0.000002, 11 | "currency": "USD", 12 | "lastRefundNotification": "2021-08-29T20:45:35.368Z", 13 | "refundFee": 0.04, 14 | "immediate": false, 15 | "buyerPaysRefundFee": false, 16 | "requestDate": "2021-08-29T20:45:34.000Z" 17 | } -------------------------------------------------------------------------------- /tests/functional/json/get_refunds_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "WoE46gSLkJQS48RJEiNw3L", 4 | "invoice": "Hpqc63wvE1ZjzeeH4kEycF", 5 | "reference": "Test refund", 6 | "status": "created", 7 | "amount": 10, 8 | "transactionCurrency": "BTC", 9 | "transactionAmount": 0.000594, 10 | "transactionRefundFee": 0.000002, 11 | "currency": "USD", 12 | "lastRefundNotification": "2021-08-29T20:45:35.368Z", 13 | "refundFee": 0.04, 14 | "immediate": false, 15 | "buyerPaysRefundFee": false, 16 | "requestDate": "2021-08-29T20:45:34.000Z" 17 | } 18 | ] -------------------------------------------------------------------------------- /tests/functional/json/get_settlement_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "RPWTabW8urd3xWv2To989v", 3 | "accountId": "YJCgTf3jrXHkUVzLQ7y4eg", 4 | "status": "processing", 5 | "currency": "EUR", 6 | "payoutInfo": { 7 | "label": "Corporate account", 8 | "bankCountry": "Netherlands", 9 | "name": "Test Organization", 10 | "bank": "Test", 11 | "swift": "RABONL2U", 12 | "account": "NL85ABNA0000000000" 13 | }, 14 | "dateCreated": "2021-05-11T09:05:00.176Z", 15 | "dateExecuted": "2021-05-11T11:52:29.681Z", 16 | "openingDate": "2021-05-10T09:00:00.000Z", 17 | "closingDate": "2021-05-11T09:00:00.000Z", 18 | "openingBalance": 23.27, 19 | "ledgerEntriesSum": 20.82, 20 | "withholdings": [ 21 | { 22 | "amount": 8.21, 23 | "code": "W005", 24 | "description": "Pending Refunds" 25 | } 26 | ], 27 | "withholdingsSum": 8.21, 28 | "totalAmount": 35.88, 29 | "token": "2GrR6GDeYxUFYM9sDKViy6nFFTy4Rjvm1SYdLBjK46jkeJdgUTRccRfhtwkhNcuZky" 30 | } -------------------------------------------------------------------------------- /tests/functional/json/get_settlements_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "KBkdURgmE3Lsy9VTnavZHX", 4 | "accountId": "YJCgTf3jrXHkUVzLQ7y4eg", 5 | "status": "processing", 6 | "currency": "EUR", 7 | "payoutInfo": { 8 | "label": "Corporate account", 9 | "bankCountry": "Netherlands", 10 | "name": "Test Organization", 11 | "bank": "Test", 12 | "swift": "RABONL2U", 13 | "account": "NL85ABNA0000000000" 14 | }, 15 | "dateCreated": "2021-05-10T09:05:00.176Z", 16 | "dateExecuted": "2021-05-10T11:52:29.681Z", 17 | "openingDate": "2021-05-09T09:00:00.000Z", 18 | "closingDate": "2021-05-10T09:00:00.000Z", 19 | "openingBalance": 1.27, 20 | "ledgerEntriesSum": 20.82, 21 | "withholdings": [], 22 | "withholdingsSum": 0, 23 | "totalAmount": 22.09, 24 | "token": "2gBtViSiBWSEJGo1LfaMFHoaBRzE2jek2VitKAYeenj2SRiTVSCgRvs1WTN8w4w8Lc" 25 | }, 26 | { 27 | "id": "RPWTabW8urd3xWv2To989v", 28 | "accountId": "YJCgTf3jrXHkUVzLQ7y4eg", 29 | "status": "processing", 30 | "currency": "EUR", 31 | "payoutInfo": { 32 | "label": "Corporate account", 33 | "bankCountry": "Netherlands", 34 | "name": "Test Organization", 35 | "bank": "Test", 36 | "swift": "RABONL2U", 37 | "account": "NL85ABNA0000000000" 38 | }, 39 | "dateCreated": "2021-05-11T09:05:00.176Z", 40 | "dateExecuted": "2021-05-11T11:52:29.681Z", 41 | "openingDate": "2021-05-10T09:00:00.000Z", 42 | "closingDate": "2021-05-11T09:00:00.000Z", 43 | "openingBalance": 23.27, 44 | "ledgerEntriesSum": 20.82, 45 | "withholdings": [ 46 | { 47 | "amount": 8.21, 48 | "code": "W005", 49 | "description": "Pending Refunds" 50 | } 51 | ], 52 | "withholdingsSum": 8.21, 53 | "totalAmount": 35.88, 54 | "token": "2gBtViSiBWSEJitKAYSCgRvs1WTN8w4Go1Leenj2SRiTVFHoaBRzE2jek2VfaMw8Lc" 55 | } 56 | ] -------------------------------------------------------------------------------- /tests/functional/json/submit_payout_recipients_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "recipients": [ 3 | { 4 | "email": "alice@email.com", 5 | "label": "Alice" 6 | }, 7 | { 8 | "email": "bob@email.com", 9 | "label": "Bob" 10 | } 11 | ], 12 | "guid": "chc9kj52-04g0-4b6f-941d-3a844e352758", 13 | "token": "somePayoutToken" 14 | } -------------------------------------------------------------------------------- /tests/functional/json/submit_payout_recipients_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "email": "alice@email.com", 4 | "label": "Alice", 5 | "status": "invited", 6 | "id": "JA4cEtmBxCp5cybtnh1rds", 7 | "shopperId": null, 8 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXcejgPXVmZ4Ae3oGaCGBFKQf" 9 | }, 10 | { 11 | "email": "bob@email.com", 12 | "label": "Bob", 13 | "status": "invited", 14 | "id": "X3icwc4tE8KJ5hEPNPpDXW", 15 | "shopperId": null, 16 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXrrBAB9vRY3BVxGLbAa6uEx7" 17 | } 18 | ] -------------------------------------------------------------------------------- /tests/functional/json/submit_payout_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "amount": 10.0, 3 | "currency": "USD", 4 | "email": "john@doe.com", 5 | "ledgerCurrency": "GBP", 6 | "notificationEmail": "merchant@email.com", 7 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 8 | "reference": "payout_20210527", 9 | "token": "somePayoutToken", 10 | "label": "John Doe" 11 | } -------------------------------------------------------------------------------- /tests/functional/json/submit_payout_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 3 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 4 | "accountId": "SJcWZCFq344DL8QnXpdBNM", 5 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 6 | "amount": 10, 7 | "currency": "USD", 8 | "ledgerCurrency": "GBP", 9 | "email": "john@doe.com", 10 | "reference": "payout_20210527", 11 | "label": "John Doe", 12 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 13 | "notificationEmail": "merchant@email.com", 14 | "effectiveDate": "2021-05-27T09:00:00.000Z", 15 | "requestDate": "2021-05-27T10:47:37.834Z", 16 | "status": "new", 17 | "transactions": [], 18 | "token": "6RZSTPtnzEaroAe2X4YijenRiqteRDNvzbT8NjtcHjUVd9FUFwa7dsX8RFgRDDC5SL" 19 | } -------------------------------------------------------------------------------- /tests/functional/json/success_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": {}, 4 | "message": null 5 | } -------------------------------------------------------------------------------- /tests/functional/json/update_bill_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "address1": "2630 Hegal Place", 3 | "address2": "Apt 42", 4 | "cc": [ 5 | "jane@doe.com" 6 | ], 7 | "city": "Alexandria", 8 | "country": "US", 9 | "currency": "USD", 10 | "dueDate": "2021-5-31", 11 | "email": "some@email.com", 12 | "items": [ 13 | { 14 | "description": "Test Item 1", 15 | "price": 6.0, 16 | "quantity": 1 17 | }, 18 | { 19 | "description": "Test Item 2", 20 | "price": 4.0, 21 | "quantity": 1 22 | } 23 | ], 24 | "name": "John Doe", 25 | "number": "bill1234-ABCD", 26 | "passProcessingFee": true, 27 | "phone": "555-123-456", 28 | "state": "VA", 29 | "zip": "23242", 30 | "token": "billToken" 31 | } -------------------------------------------------------------------------------- /tests/functional/json/update_payout_recipient_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid": "chc9kj52-04g0-4b6f-941d-3a844e352758", 3 | "label": "Bob123", 4 | "token": "somePayoutToken" 5 | } -------------------------------------------------------------------------------- /tests/functional/json/update_payout_recipient_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "bob@email.com", 3 | "label": "Bob123", 4 | "status": "invited", 5 | "id": "X3icwc4tE8KJ5hEPNPpDXW", 6 | "shopperId": null, 7 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXrrBAB9vRY3BVxGLbAa6uEx7" 8 | } -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/bitpay.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "BitPayConfiguration": { 3 | "Environment": "Test", 4 | "EnvConfig": { 5 | "Test": { 6 | "PrivateKeyPath": null, 7 | "PrivateKey": "-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIOxJU5ZaTATE35wP1eyOCLm5OuBRH60/jbb3u5UBJmFcoAcGBSuBBAAK\noUQDQgAE3aKZq/x/RQdBCL6eeXiF3XkJWul0I8RAjNxuuvDk0Fi69Ojnfwz3z0WA\najNGenn4Hn4NLw483gjpqTzlto9IiQ==\n-----END EC PRIVATE KEY-----\n", 8 | "ApiTokens": { 9 | "merchant": "someMerchantToken", 10 | "payout": "somePayoutToken" 11 | }, 12 | "proxy": null 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /tests/unit/clients/response_with_errors.json: -------------------------------------------------------------------------------- 1 | {"errors":[{"error":"Missing required parameter.","param":"price"},{"error":"Missing required parameter.","param":"currency"}]} -------------------------------------------------------------------------------- /tests/unit/clients/test_response_parser.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | import pytest 5 | 6 | from requests import Response, PreparedRequest 7 | 8 | from bitpay.clients.response_parser import ResponseParser 9 | from bitpay.exceptions.bitpay_api_exception import BitPayApiException 10 | 11 | 12 | @pytest.mark.unit 13 | def test_handle_multiple_errors(mocker): 14 | with pytest.raises(BitPayApiException) as exc_info: 15 | with open( 16 | os.path.abspath(os.path.dirname(__file__)) 17 | + "/response_with_errors.json", 18 | "r", 19 | ) as file: 20 | response_json = json.load(file) 21 | request = mocker.Mock(spec=PreparedRequest) 22 | request.method = "POST" 23 | request.url = "https://some-url.com" 24 | response = mocker.Mock(spec=Response) 25 | response.request = request 26 | response.json.return_value = response_json 27 | ResponseParser.response_to_json_string(response) 28 | 29 | assert str(exc_info.value) == "Missing required parameter price. Missing required parameter currency." 30 | -------------------------------------------------------------------------------- /tests/unit/json/cancel_payout_group_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "cancelled": [ 3 | { 4 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 5 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 6 | "accountId": "2tRxwvX5JkVbhqBLGyanmF", 7 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 8 | "amount": 10, 9 | "currency": "USD", 10 | "ledgerCurrency": "GBP", 11 | "email": "john@doe.com", 12 | "reference": "payout_20210527", 13 | "label": "John Doe", 14 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 15 | "notificationEmail": "merchant@email.com", 16 | "effectiveDate": "2021-05-27T09:00:00.000Z", 17 | "requestDate": "2021-05-27T10:47:37.834Z", 18 | "status": "cancelled", 19 | "transactions": [] 20 | }, 21 | { 22 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 23 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 24 | "accountId": "2tRxwvX5JkVbhqBLGyanmF", 25 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 26 | "amount": 20, 27 | "currency": "USD", 28 | "ledgerCurrency": "GBP", 29 | "email": "john@doe.com", 30 | "reference": "payout_20210527", 31 | "label": "John Doe 2", 32 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 33 | "notificationEmail": "merchant@email.com", 34 | "effectiveDate": "2021-05-27T09:00:00.000Z", 35 | "requestDate": "2021-05-27T10:47:37.834Z", 36 | "status": "cancelled", 37 | "transactions": [] 38 | } 39 | ], 40 | "failed": [ 41 | { 42 | "errMessage": "PayoutId is missing or invalid", 43 | "payoutId": "D8tgWzn1psUua4NYWW1vYo" 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /tests/unit/json/cancel_refund_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "WoE46gSLkJQS48RJEiNw3L", 3 | "invoice": "Hpqc63wvE1ZjzeeH4kEycF", 4 | "reference": "Test refund", 5 | "status": "cancelled", 6 | "amount": 10, 7 | "transactionCurrency": "BTC", 8 | "transactionAmount": 0.000594, 9 | "transactionRefundFee": 0.000002, 10 | "currency": "USD", 11 | "lastRefundNotification": "2021-08-29T20:45:35.368Z", 12 | "refundFee": 0.04, 13 | "immediate": false, 14 | "buyerPaysRefundFee": false, 15 | "requestDate": "2021-08-29T20:45:34.000Z" 16 | } -------------------------------------------------------------------------------- /tests/unit/json/create_bill_by_pos_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "address1": "2630 Hegal Place", 3 | "address2": "Apt 42", 4 | "cc": [ 5 | "jane@doe.com" 6 | ], 7 | "city": "Alexandria", 8 | "country": "US", 9 | "currency": "USD", 10 | "dueDate": "2021-05-31T00:00:00.00Z", 11 | "email": "some@email.com", 12 | "items": [ 13 | { 14 | "description": "Test Item 1", 15 | "price": 6.0, 16 | "quantity": 1 17 | }, 18 | { 19 | "description": "Test Item 2", 20 | "price": 4.0, 21 | "quantity": 1 22 | } 23 | ], 24 | "name": "John Doe", 25 | "number": "bill1234-ABCD", 26 | "passProcessingFee": true, 27 | "phone": "555-123-456", 28 | "state": "VA", 29 | "token": "somePosToken", 30 | "zip": "23242" 31 | } -------------------------------------------------------------------------------- /tests/unit/json/create_bill_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "address1": "2630 Hegal Place", 3 | "address2": "Apt 42", 4 | "cc": [ 5 | "jane@doe.com" 6 | ], 7 | "city": "Alexandria", 8 | "country": "US", 9 | "currency": "USD", 10 | "dueDate": "2021-05-31T00:00:00.00Z", 11 | "email": "some@email.com", 12 | "items": [ 13 | { 14 | "description": "Test Item 1", 15 | "price": 6.0, 16 | "quantity": 1 17 | }, 18 | { 19 | "description": "Test Item 2", 20 | "price": 4.0, 21 | "quantity": 1 22 | } 23 | ], 24 | "name": "John Doe", 25 | "number": "bill1234-ABCD", 26 | "passProcessingFee": true, 27 | "phone": "555-123-456", 28 | "state": "VA", 29 | "token": "someMerchantToken", 30 | "zip": "23242" 31 | } -------------------------------------------------------------------------------- /tests/unit/json/create_bill_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "draft", 3 | "url": "https://bitpay.com/bill?id=X6KJbe9RxAGWNReCwd1xRw&resource=bills", 4 | "number": "bill1234-ABCD", 5 | "createdDate": "2021-05-21T09:48:02.373Z", 6 | "dueDate": "2021-05-31T00:00:00.000Z", 7 | "currency": "USD", 8 | "email": "john@doe.com", 9 | "cc": [ 10 | "jane@doe.com" 11 | ], 12 | "passProcessingFee": true, 13 | "id": "X6KJbe9RxAGWNReCwd1xRw", 14 | "items": [ 15 | { 16 | "id": "EL4vx41Nxc5RYhbqDthjE", 17 | "description": "Test Item 1", 18 | "price": 6, 19 | "quantity": 1 20 | }, 21 | { 22 | "id": "6spPADZ2h6MfADvnhfsuBt", 23 | "description": "Test Item 2", 24 | "price": 4, 25 | "quantity": 1 26 | } 27 | ], 28 | "token": "qVVgRARN6fKtNZ7Tcq6qpoPBBE3NxdrmdMD883RyMK4Pf8EHENKVxCXhRwyynWveo" 29 | } -------------------------------------------------------------------------------- /tests/unit/json/create_invoice_by_pos_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "price": 2.16, 3 | "currency": "EUR", 4 | "orderId": "98e572ea-910e-415d-b6de-65f5090680f6", 5 | "fullNotifications": true, 6 | "extendedNotifications": true, 7 | "transactionSpeed": "medium", 8 | "notificationURL": "https://hookbin.com/lJnJg9WW7MtG9GZlPVdj", 9 | "redirectURL": "https://hookbin.com/lJnJg9WW7MtG9GZlPVdj", 10 | "posData": "98e572ea35hj356xft8y8cgh56h5090680f6", 11 | "itemDesc": "Ab tempora sed ut.", 12 | "buyer": { 13 | "name": "Bily Matthews", 14 | "email": "sandbox@bitpay.com", 15 | "address1": "168 General Grove", 16 | "address2": "sandbox@bitpay.com", 17 | "country": "AD", 18 | "locality": "Port Horizon", 19 | "notify": true, 20 | "phone": "+99477512690", 21 | "postalCode": "KY7 1TH", 22 | "region": "New Port" 23 | }, 24 | "guid": "chc9kj52-04g0-4b6f-941d-3a844e352758", 25 | "token": "somePosToken" 26 | } -------------------------------------------------------------------------------- /tests/unit/json/create_invoice_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "price": 2.16, 3 | "currency": "EUR", 4 | "orderId": "98e572ea-910e-415d-b6de-65f5090680f6", 5 | "fullNotifications": true, 6 | "extendedNotifications": true, 7 | "transactionSpeed": "medium", 8 | "notificationURL": "https://hookbin.com/lJnJg9WW7MtG9GZlPVdj", 9 | "redirectURL": "https://hookbin.com/lJnJg9WW7MtG9GZlPVdj", 10 | "posData": "98e572ea35hj356xft8y8cgh56h5090680f6", 11 | "itemDesc": "Ab tempora sed ut.", 12 | "buyer": { 13 | "name": "Bily Matthews", 14 | "email": "sandbox@bitpay.com", 15 | "address1": "168 General Grove", 16 | "address2": "sandbox@bitpay.com", 17 | "country": "AD", 18 | "locality": "Port Horizon", 19 | "notify": true, 20 | "phone": "+99477512690", 21 | "postalCode": "KY7 1TH", 22 | "region": "New Port" 23 | }, 24 | "guid": "chc9kj52-04g0-4b6f-941d-3a844e352758", 25 | "token": "someMerchantToken" 26 | } -------------------------------------------------------------------------------- /tests/unit/json/create_payout_group_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "instructions": [ 3 | { 4 | "amount": 10, 5 | "currency": "USD", 6 | "ledgerCurrency": "USD", 7 | "reference": "payout_20210527", 8 | "notificationURL": "https:\/\/yournotiticationURL.com\/wed3sa0wx1rz5bg0bv97851eqx", 9 | "notificationEmail": "merchant@email.com", 10 | "email": "john@doe.com", 11 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 12 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2" 13 | } 14 | ], 15 | "token": "somePayoutToken" 16 | } -------------------------------------------------------------------------------- /tests/unit/json/create_payout_group_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": [ 3 | { 4 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 5 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 6 | "accountId": "SJcWZCFq344DL8QnXpdBNM", 7 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 8 | "amount": 10, 9 | "currency": "USD", 10 | "ledgerCurrency": "GBP", 11 | "email": "john@doe.com", 12 | "reference": "payout_20210527", 13 | "label": "John Doe", 14 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 15 | "notificationEmail": "merchant@email.com", 16 | "effectiveDate": "2021-05-27T09:00:00.000Z", 17 | "requestDate": "2021-05-27T10:47:37.834Z", 18 | "status": "new", 19 | "groupId": "SxKRk4MdW8Ae3vsoR6UPQE", 20 | "transactions": [] 21 | } 22 | ], 23 | "failed": [ 24 | { 25 | "errMessage": "Ledger currency is required", 26 | "payee": "john@doe.com" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /tests/unit/json/create_refund_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid":"37bd36bd-6fcb-409c-a907-47f9244302aa", 3 | "id": "Eso8srxKJR5U71ahCspAAA", 4 | "invoice": "UZjwcYkWAKfTMn9J1yyfs4", 5 | "reference": "someReference", 6 | "status": "preview", 7 | "amount": 10, 8 | "transactionCurrency": "BTC", 9 | "transactionAmount": 7.19E-4, 10 | "transactionRefundFee": 2.0E-6, 11 | "currency": "USD", 12 | "refundFee": 0.03, 13 | "immediate": false, 14 | "buyerPaysRefundFee": false, 15 | "requestDate": "2022-11-14T12:20:47.000Z" 16 | } -------------------------------------------------------------------------------- /tests/unit/json/get_bill_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "draft", 3 | "url": "https://bitpay.com/bill?id=3Zpmji8bRKxWJo2NJbWX5H&resource=bills", 4 | "number": "bill1234-EFGH", 5 | "createdDate": "2021-05-21T09:51:04.126Z", 6 | "dueDate": "2021-05-31T00:00:00.000Z", 7 | "currency": "USD", 8 | "name": "John Doe", 9 | "address1": "2630 Hegal Place", 10 | "address2": "Apt 42", 11 | "city": "Alexandria", 12 | "state": "VA", 13 | "zip": "23242", 14 | "country": "US", 15 | "email": "john@doe.com", 16 | "cc": [ 17 | "jane@doe.com" 18 | ], 19 | "phone": "555-123-456", 20 | "passProcessingFee": true, 21 | "emailBill": true, 22 | "id": "X6KJbe9RxAGWNReCwd1xRw", 23 | "merchant": "7HyKWn3d4xdhAMQYAEVxVq", 24 | "items": [ 25 | { 26 | "id": "NV35GRWtrdB2cmGEjY4LKY", 27 | "description": "Test Item 1", 28 | "price": 6, 29 | "quantity": 1 30 | }, 31 | { 32 | "id": "Apy3i2TpzHRYP8tJCkrZMT", 33 | "description": "Test Item 2", 34 | "price": 4, 35 | "quantity": 1 36 | } 37 | ], 38 | "token": "6EBQR37MgDJPfEiLY3jtRq7eTP2aodR5V5wmXyyZhru5FM5yF4RCGKYQtnT7nhwHjA" 39 | } -------------------------------------------------------------------------------- /tests/unit/json/get_bills_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "status": "draft", 4 | "url": "https://bitpay.com/bill?id=X6KJbe9RxAGWNReCwd1xRw&resource=bills", 5 | "number": "bill1234-ABCD", 6 | "createdDate": "2021-05-21T09:48:02.373Z", 7 | "dueDate": "2021-05-31T00:00:00.000Z", 8 | "currency": "USD", 9 | "name": "John Doe", 10 | "address1": "2630 Hegal Place", 11 | "address2": "Apt 42", 12 | "city": "Alexandria", 13 | "state": "VA", 14 | "zip": "23242", 15 | "country": "US", 16 | "email": "john@doe.com", 17 | "cc": [ 18 | "jane@doe.com" 19 | ], 20 | "phone": "555-123-456", 21 | "passProcessingFee": true, 22 | "emailBill": true, 23 | "id": "X6KJbe9RxAGWNReCwd1xRw", 24 | "merchant": "7HyKWn3d4xdhAMQYAEVxVq", 25 | "items": [ 26 | { 27 | "id": "EL4vx41Nxc5RYhbqDthjE", 28 | "description": "Test Item 1", 29 | "price": 6, 30 | "quantity": 1 31 | }, 32 | { 33 | "id": "6spPADZ2h6MfADvnhfsuBt", 34 | "description": "Test Item 2", 35 | "price": 4, 36 | "quantity": 1 37 | } 38 | ], 39 | "token": "6EBQR37MgDJPfEiLY3jtRqBMYLg8XSDqhp2kp7VSDqCMHGHnsw4bqnnwQmtehzCvSo" 40 | }, 41 | { 42 | "status": "draft", 43 | "url": "https://bitpay.com/bill?id=3Zpmji8bRKxWJo2NJbWX5H&resource=bills", 44 | "number": "bill1234-EFGH", 45 | "createdDate": "2021-05-21T09:51:04.126Z", 46 | "dueDate": "2021-05-31T00:00:00.000Z", 47 | "currency": "USD", 48 | "name": "John Doe", 49 | "address1": "2630 Hegal Place", 50 | "address2": "Apt 42", 51 | "city": "Alexandria", 52 | "state": "VA", 53 | "zip": "23242", 54 | "country": "US", 55 | "email": "john@doe.com", 56 | "cc": [ 57 | "jane@doe.com" 58 | ], 59 | "phone": "555-123-456", 60 | "passProcessingFee": true, 61 | "emailBill": true, 62 | "id": "3Zpmji8bRKxWJo2NJbWX5H", 63 | "merchant": "7HyKWn3d4xdhAMQYAEVxVq", 64 | "items": [ 65 | { 66 | "id": "NV35GRWtrdB2cmGEjY4LKY", 67 | "description": "Test Item 1", 68 | "price": 6, 69 | "quantity": 1 70 | }, 71 | { 72 | "id": "Apy3i2TpzHRYP8tJCkrZMT", 73 | "description": "Test Item 2", 74 | "price": 4, 75 | "quantity": 1 76 | } 77 | ], 78 | "token": "6EBQR37MgDJPfEiLY3jtRq7eTP2aodR5V5wmXyyZhru5FM5yF4RCGKYQtnT7nhwHjA" 79 | } 80 | ] -------------------------------------------------------------------------------- /tests/unit/json/get_invoice_event_token.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://bitpay.com/events", 3 | "token": "4MuqDPt93i9Xbf8SnAPniwbGeNLW8A3ScgAmukFMgFUFRqTLuuhVdAFfePPysVqL2P", 4 | "events": [ 5 | "payment", 6 | "confirmation" 7 | ], 8 | "actions": [ 9 | "subscribe", 10 | "unsubscribe" 11 | ] 12 | } -------------------------------------------------------------------------------- /tests/unit/json/get_ledger_entries_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Invoice", 4 | "amount": 823000000, 5 | "code": 1000, 6 | "description": "20210510_fghij", 7 | "timestamp": "2021-05-10T20:08:52.919Z", 8 | "txType": "sale", 9 | "scale": 100000000, 10 | "invoiceId": "Hpqc63wvE1ZjzeeH4kEycF", 11 | "buyerFields": { 12 | "buyerName": "John Doe", 13 | "buyerAddress1": "2630 Hegal Place", 14 | "buyerAddress2": "Apt 42", 15 | "buyerCity": "Alexandria", 16 | "buyerState": "VA", 17 | "buyerZip": "23242", 18 | "buyerCountry": "US", 19 | "buyerPhone": "555-123-456", 20 | "buyerNotify": true, 21 | "buyerEmail": "john@doe.com" 22 | }, 23 | "invoiceAmount": 10, 24 | "invoiceCurrency": "USD", 25 | "transactionCurrency": "BCH", 26 | "id": "FR4rgfADCRNmAhtz1Ci4kU" 27 | }, 28 | { 29 | "type": "Invoice Fee", 30 | "amount": -8000000, 31 | "code": 1023, 32 | "description": "Invoice Fee", 33 | "timestamp": "2021-05-10T20:08:52.919Z", 34 | "txType": "Invoice Fee", 35 | "scale": 100000000, 36 | "invoiceId": "Hpqc63wvE1ZjzeeH4kEycF", 37 | "buyerFields": { 38 | "buyerName": "John Doe", 39 | "buyerAddress1": "2630 Hegal Place", 40 | "buyerAddress2": "Apt 42", 41 | "buyerCity": "Alexandria", 42 | "buyerState": "VA", 43 | "buyerZip": "23242", 44 | "buyerCountry": "US", 45 | "buyerPhone": "555-123-456", 46 | "buyerNotify": true, 47 | "buyerEmail": "john@doe.com" 48 | }, 49 | "invoiceAmount": 10, 50 | "invoiceCurrency": "USD", 51 | "transactionCurrency": "BCH", 52 | "id": "XCkhgHKP2pSme4qszMpM3B" 53 | }, 54 | { 55 | "type": "Invoice Refund", 56 | "supportRequest": "SYyrnbRCJ78V1DknHakKPo", 57 | "amount": -823000000, 58 | "code": 1020, 59 | "description": "Invoice Refund", 60 | "timestamp": "2021-05-12T13:00:45.063Z", 61 | "txType": "Invoice Refund", 62 | "scale": 100000000, 63 | "invoiceId": "Hpqc63wvE1ZjzeeH4kEycF", 64 | "buyerFields": { 65 | "buyerName": "John Doe", 66 | "buyerAddress1": "2630 Hegal Place", 67 | "buyerAddress2": "Apt 42", 68 | "buyerCity": "Alexandria", 69 | "buyerState": "VA", 70 | "buyerZip": "23242", 71 | "buyerCountry": "US", 72 | "buyerPhone": "555-123-456", 73 | "buyerNotify": true, 74 | "buyerEmail": "john@doe.com" 75 | }, 76 | "invoiceAmount": 10, 77 | "invoiceCurrency": "USD", 78 | "transactionCurrency": "BCH", 79 | "id": "PBqakmWMZ2H3RwhGq9vCsg" 80 | } 81 | ] -------------------------------------------------------------------------------- /tests/unit/json/get_ledgers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "currency": "EUR", 4 | "balance": 0 5 | }, 6 | { 7 | "currency": "USD", 8 | "balance": 2389.82 9 | }, 10 | { 11 | "currency": "BTC", 12 | "balance": 0.000287 13 | } 14 | ] -------------------------------------------------------------------------------- /tests/unit/json/get_payout_recipient_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "john.smith@email.com", 3 | "label": "Bob123", 4 | "status": "invited", 5 | "id": "JA4cEtmBxCp5cybtnh1rds", 6 | "shopperId": null, 7 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXcejgPXVmZ4Ae3oGaCGBFKQf" 8 | } -------------------------------------------------------------------------------- /tests/unit/json/get_payout_recipients_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "email": "alice@email.com", 4 | "label": "Alice", 5 | "status": "invited", 6 | "id": "JA4cEtmBxCp5cybtnh1rds", 7 | "shopperId": null, 8 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXcejgPXVmZ4Ae3oGaCGBFKQf" 9 | }, 10 | { 11 | "email": "bob@email.com", 12 | "label": "Bob", 13 | "status": "invited", 14 | "id": "X3icwc4tE8KJ5hEPNPpDXW", 15 | "shopperId": null, 16 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXrrBAB9vRY3BVxGLbAa6uEx7" 17 | } 18 | ] -------------------------------------------------------------------------------- /tests/unit/json/get_payout_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 3 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 4 | "accountId": "SJcWZCFq344DL8QnXpdBNM", 5 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 6 | "amount": 10, 7 | "currency": "USD", 8 | "ledgerCurrency": "GBP", 9 | "exchangeRates": { 10 | "BTC": { 11 | "USD": 39390.47, 12 | "GBP": 27883.962246420004 13 | } 14 | }, 15 | "email": "john@doe.com", 16 | "reference": "payout_20210527", 17 | "label": "John Doe", 18 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 19 | "notificationEmail": "merchant@email.com", 20 | "effectiveDate": "2021-05-27T09:00:00.000Z", 21 | "requestDate": "2021-05-27T10:47:37.834Z", 22 | "dateExecuted": "2021-05-27T09:00:00.000Z", 23 | "status": "complete", 24 | "transactions": [ 25 | { 26 | "txid": "db53d7e2bf3385a31257ce09396202d9c2823370a5ca186db315c45e24594057", 27 | "amount": 0.000254, 28 | "date": "2021-05-27T11:04:23.155Z" 29 | } 30 | ], 31 | "token": "6RZSTPtnzEaroAe2X4YijenRiqteRDNvzbT8NjtcHjUVd9FUFwa7dsX8RFgRDDC5SL" 32 | } -------------------------------------------------------------------------------- /tests/unit/json/get_payouts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 4 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 5 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 6 | "amount": 10, 7 | "currency": "USD", 8 | "ledgerCurrency": "GBP", 9 | "exchangeRates": { 10 | "BTC": { 11 | "USD": 39390.47, 12 | "GBP": 27883.962246420004 13 | } 14 | }, 15 | "email": "john@doe.com", 16 | "reference": "payout_20210527", 17 | "label": "John Doe", 18 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 19 | "notificationEmail": "merchant@email.com", 20 | "effectiveDate": "2021-05-27T09:00:00.000Z", 21 | "requestDate": "2021-05-27T10:47:37.834Z", 22 | "status": "complete", 23 | "transactions": [ 24 | { 25 | "txid": "db53d7e2bf3385a31257ce09396202d9c2823370a5ca186db315c45e24594057", 26 | "amount": 0.000254, 27 | "date": "2021-05-27T11:04:23.155Z" 28 | } 29 | ], 30 | "token": "9pVLfvdjt59q1JiY2JEsf2uzeeEpSqDwwfRAzuFr9CcrxZX25rTnP6HdRhsMBGLArz" 31 | }, 32 | { 33 | "id": "KMXZeQigXG6T5abzCJmTcH", 34 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 35 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 36 | "amount": 10, 37 | "currency": "USD", 38 | "ledgerCurrency": "GBP", 39 | "email": "jane@doe.com", 40 | "reference": "payout_20210528", 41 | "label": "Jane Doe", 42 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 43 | "notificationEmail": "merchant@email.com", 44 | "effectiveDate": "2021-05-28T09:00:00.000Z", 45 | "requestDate": "2021-05-28T10:23:43.765Z", 46 | "status": "cancelled", 47 | "transactions": [], 48 | "token": "9pVLfvdjt59q1JiY2JEsf2hr5FsjimfY4qRLFi85tMiXSCkJ9mQ2oSQqYKVangKaro" 49 | } 50 | ] -------------------------------------------------------------------------------- /tests/unit/json/get_rates_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "code":"BTC", 4 | "name":"Bitcoin", 5 | "rate":1 6 | }, 7 | { 8 | "code":"BCH", 9 | "name":"Bitcoin Cash", 10 | "rate":50.77 11 | }, 12 | { 13 | "code":"USD", 14 | "name":"US Dollar", 15 | "rate":41248.11 16 | }, 17 | { 18 | "code":"EUR", 19 | "name":"Eurozone Euro", 20 | "rate":33823.04 21 | }, 22 | { 23 | "code":"GBP", 24 | "name":"Pound Sterling", 25 | "rate":29011.49 26 | }, 27 | { 28 | "code":"JPY", 29 | "name":"Japanese Yen", 30 | "rate":4482741 31 | }, 32 | { 33 | "code":"CAD", 34 | "name":"Canadian Dollar", 35 | "rate":49670.85 36 | }, 37 | { 38 | "code":"AUD", 39 | "name":"Australian Dollar", 40 | "rate":53031.99 41 | }, 42 | { 43 | "code":"CNY", 44 | "name":"Chinese Yuan", 45 | "rate":265266.57 46 | } 47 | ] -------------------------------------------------------------------------------- /tests/unit/json/get_refund_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid": "37bd36bd-6fcb-409c-a907-47f9244302aa", 3 | "id": "WoE46gSLkJQS48RJEiNw3L", 4 | "invoice": "Hpqc63wvE1ZjzeeH4kEycF", 5 | "reference": "Test refund", 6 | "status": "created", 7 | "amount": 10, 8 | "transactionCurrency": "BTC", 9 | "transactionAmount": 0.000594, 10 | "transactionRefundFee": 0.000002, 11 | "currency": "USD", 12 | "lastRefundNotification": "2021-08-29T20:45:35.368Z", 13 | "refundFee": 0.04, 14 | "immediate": false, 15 | "buyerPaysRefundFee": false, 16 | "requestDate": "2021-08-29T20:45:34.000Z" 17 | } -------------------------------------------------------------------------------- /tests/unit/json/get_refunds_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "WoE46gSLkJQS48RJEiNw3L", 4 | "invoice": "Hpqc63wvE1ZjzeeH4kEycF", 5 | "reference": "Test refund", 6 | "status": "created", 7 | "amount": 10, 8 | "transactionCurrency": "BTC", 9 | "transactionAmount": 0.000594, 10 | "transactionRefundFee": 0.000002, 11 | "currency": "USD", 12 | "lastRefundNotification": "2021-08-29T20:45:35.368Z", 13 | "refundFee": 0.04, 14 | "immediate": false, 15 | "buyerPaysRefundFee": false, 16 | "requestDate": "2021-08-29T20:45:34.000Z" 17 | } 18 | ] -------------------------------------------------------------------------------- /tests/unit/json/get_settlement_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "RPWTabW8urd3xWv2To989v", 3 | "accountId": "YJCgTf3jrXHkUVzLQ7y4eg", 4 | "status": "processing", 5 | "currency": "EUR", 6 | "payoutInfo": { 7 | "label": "Corporate account", 8 | "bankCountry": "Netherlands", 9 | "name": "Test Organization", 10 | "bank": "Test", 11 | "swift": "RABONL2U", 12 | "account": "NL85ABNA0000000000" 13 | }, 14 | "dateCreated": "2021-05-11T09:05:00.176Z", 15 | "dateExecuted": "2021-05-11T11:52:29.681Z", 16 | "openingDate": "2021-05-10T09:00:00.000Z", 17 | "closingDate": "2021-05-11T09:00:00.000Z", 18 | "openingBalance": 23.27, 19 | "ledgerEntriesSum": 20.82, 20 | "withholdings": [ 21 | { 22 | "amount": 8.21, 23 | "code": "W005", 24 | "description": "Pending Refunds" 25 | } 26 | ], 27 | "withholdingsSum": 8.21, 28 | "totalAmount": 35.88, 29 | "token": "2GrR6GDeYxUFYM9sDKViy6nFFTy4Rjvm1SYdLBjK46jkeJdgUTRccRfhtwkhNcuZky" 30 | } -------------------------------------------------------------------------------- /tests/unit/json/get_settlements_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "KBkdURgmE3Lsy9VTnavZHX", 4 | "accountId": "YJCgTf3jrXHkUVzLQ7y4eg", 5 | "status": "processing", 6 | "currency": "EUR", 7 | "payoutInfo": { 8 | "label": "Corporate account", 9 | "bankCountry": "Netherlands", 10 | "name": "Test Organization", 11 | "bank": "Test", 12 | "swift": "RABONL2U", 13 | "account": "NL85ABNA0000000000" 14 | }, 15 | "dateCreated": "2021-05-10T09:05:00.176Z", 16 | "dateExecuted": "2021-05-10T11:52:29.681Z", 17 | "openingDate": "2021-05-09T09:00:00.000Z", 18 | "closingDate": "2021-05-10T09:00:00.000Z", 19 | "openingBalance": 1.27, 20 | "ledgerEntriesSum": 20.82, 21 | "withholdings": [], 22 | "withholdingsSum": 0, 23 | "totalAmount": 22.09, 24 | "token": "2gBtViSiBWSEJGo1LfaMFHoaBRzE2jek2VitKAYeenj2SRiTVSCgRvs1WTN8w4w8Lc" 25 | }, 26 | { 27 | "id": "RPWTabW8urd3xWv2To989v", 28 | "accountId": "YJCgTf3jrXHkUVzLQ7y4eg", 29 | "status": "processing", 30 | "currency": "EUR", 31 | "payoutInfo": { 32 | "label": "Corporate account", 33 | "bankCountry": "Netherlands", 34 | "name": "Test Organization", 35 | "bank": "Test", 36 | "swift": "RABONL2U", 37 | "account": "NL85ABNA0000000000" 38 | }, 39 | "dateCreated": "2021-05-11T09:05:00.176Z", 40 | "dateExecuted": "2021-05-11T11:52:29.681Z", 41 | "openingDate": "2021-05-10T09:00:00.000Z", 42 | "closingDate": "2021-05-11T09:00:00.000Z", 43 | "openingBalance": 23.27, 44 | "ledgerEntriesSum": 20.82, 45 | "withholdings": [ 46 | { 47 | "amount": 8.21, 48 | "code": "W005", 49 | "description": "Pending Refunds" 50 | } 51 | ], 52 | "withholdingsSum": 8.21, 53 | "totalAmount": 35.88, 54 | "token": "2gBtViSiBWSEJitKAYSCgRvs1WTN8w4Go1Leenj2SRiTVFHoaBRzE2jek2VfaMw8Lc" 55 | } 56 | ] -------------------------------------------------------------------------------- /tests/unit/json/submit_payout_recipients_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "recipients": [ 3 | { 4 | "email": "alice@email.com", 5 | "label": "Alice" 6 | }, 7 | { 8 | "email": "bob@email.com", 9 | "label": "Bob" 10 | } 11 | ], 12 | "guid": "chc9kj52-04g0-4b6f-941d-3a844e352758", 13 | "token": "somePayoutToken" 14 | } -------------------------------------------------------------------------------- /tests/unit/json/submit_payout_recipients_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "email": "alice@email.com", 4 | "label": "Alice", 5 | "status": "invited", 6 | "id": "JA4cEtmBxCp5cybtnh1rds", 7 | "shopperId": null, 8 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXcejgPXVmZ4Ae3oGaCGBFKQf" 9 | }, 10 | { 11 | "email": "bob@email.com", 12 | "label": "Bob", 13 | "status": "invited", 14 | "id": "X3icwc4tE8KJ5hEPNPpDXW", 15 | "shopperId": null, 16 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXrrBAB9vRY3BVxGLbAa6uEx7" 17 | } 18 | ] -------------------------------------------------------------------------------- /tests/unit/json/submit_payout_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "amount": 10.0, 3 | "currency": "USD", 4 | "email": "john@doe.com", 5 | "ledgerCurrency": "GBP", 6 | "notificationEmail": "merchant@email.com", 7 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 8 | "reference": "payout_20210527", 9 | "token": "somePayoutToken", 10 | "label": "John Doe" 11 | } -------------------------------------------------------------------------------- /tests/unit/json/submit_payout_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "JMwv8wQCXANoU2ZZQ9a9GH", 3 | "recipientId": "LDxRZCGq174SF8AnQpdBPB", 4 | "accountId": "SJcWZCFq344DL8QnXpdBNM", 5 | "shopperId": "7qohDf2zZnQK5Qanj8oyC2", 6 | "amount": 10, 7 | "currency": "USD", 8 | "ledgerCurrency": "GBP", 9 | "email": "john@doe.com", 10 | "reference": "payout_20210527", 11 | "label": "John Doe", 12 | "notificationURL": "https://yournotiticationURL.com/wed3sa0wx1rz5bg0bv97851eqx", 13 | "notificationEmail": "merchant@email.com", 14 | "effectiveDate": "2021-05-27T09:00:00.000Z", 15 | "requestDate": "2021-05-27T10:47:37.834Z", 16 | "status": "new", 17 | "transactions": [], 18 | "token": "6RZSTPtnzEaroAe2X4YijenRiqteRDNvzbT8NjtcHjUVd9FUFwa7dsX8RFgRDDC5SL" 19 | } -------------------------------------------------------------------------------- /tests/unit/json/success_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": {}, 4 | "message": null 5 | } -------------------------------------------------------------------------------- /tests/unit/json/update_bill_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "address1": "2630 Hegal Place", 3 | "address2": "Apt 42", 4 | "cc": [ 5 | "jane@doe.com" 6 | ], 7 | "city": "Alexandria", 8 | "country": "US", 9 | "currency": "USD", 10 | "dueDate": "2021-05-31T00:00:00.00Z", 11 | "email": "some@email.com", 12 | "items": [ 13 | { 14 | "description": "Test Item 1", 15 | "price": 6.0, 16 | "quantity": 1 17 | }, 18 | { 19 | "description": "Test Item 2", 20 | "price": 4.0, 21 | "quantity": 1 22 | } 23 | ], 24 | "name": "John Doe", 25 | "number": "bill1234-ABCD", 26 | "passProcessingFee": true, 27 | "phone": "555-123-456", 28 | "state": "VA", 29 | "zip": "23242", 30 | "token": "billToken" 31 | } -------------------------------------------------------------------------------- /tests/unit/json/update_payout_recipient_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid": "chc9kj52-04g0-4b6f-941d-3a844e352758", 3 | "label": "Bob123", 4 | "token": "somePayoutToken" 5 | } -------------------------------------------------------------------------------- /tests/unit/json/update_payout_recipient_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "bob@email.com", 3 | "label": "Bob123", 4 | "status": "invited", 5 | "id": "X3icwc4tE8KJ5hEPNPpDXW", 6 | "shopperId": null, 7 | "token": "2LVBntm7z92rnuVjVX5ZVaDoUEaoY4LxhZMMzPAMGyXrrBAB9vRY3BVxGLbAa6uEx7" 8 | } -------------------------------------------------------------------------------- /tests/unit/logger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/logger/__init__.py -------------------------------------------------------------------------------- /tests/unit/logger/test_logger_provider.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.logger.bitpay_logger import BitPayLogger 4 | from bitpay.logger.logger_provider import LoggerProvider 5 | 6 | @pytest.mark.unit 7 | def test_get_logger_should_returns_default_logger(): 8 | assert isinstance(LoggerProvider.get_logger(), BitPayLogger) 9 | 10 | @pytest.mark.unit 11 | def test_set_logger(): 12 | another_client = AnotherLogger() 13 | 14 | LoggerProvider.set_logger(another_client) 15 | 16 | assert isinstance(LoggerProvider.get_logger(), AnotherLogger) 17 | assert isinstance(LoggerProvider.get_logger(), AnotherLogger) 18 | 19 | 20 | class AnotherLogger(BitPayLogger): 21 | def log_request(self, method: str, endpoint: str, json: str): 22 | super().log_request(method, endpoint, json) 23 | 24 | def log_response(self, method: str, endpoint: str, json: str): 25 | super().log_response(method, endpoint, json) 26 | 27 | def log_error(self, message: str): 28 | super().log_error(message) -------------------------------------------------------------------------------- /tests/unit/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/models/__init__.py -------------------------------------------------------------------------------- /tests/unit/models/bill/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/models/bill/__init__.py -------------------------------------------------------------------------------- /tests/unit/models/bill/test_bill.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.bill.bill import Bill 4 | from bitpay.models.bill.item import Item 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | city = "Rzeszow" 10 | country = "PL" 11 | items = [Item()] 12 | bill = Bill(city=city, country=country, items=items) 13 | 14 | assert city == bill.city 15 | assert country == bill.country 16 | assert items == bill.items 17 | 18 | 19 | @pytest.mark.unit 20 | def test_to_json(): 21 | city = "Rzeszow" 22 | country = "PL" 23 | items = [Item()] 24 | bill = Bill(city=city, country=country, items=items) 25 | to_json = bill.to_json() 26 | 27 | assert city == to_json.get("city") 28 | assert country == to_json.get("country") 29 | assert len(items) == len(to_json.get("items")) 30 | -------------------------------------------------------------------------------- /tests/unit/models/bill/test_item.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.bill.item import Item 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | id = "123" 9 | price = 45.3 10 | quantity = 23 11 | item = Item(id=id, price=price, quantity=quantity) 12 | 13 | assert id == item.id 14 | assert price == item.price 15 | assert quantity == item.quantity 16 | 17 | 18 | @pytest.mark.unit 19 | def test_to_json(): 20 | id = "123" 21 | price = 45.3 22 | quantity = 23 23 | item = Item(id=id, price=price, quantity=quantity).to_json() 24 | 25 | assert id == item.get("id") 26 | assert price == item.get("price") 27 | assert quantity == item.get("quantity") 28 | -------------------------------------------------------------------------------- /tests/unit/models/bitpay_model_test.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import pytest 4 | 5 | from bitpay.exceptions.bitpay_exception import BitPayException 6 | from bitpay.models.invoice.buyer import Buyer 7 | from bitpay.models.invoice.invoice import Invoice 8 | from bitpay.models.invoice.supported_transaction_currencies import ( 9 | SupportedTransactionCurrencies, 10 | ) 11 | from bitpay.models.invoice.supported_transaction_currency import ( 12 | SupportedTransactionCurrency, 13 | ) 14 | 15 | 16 | @pytest.mark.unit 17 | def test_from_to_json(): 18 | bch = SupportedTransactionCurrency(reason="some reason") 19 | supported_transaction_currencies = SupportedTransactionCurrencies(bch=bch) 20 | buyer = Buyer(name="Bily Matthews") 21 | invoice = Invoice(price=2.16, currency="eur", buyer=buyer, 22 | supported_transaction_currencies=supported_transaction_currencies) 23 | invoice.order_id = "98e572ea-910e-415d-b6de-65f5090680f6" 24 | invoice.redirect_url = "https://url.com" 25 | 26 | result = Invoice.to_json(invoice) 27 | with open( 28 | os.path.abspath(os.path.dirname(__file__)) + "/invoice_model.json", "r" 29 | ) as file: 30 | expected_result = json.load(file) 31 | 32 | assert result == expected_result 33 | 34 | 35 | @pytest.mark.unit 36 | def test_currency_validation(): 37 | valid_invoice = Invoice(currency="USD") 38 | 39 | with pytest.raises(BitPayException) as exception: 40 | invalid_invoice = Invoice(currency="INVALID") 41 | 42 | assert ( 43 | exception.value.args[0] 44 | == ""'BITPAY-GENERIC: Unexpected Bitpay exception.:currency code must be a type of Model.Currency'"" 45 | ) 46 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/models/invoice/__init__.py -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_buyer.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.buyer import Buyer 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | address1 = "someAddress" 9 | country = "PL" 10 | buyer_email = "someEmail" 11 | buyer = Buyer(address1=address1, country=country, email=buyer_email) 12 | 13 | assert address1 == buyer.address1 14 | assert country == buyer.country 15 | assert buyer_email == buyer.email 16 | 17 | 18 | @pytest.mark.unit 19 | def test_to_json(): 20 | address1 = "someAddress" 21 | country = "PL" 22 | buyer_email = "someEmail" 23 | json = Buyer(address1=address1, country=country, email=buyer_email).to_json() 24 | 25 | assert address1 == json.get("address1") 26 | assert country == json.get("country") 27 | assert buyer_email == json.get("email") 28 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_buyer_provided_info.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.buyer_provided_info import BuyerProvidedInfo 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | name = "someName" 9 | phone_number = "1234" 10 | buyer_provided_info = BuyerProvidedInfo(name=name, phoneNumber=phone_number) 11 | 12 | assert name == buyer_provided_info.name 13 | assert phone_number == buyer_provided_info.phone_number 14 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_invoice.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.buyer import Buyer 4 | from bitpay.models.invoice.invoice import Invoice 5 | from bitpay.models.invoice.itemized_details import ItemizedDetails 6 | from bitpay.models.invoice.shopper import Shopper 7 | 8 | 9 | @pytest.mark.unit 10 | def test_constructor(): 11 | price = 10.25 12 | currency = "BCH" 13 | shopper = Shopper() 14 | guid = "someGuid" 15 | buyer_name = "someName" 16 | itemized_details = [ItemizedDetails(), ItemizedDetails()] 17 | 18 | invoice = Invoice( 19 | price=price, 20 | currency=currency, 21 | shopper=shopper, 22 | guid=guid, 23 | buyer=Buyer(name=buyer_name), 24 | itemizedDetails=itemized_details 25 | ) 26 | 27 | assert price == invoice.price 28 | assert currency == invoice.currency 29 | assert shopper == invoice.shopper 30 | assert guid == invoice.guid 31 | assert buyer_name == invoice.buyer.name 32 | assert itemized_details == invoice.itemized_details 33 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_invoice_event_token.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from bitpay.models.invoice.invoice_event_token import InvoiceEventToken 3 | 4 | 5 | @pytest.mark.unit 6 | def test_constructor(): 7 | actions = ["someActions"] 8 | events = ["someEvents"] 9 | token = "someToken" 10 | invoice_event_token = InvoiceEventToken(actions=actions, events=events, token=token) 11 | 12 | assert actions == invoice_event_token.actions 13 | assert events == invoice_event_token.events 14 | assert token == invoice_event_token.token 15 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_itemized_details.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.itemized_details import ItemizedDetails 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | amount = 12.45 9 | description = "someDescription" 10 | is_fee = True 11 | itemized_details = ItemizedDetails(amount=amount, description=description, is_fee=is_fee) 12 | 13 | assert amount == itemized_details.amount 14 | assert description == itemized_details.description 15 | assert is_fee == itemized_details.is_fee 16 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_miner_fees.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.miner_fees import MinerFees 4 | from bitpay.models.invoice.miner_fees_item import MinerFeesItem 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | busd = MinerFeesItem() 10 | busd.total_fee = 1234 11 | gusd = MinerFeesItem() 12 | doge_as_dict = {"totalFee": 3456} 13 | miner_fees = MinerFees(busd=busd, doge=doge_as_dict, gusd=gusd) 14 | 15 | assert busd == miner_fees.busd 16 | assert gusd == miner_fees.gusd 17 | assert 3456 == miner_fees.doge.total_fee 18 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_miner_fees_item.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.miner_fees_item import MinerFeesItem 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | amount = 12.34 9 | satoshis = 12.345 10 | total_fee = 4354 11 | miner_fees_item = MinerFeesItem(fiat_amount=amount, satoshis_per_byte=satoshis, total_fee=total_fee) 12 | 13 | assert amount == miner_fees_item.fiat_amount 14 | assert 12.345 == miner_fees_item.satoshis_per_byte 15 | assert total_fee == miner_fees_item.total_fee 16 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_refund.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.refund import Refund 4 | 5 | 6 | @pytest.mark.unit 7 | def test_construct(): 8 | invoice_id = "123" 9 | amount = 12.34 10 | currency = "BCH" 11 | refund_fee = 12.10 12 | 13 | refund = Refund(invoice=invoice_id, amount=amount, currency=currency, refund_fee=refund_fee) 14 | 15 | assert invoice_id == refund.invoice 16 | assert amount == refund.amount 17 | assert currency == refund.currency 18 | assert refund_fee == refund.refund_fee 19 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_refund_info.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.refund_info import RefundInfo 4 | 5 | 6 | @pytest.mark.unit 7 | def test_construct(): 8 | support_request = "someValue" 9 | currency = "USD" 10 | 11 | refund = RefundInfo(support_request=support_request, currency=currency) 12 | 13 | assert support_request == refund.support_request 14 | assert currency == refund.currency 15 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_shopper.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.shopper import Shopper 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | user = "someUser" 9 | shopper = Shopper(user=user) 10 | 11 | assert user == shopper.user 12 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_supported_transaction_currencies.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.supported_transaction_currencies import ( 4 | SupportedTransactionCurrencies, 5 | ) 6 | from bitpay.models.invoice.supported_transaction_currency import ( 7 | SupportedTransactionCurrency, 8 | ) 9 | 10 | 11 | @pytest.mark.unit 12 | def test_constructor(): 13 | btc = SupportedTransactionCurrency(enabled=True) 14 | pax = SupportedTransactionCurrency(enabled=False) 15 | supported_transaction_currencies = SupportedTransactionCurrencies(btc=btc, pax=pax) 16 | 17 | assert True is supported_transaction_currencies.btc.enabled 18 | assert False is supported_transaction_currencies.pax.enabled 19 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_supported_transaction_currency.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.supported_transaction_currency import ( 4 | SupportedTransactionCurrency, 5 | ) 6 | 7 | 8 | @pytest.mark.unit 9 | def test_constructor(): 10 | enabled = True 11 | reason = "someReason" 12 | 13 | supported_transaction_currency = SupportedTransactionCurrency(enabled=enabled, reason=reason) 14 | 15 | assert enabled == supported_transaction_currency.enabled 16 | assert reason == supported_transaction_currency.reason 17 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_transaction.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.transaction import Transaction 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | amount = 12.45 9 | confirmations = 4 10 | transaction = Transaction(amount=amount, confirmations=confirmations) 11 | 12 | assert transaction.amount == amount 13 | assert transaction.confirmations == confirmations 14 | -------------------------------------------------------------------------------- /tests/unit/models/invoice/test_universal_codes.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.invoice.universal_codes import UniversalCodes 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | payment_string = "someString" 9 | verification_link = "https://url.com" 10 | 11 | universal_codes = UniversalCodes(payment_string=payment_string, verificationLink=verification_link) 12 | 13 | assert payment_string == universal_codes.payment_string 14 | assert verification_link == universal_codes.verification_link 15 | -------------------------------------------------------------------------------- /tests/unit/models/invoice_model.json: -------------------------------------------------------------------------------- 1 | {"price": 2.16, "currency": "eur", "orderId": "98e572ea-910e-415d-b6de-65f5090680f6", "redirectURL": "https://url.com", "buyer": {"name": "Bily Matthews"}, "supportedTransactionCurrencies": {"BCH": {"reason": "some reason"}}} -------------------------------------------------------------------------------- /tests/unit/models/ledger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/models/ledger/__init__.py -------------------------------------------------------------------------------- /tests/unit/models/ledger/test_buyer.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.ledger.buyer import Buyer 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | address1 = "someAddress" 9 | state = "someState" 10 | buyer = Buyer(address1=address1, state=state) 11 | 12 | assert address1 == buyer.address1 13 | assert state == buyer.state 14 | -------------------------------------------------------------------------------- /tests/unit/models/ledger/test_ledger.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.ledger.ledger import Ledger 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | currency = "USD" 9 | balance = 12.34 10 | ledger = Ledger(currency=currency, balance=balance) 11 | 12 | assert currency == ledger.currency 13 | assert balance == ledger.balance 14 | -------------------------------------------------------------------------------- /tests/unit/models/ledger/test_ledger_entry.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.ledger.buyer import Buyer 4 | from bitpay.models.ledger.ledger_entry import LedgerEntry 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | type = "invoice" 10 | currency = "USD" 11 | buyer_city = "Rzeszow" 12 | buyer_fields = Buyer(city=buyer_city) 13 | ledger_entry = LedgerEntry( 14 | type=type, currency=currency, buyerFields=buyer_fields 15 | ) 16 | 17 | assert type == ledger_entry.type 18 | assert currency == ledger_entry.currency 19 | assert buyer_city == ledger_entry.buyer_fields.city 20 | -------------------------------------------------------------------------------- /tests/unit/models/payout/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/models/payout/__init__.py -------------------------------------------------------------------------------- /tests/unit/models/payout/test_payout.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.payout.payout import Payout 4 | from bitpay.models.payout.payout_transaction import PayoutTransaction 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | amount = 12.34 10 | currency = "USD" 11 | ledger_currency = "BCH" 12 | account = "1234" 13 | notification_url = "https://url.com" 14 | transactions = [PayoutTransaction()] 15 | payout = Payout( 16 | amount=amount, 17 | currency=currency, 18 | ledger_currency=ledger_currency, 19 | notification_url=notification_url, 20 | account=account, 21 | transactions=transactions 22 | ) 23 | 24 | assert amount == payout.amount 25 | assert currency == payout.currency 26 | assert ledger_currency == payout.ledger_currency 27 | assert transactions == payout.transactions 28 | -------------------------------------------------------------------------------- /tests/unit/models/payout/test_payout_recipient.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.payout.payout_recipient import PayoutRecipient 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | email = "some@email.com" 9 | guid = "someGuid" 10 | payout_recipient = PayoutRecipient(email=email, guid=guid) 11 | 12 | assert email == payout_recipient.email 13 | assert guid == payout_recipient.guid 14 | -------------------------------------------------------------------------------- /tests/unit/models/payout/test_payout_recipients.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.payout.payout_recipient import PayoutRecipient 4 | from bitpay.models.payout.payout_recipients import PayoutRecipients 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | guid = "1234" 10 | recipients = [PayoutRecipient(), PayoutRecipient()] 11 | payout_recipients = PayoutRecipients(guid=guid, recipients=recipients) 12 | 13 | assert guid == payout_recipients.guid 14 | assert recipients == payout_recipients.recipients 15 | -------------------------------------------------------------------------------- /tests/unit/models/payout/test_payout_transaction.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.payout.payout_transaction import PayoutTransaction 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | txid = "someValue" 9 | amount = 1234 10 | payout_transaction = PayoutTransaction(txid=txid, amount=amount) 11 | 12 | assert txid == payout_transaction.txid 13 | assert amount == payout_transaction.amount 14 | -------------------------------------------------------------------------------- /tests/unit/models/rate/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/models/rate/__init__.py -------------------------------------------------------------------------------- /tests/unit/models/rate/test_rate.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.rate.rate import Rate 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | name = "someName" 9 | code = "1234" 10 | rate = 12.45 11 | tested_class = Rate(name=name, code=code, rate=rate) 12 | 13 | assert name == tested_class.name 14 | assert code == tested_class.code 15 | assert rate == tested_class.rate 16 | -------------------------------------------------------------------------------- /tests/unit/models/rate/test_rates.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import pytest 4 | 5 | from bitpay.clients.rate_client import RateClient 6 | from bitpay.models.rate.rate import Rate 7 | from bitpay.models.rate.rates import Rates 8 | 9 | 10 | @pytest.mark.unit 11 | def test_constructor(): 12 | rate = 12.23 13 | rates_list: List[Rate] = [ 14 | Rate(code="BCH", rate=10.23), 15 | Rate(code="USD", rate=rate), 16 | ] 17 | rates = Rates(rates=rates_list) 18 | 19 | assert rates.get_rate("USD") == rate 20 | 21 | 22 | @pytest.mark.unit 23 | def test_update(mocker): 24 | expected_rate = 10.23 25 | rate_client = mocker.Mock(spec=RateClient) 26 | rate_client.get_rates.return_value = Rates(rates= 27 | [Rate(code="BCH", rate=expected_rate)] 28 | ) 29 | 30 | rates = Rates(rates=[Rate(code="USD", rate=123.45)]) 31 | 32 | assert rates.get_rate("BCH") is None 33 | rates.update(rate_client=rate_client) 34 | assert rates.get_rate("BCH") == expected_rate 35 | -------------------------------------------------------------------------------- /tests/unit/models/settlement/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/models/settlement/__init__.py -------------------------------------------------------------------------------- /tests/unit/models/settlement/test_invoice_data.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.settlement.invoice_data import InvoiceData 4 | from bitpay.models.settlement.refund_info import RefundInfo 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | price = 12.23 10 | transaction_currency = "BCH" 11 | refund_info = RefundInfo() 12 | 13 | invoice_data = InvoiceData( 14 | price=price, 15 | transaction_currency=transaction_currency, 16 | refund_info=refund_info 17 | ) 18 | 19 | assert price == invoice_data.price 20 | assert transaction_currency == invoice_data.transaction_currency 21 | assert refund_info == invoice_data.refund_info 22 | -------------------------------------------------------------------------------- /tests/unit/models/settlement/test_payout_info.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.settlement.payout_info import PayoutInfo 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | routing = "someRouting" 9 | bank_name = "someBankName" 10 | account_holder_name = "someAccountHolderName" 11 | payout_info = PayoutInfo( 12 | routing=routing, 13 | bankName=bank_name, 14 | account_holder_name=account_holder_name 15 | ) 16 | 17 | assert payout_info.routing == routing 18 | assert payout_info.bank_name == bank_name 19 | assert payout_info.account_holder_name == account_holder_name 20 | -------------------------------------------------------------------------------- /tests/unit/models/settlement/test_refund_info.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.settlement.refund_info import RefundInfo 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | support_request = "someValue" 9 | currency = "USD" 10 | amounts = {"USD": 12.34} 11 | refund_info = RefundInfo( 12 | support_request=support_request, currency=currency, amounts=amounts 13 | ) 14 | 15 | assert support_request == refund_info.support_request 16 | assert currency == refund_info.currency 17 | assert amounts == refund_info.amounts 18 | -------------------------------------------------------------------------------- /tests/unit/models/settlement/test_settlement.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.settlement.payout_info import PayoutInfo 4 | from bitpay.models.settlement.settlement import Settlement 5 | from bitpay.models.settlement.with_holdings import WithHoldings 6 | 7 | 8 | @pytest.mark.unit 9 | def test_constructor(): 10 | account_id = "1234" 11 | currency = "USD" 12 | payout_info = PayoutInfo() 13 | withholdings = [WithHoldings()] 14 | 15 | settlement = Settlement( 16 | account_id=account_id, 17 | currency=currency, 18 | payoutInfo=payout_info, 19 | withholdings=withholdings, 20 | 21 | ) 22 | 23 | assert settlement.account_id == account_id 24 | assert settlement.currency == currency 25 | assert settlement.payout_info == payout_info 26 | assert settlement.withholdings == withholdings 27 | 28 | withholdings_amount = 12.34 29 | settlement = Settlement(withholdings=[WithHoldings(amount=withholdings_amount)]) 30 | 31 | assert len(settlement.withholdings) == 1 32 | assert settlement.withholdings[0].amount == withholdings_amount 33 | -------------------------------------------------------------------------------- /tests/unit/models/settlement/test_settlement_ledger_entry.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.settlement.invoice_data import InvoiceData 4 | from bitpay.models.settlement.settlement_ledger_entry import SettlementLedgerEntry 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | invoice_id = "1234" 10 | amount = 12.34 11 | description = "someDescription" 12 | invoice_data = InvoiceData() 13 | settlement_ledger_entry = SettlementLedgerEntry( 14 | invoice_id=invoice_id, 15 | amount=amount, 16 | description=description, 17 | invoice_data=invoice_data 18 | ) 19 | 20 | assert invoice_id == settlement_ledger_entry.invoice_id 21 | assert amount == settlement_ledger_entry.amount 22 | assert description == settlement_ledger_entry.description 23 | assert invoice_data == settlement_ledger_entry.invoice_data 24 | 25 | settlement_ledger_entry = SettlementLedgerEntry(invoice_data=InvoiceData(price=12.34)) 26 | assert 12.34 == settlement_ledger_entry.invoice_data.price 27 | -------------------------------------------------------------------------------- /tests/unit/models/settlement/test_with_holdings.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.settlement.with_holdings import WithHoldings 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | amount = 12.34 9 | notes = "someNotes" 10 | label = "someLabel" 11 | withholdings = WithHoldings(amount=amount, notes=notes, label=label) 12 | 13 | assert amount == withholdings.amount 14 | assert notes == withholdings.notes 15 | assert label == withholdings.label 16 | -------------------------------------------------------------------------------- /tests/unit/models/wallet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpay/python-bitpay-client/2ea98860b9d4da45fd5f131409c9d866bf45448c/tests/unit/models/wallet/__init__.py -------------------------------------------------------------------------------- /tests/unit/models/wallet/test_currencies.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.wallet.currencies import Currencies 4 | from bitpay.models.wallet.currency_qr import CurrencyQr 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | code = "USD" 10 | pay_pro = True 11 | qr = CurrencyQr() 12 | wallet_connect = True 13 | currencies = Currencies(code=code, pay_pro=pay_pro, qr=qr, walletConnect=wallet_connect 14 | ) 15 | 16 | assert code == currencies.code 17 | assert pay_pro == currencies.pay_pro 18 | assert qr == currencies.qr 19 | assert wallet_connect == currencies.wallet_connect 20 | 21 | currency_type = "BIP72b" 22 | currencies = Currencies(qr=CurrencyQr(type=currency_type)) 23 | 24 | assert currencies.qr.type == currency_type 25 | -------------------------------------------------------------------------------- /tests/unit/models/wallet/test_currency_qr.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.wallet.currency_qr import CurrencyQr 4 | 5 | 6 | @pytest.mark.unit 7 | def test_constructor(): 8 | type = "someType" 9 | collapsed = True 10 | qr = CurrencyQr(type=type, collapsed=collapsed) 11 | 12 | assert type == qr.type 13 | assert collapsed == qr.collapsed 14 | -------------------------------------------------------------------------------- /tests/unit/models/wallet/test_wallet.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from bitpay.models.wallet.currencies import Currencies 4 | from bitpay.models.wallet.wallet import Wallet 5 | 6 | 7 | @pytest.mark.unit 8 | def test_constructor(): 9 | currencies = [Currencies()] 10 | pay_pro = True 11 | key = "someKey" 12 | 13 | wallet = Wallet(currencies=currencies, pay_pro=pay_pro, key=key) 14 | assert currencies == wallet.currencies 15 | assert pay_pro == wallet.pay_pro 16 | assert key == wallet.key 17 | 18 | wallet = Wallet(currencies=[Currencies(code="USD"), Currencies(code="BCH")]) 19 | assert len(wallet.currencies) == 2 20 | assert wallet.currencies[1].code == "BCH" 21 | --------------------------------------------------------------------------------